commit
335c69074a
@ -0,0 +1,33 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
onLaunch: function () {
|
||||||
|
console.log('App Launch')
|
||||||
|
this.setToken()
|
||||||
|
},
|
||||||
|
onShow: function () {
|
||||||
|
console.log('App Show')
|
||||||
|
},
|
||||||
|
onHide: function () {
|
||||||
|
console.log('App Hide')
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
setToken() {
|
||||||
|
if (this.$cache.get('token')) {
|
||||||
|
uni.switchTab({
|
||||||
|
url: `/pages/home/home`,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/index/index',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
/*每个页面公共css */
|
||||||
|
@import '@/uni_modules/uview-ui/index.scss';
|
||||||
|
</style>
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
export default {
|
||||||
|
// server: 'https://daike2.tt.demo.cq1080.com/api/v1', // 开发接口地址
|
||||||
|
server: 'https://gupiao.365rise.top/ssdmn-gupiao-api', // 生产接口地址
|
||||||
|
|
||||||
|
appKey: 'bc097852a77d4ee9ce3435e0948d0e5e', // 接口 appkey
|
||||||
|
prefix: 'yyq_', // 缓存信息 key 前缀
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<script>
|
||||||
|
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||||
|
CSS.supports('top: constant(a)'))
|
||||||
|
document.write(
|
||||||
|
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||||
|
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||||
|
</script>
|
||||||
|
<title></title>
|
||||||
|
<!--preload-links-->
|
||||||
|
<!--app-context-->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"><!--app-html--></div>
|
||||||
|
<script type="module" src="/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import App from './App'
|
||||||
|
|
||||||
|
// #ifndef VUE3
|
||||||
|
import Vue from 'vue'
|
||||||
|
import uView from '@/uni_modules/uview-ui'
|
||||||
|
import store from './store'
|
||||||
|
import api from './plugins/api'
|
||||||
|
import cache from './plugins/cache'
|
||||||
|
import permission from './plugins/permission'
|
||||||
|
import * as util from './plugins/util'
|
||||||
|
import config from './config'
|
||||||
|
import * as echarts from '@/uni_modules/lime-echart/static/echarts.min'
|
||||||
|
Vue.use(uView)
|
||||||
|
|
||||||
|
Vue.prototype.$api = api
|
||||||
|
Vue.prototype.$cache = cache
|
||||||
|
Vue.prototype.$store = store
|
||||||
|
Vue.prototype.$permission = permission
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
App.mpType = 'app'
|
||||||
|
const app = new Vue({
|
||||||
|
...App,
|
||||||
|
})
|
||||||
|
app.$mount()
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef VUE3
|
||||||
|
import { createSSRApp } from 'vue'
|
||||||
|
export function createApp() {
|
||||||
|
const app = createSSRApp(App)
|
||||||
|
return {
|
||||||
|
app,
|
||||||
|
store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
app.$mount()
|
||||||
|
// #endif
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"name" : "gupiao_weixin_uniapp",
|
||||||
|
"appid" : "__UNI__FFD798D",
|
||||||
|
"description" : "",
|
||||||
|
"versionName" : "1.0.0",
|
||||||
|
"versionCode" : "100",
|
||||||
|
"transformPx" : false,
|
||||||
|
/* 5+App特有相关 */
|
||||||
|
"app-plus" : {
|
||||||
|
"usingComponents" : true,
|
||||||
|
"nvueStyleCompiler" : "uni-app",
|
||||||
|
"compilerVersion" : 3,
|
||||||
|
"splashscreen" : {
|
||||||
|
"alwaysShowBeforeRender" : true,
|
||||||
|
"waiting" : true,
|
||||||
|
"autoclose" : true,
|
||||||
|
"delay" : 0
|
||||||
|
},
|
||||||
|
/* 模块配置 */
|
||||||
|
"modules" : {},
|
||||||
|
/* 应用发布信息 */
|
||||||
|
"distribute" : {
|
||||||
|
/* android打包配置 */
|
||||||
|
"android" : {
|
||||||
|
"permissions" : [
|
||||||
|
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||||
|
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||||
|
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
/* ios打包配置 */
|
||||||
|
"ios" : {},
|
||||||
|
/* SDK配置 */
|
||||||
|
"sdkConfigs" : {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* 快应用特有相关 */
|
||||||
|
"quickapp" : {},
|
||||||
|
/* 小程序特有相关 */
|
||||||
|
"mp-weixin" : {
|
||||||
|
"appid" : "wx1ff32548702fbfdc",
|
||||||
|
"setting" : {
|
||||||
|
"urlCheck" : false,
|
||||||
|
"minified" : true
|
||||||
|
},
|
||||||
|
"usingComponents" : true,
|
||||||
|
"lazyCodeLoading" : "requiredComponents",
|
||||||
|
"optimization" : {
|
||||||
|
"subPackages" : true
|
||||||
|
},
|
||||||
|
"packOptions" : "",
|
||||||
|
"include" : [
|
||||||
|
{
|
||||||
|
"type" : "file",
|
||||||
|
"value" : "uni_modules/lime-echart/static/echarts.min.js"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type" : "file",
|
||||||
|
"value" : "uni_modules/lime-echart/static/ecStat.min"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type" : "file",
|
||||||
|
"value" : "uni_modules/lime-echart/static/uni.webview.1.5.3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mp-alipay" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"mp-baidu" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"mp-toutiao" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"uniStatistics" : {
|
||||||
|
"enable" : false
|
||||||
|
},
|
||||||
|
"vueVersion" : "2"
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
"$basedir/node" "$basedir/../xss/bin/xss" "$@"
|
||||||
|
ret=$?
|
||||||
|
else
|
||||||
|
node "$basedir/../xss/bin/xss" "$@"
|
||||||
|
ret=$?
|
||||||
|
fi
|
||||||
|
exit $ret
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
@IF EXIST "%~dp0\node.exe" (
|
||||||
|
"%~dp0\node.exe" "%~dp0\..\xss\bin\xss" %*
|
||||||
|
) ELSE (
|
||||||
|
@SETLOCAL
|
||||||
|
@SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||||
|
node "%~dp0\..\xss\bin\xss" %*
|
||||||
|
)
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"systemParams": "win32-x64-93",
|
||||||
|
"modulesFolders": [
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
|
"flags": [],
|
||||||
|
"linkedModules": [],
|
||||||
|
"topLevelPatterns": [
|
||||||
|
"md5@^2.3.0",
|
||||||
|
"qs@^5.2.1",
|
||||||
|
"xss@^1.0.14"
|
||||||
|
],
|
||||||
|
"lockfileEntries": {
|
||||||
|
"charenc@0.0.2": "https://registry.npmmirror.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667",
|
||||||
|
"commander@^2.20.3": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33",
|
||||||
|
"crypt@0.0.2": "https://registry.npmmirror.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b",
|
||||||
|
"cssfilter@0.0.10": "https://registry.npmmirror.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae",
|
||||||
|
"is-buffer@~1.1.6": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be",
|
||||||
|
"md5@^2.3.0": "https://registry.npmmirror.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f",
|
||||||
|
"qs@^5.2.1": "https://registry.npmmirror.com/qs/-/qs-5.2.1.tgz#801fee030e0b9450d6385adc48a4cc55b44aedfc",
|
||||||
|
"xss@^1.0.14": "https://registry.npmmirror.com/xss/-/xss-1.0.14.tgz#4f3efbde75ad0d82e9921cc3c95e6590dd336694"
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"artifacts": {}
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
Copyright © 2011, Paul Vorbach. All rights reserved.
|
||||||
|
Copyright © 2009, Jeff Mott. All rights reserved.
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
* Neither the name Crypto-JS nor the names of its contributors may be used to
|
||||||
|
endorse or promote products derived from this software without specific prior
|
||||||
|
written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@ -0,0 +1 @@
|
|||||||
|
**enc** provides crypto character encoding utilities.
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
var charenc = {
|
||||||
|
// UTF-8 encoding
|
||||||
|
utf8: {
|
||||||
|
// Convert a string to a byte array
|
||||||
|
stringToBytes: function(str) {
|
||||||
|
return charenc.bin.stringToBytes(unescape(encodeURIComponent(str)));
|
||||||
|
},
|
||||||
|
|
||||||
|
// Convert a byte array to a string
|
||||||
|
bytesToString: function(bytes) {
|
||||||
|
return decodeURIComponent(escape(charenc.bin.bytesToString(bytes)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Binary encoding
|
||||||
|
bin: {
|
||||||
|
// Convert a string to a byte array
|
||||||
|
stringToBytes: function(str) {
|
||||||
|
for (var bytes = [], i = 0; i < str.length; i++)
|
||||||
|
bytes.push(str.charCodeAt(i) & 0xFF);
|
||||||
|
return bytes;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Convert a byte array to a string
|
||||||
|
bytesToString: function(bytes) {
|
||||||
|
for (var str = [], i = 0; i < bytes.length; i++)
|
||||||
|
str.push(String.fromCharCode(bytes[i]));
|
||||||
|
return str.join('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = charenc;
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"author": "Paul Vorbach <paul@vorb.de> (http://vorb.de)",
|
||||||
|
"name": "charenc",
|
||||||
|
"description": "character encoding utilities",
|
||||||
|
"tags": [
|
||||||
|
"utf8",
|
||||||
|
"binary",
|
||||||
|
"byte",
|
||||||
|
"string"
|
||||||
|
],
|
||||||
|
"version": "0.0.2",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/pvorb/node-charenc.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/pvorb/node-charenc/issues"
|
||||||
|
},
|
||||||
|
"main": "charenc.js",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
(The MIT License)
|
||||||
|
|
||||||
|
Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
|
||||||
|
|
||||||
|
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.
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "commander",
|
||||||
|
"version": "2.20.3",
|
||||||
|
"description": "the complete solution for node.js command-line programs",
|
||||||
|
"keywords": [
|
||||||
|
"commander",
|
||||||
|
"command",
|
||||||
|
"option",
|
||||||
|
"parser"
|
||||||
|
],
|
||||||
|
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tj/commander.js.git"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint index.js",
|
||||||
|
"test": "node test/run.js && npm run test-typings",
|
||||||
|
"test-typings": "tsc -p tsconfig.json"
|
||||||
|
},
|
||||||
|
"main": "index",
|
||||||
|
"files": [
|
||||||
|
"index.js",
|
||||||
|
"typings/index.d.ts"
|
||||||
|
],
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^12.7.8",
|
||||||
|
"eslint": "^6.4.0",
|
||||||
|
"should": "^13.2.3",
|
||||||
|
"sinon": "^7.5.0",
|
||||||
|
"standard": "^14.3.1",
|
||||||
|
"ts-node": "^8.4.1",
|
||||||
|
"typescript": "^3.6.3"
|
||||||
|
},
|
||||||
|
"typings": "typings/index.d.ts"
|
||||||
|
}
|
||||||
@ -0,0 +1,310 @@
|
|||||||
|
// Type definitions for commander 2.11
|
||||||
|
// Project: https://github.com/visionmedia/commander.js
|
||||||
|
// Definitions by: Alan Agius <https://github.com/alan-agius4>, Marcelo Dezem <https://github.com/mdezem>, vvakame <https://github.com/vvakame>, Jules Randolph <https://github.com/sveinburne>
|
||||||
|
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||||
|
|
||||||
|
declare namespace local {
|
||||||
|
|
||||||
|
class Option {
|
||||||
|
flags: string;
|
||||||
|
required: boolean;
|
||||||
|
optional: boolean;
|
||||||
|
bool: boolean;
|
||||||
|
short?: string;
|
||||||
|
long: string;
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a new `Option` with the given `flags` and `description`.
|
||||||
|
*
|
||||||
|
* @param {string} flags
|
||||||
|
* @param {string} [description]
|
||||||
|
*/
|
||||||
|
constructor(flags: string, description?: string);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Command extends NodeJS.EventEmitter {
|
||||||
|
[key: string]: any;
|
||||||
|
|
||||||
|
args: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a new `Command`.
|
||||||
|
*
|
||||||
|
* @param {string} [name]
|
||||||
|
*/
|
||||||
|
constructor(name?: string);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the program version to `str`.
|
||||||
|
*
|
||||||
|
* This method auto-registers the "-V, --version" flag
|
||||||
|
* which will print the version number when passed.
|
||||||
|
*
|
||||||
|
* @param {string} str
|
||||||
|
* @param {string} [flags]
|
||||||
|
* @returns {Command} for chaining
|
||||||
|
*/
|
||||||
|
version(str: string, flags?: string): Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add command `name`.
|
||||||
|
*
|
||||||
|
* The `.action()` callback is invoked when the
|
||||||
|
* command `name` is specified via __ARGV__,
|
||||||
|
* and the remaining arguments are applied to the
|
||||||
|
* function for access.
|
||||||
|
*
|
||||||
|
* When the `name` is "*" an un-matched command
|
||||||
|
* will be passed as the first arg, followed by
|
||||||
|
* the rest of __ARGV__ remaining.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* program
|
||||||
|
* .version('0.0.1')
|
||||||
|
* .option('-C, --chdir <path>', 'change the working directory')
|
||||||
|
* .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
|
||||||
|
* .option('-T, --no-tests', 'ignore test hook')
|
||||||
|
*
|
||||||
|
* program
|
||||||
|
* .command('setup')
|
||||||
|
* .description('run remote setup commands')
|
||||||
|
* .action(function() {
|
||||||
|
* console.log('setup');
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* program
|
||||||
|
* .command('exec <cmd>')
|
||||||
|
* .description('run the given remote command')
|
||||||
|
* .action(function(cmd) {
|
||||||
|
* console.log('exec "%s"', cmd);
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* program
|
||||||
|
* .command('teardown <dir> [otherDirs...]')
|
||||||
|
* .description('run teardown commands')
|
||||||
|
* .action(function(dir, otherDirs) {
|
||||||
|
* console.log('dir "%s"', dir);
|
||||||
|
* if (otherDirs) {
|
||||||
|
* otherDirs.forEach(function (oDir) {
|
||||||
|
* console.log('dir "%s"', oDir);
|
||||||
|
* });
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* program
|
||||||
|
* .command('*')
|
||||||
|
* .description('deploy the given env')
|
||||||
|
* .action(function(env) {
|
||||||
|
* console.log('deploying "%s"', env);
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* program.parse(process.argv);
|
||||||
|
*
|
||||||
|
* @param {string} name
|
||||||
|
* @param {string} [desc] for git-style sub-commands
|
||||||
|
* @param {CommandOptions} [opts] command options
|
||||||
|
* @returns {Command} the new command
|
||||||
|
*/
|
||||||
|
command(name: string, desc?: string, opts?: commander.CommandOptions): Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define argument syntax for the top-level command.
|
||||||
|
*
|
||||||
|
* @param {string} desc
|
||||||
|
* @returns {Command} for chaining
|
||||||
|
*/
|
||||||
|
arguments(desc: string): Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse expected `args`.
|
||||||
|
*
|
||||||
|
* For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.
|
||||||
|
*
|
||||||
|
* @param {string[]} args
|
||||||
|
* @returns {Command} for chaining
|
||||||
|
*/
|
||||||
|
parseExpectedArgs(args: string[]): Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register callback `fn` for the command.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* program
|
||||||
|
* .command('help')
|
||||||
|
* .description('display verbose help')
|
||||||
|
* .action(function() {
|
||||||
|
* // output help here
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @param {(...args: any[]) => void} fn
|
||||||
|
* @returns {Command} for chaining
|
||||||
|
*/
|
||||||
|
action(fn: (...args: any[]) => void): Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define option with `flags`, `description` and optional
|
||||||
|
* coercion `fn`.
|
||||||
|
*
|
||||||
|
* The `flags` string should contain both the short and long flags,
|
||||||
|
* separated by comma, a pipe or space. The following are all valid
|
||||||
|
* all will output this way when `--help` is used.
|
||||||
|
*
|
||||||
|
* "-p, --pepper"
|
||||||
|
* "-p|--pepper"
|
||||||
|
* "-p --pepper"
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // simple boolean defaulting to false
|
||||||
|
* program.option('-p, --pepper', 'add pepper');
|
||||||
|
*
|
||||||
|
* --pepper
|
||||||
|
* program.pepper
|
||||||
|
* // => Boolean
|
||||||
|
*
|
||||||
|
* // simple boolean defaulting to true
|
||||||
|
* program.option('-C, --no-cheese', 'remove cheese');
|
||||||
|
*
|
||||||
|
* program.cheese
|
||||||
|
* // => true
|
||||||
|
*
|
||||||
|
* --no-cheese
|
||||||
|
* program.cheese
|
||||||
|
* // => false
|
||||||
|
*
|
||||||
|
* // required argument
|
||||||
|
* program.option('-C, --chdir <path>', 'change the working directory');
|
||||||
|
*
|
||||||
|
* --chdir /tmp
|
||||||
|
* program.chdir
|
||||||
|
* // => "/tmp"
|
||||||
|
*
|
||||||
|
* // optional argument
|
||||||
|
* program.option('-c, --cheese [type]', 'add cheese [marble]');
|
||||||
|
*
|
||||||
|
* @param {string} flags
|
||||||
|
* @param {string} [description]
|
||||||
|
* @param {((arg1: any, arg2: any) => void) | RegExp} [fn] function or default
|
||||||
|
* @param {*} [defaultValue]
|
||||||
|
* @returns {Command} for chaining
|
||||||
|
*/
|
||||||
|
option(flags: string, description?: string, fn?: ((arg1: any, arg2: any) => void) | RegExp, defaultValue?: any): Command;
|
||||||
|
option(flags: string, description?: string, defaultValue?: any): Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow unknown options on the command line.
|
||||||
|
*
|
||||||
|
* @param {boolean} [arg] if `true` or omitted, no error will be thrown for unknown options.
|
||||||
|
* @returns {Command} for chaining
|
||||||
|
*/
|
||||||
|
allowUnknownOption(arg?: boolean): Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse `argv`, settings options and invoking commands when defined.
|
||||||
|
*
|
||||||
|
* @param {string[]} argv
|
||||||
|
* @returns {Command} for chaining
|
||||||
|
*/
|
||||||
|
parse(argv: string[]): Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse options from `argv` returning `argv` void of these options.
|
||||||
|
*
|
||||||
|
* @param {string[]} argv
|
||||||
|
* @returns {ParseOptionsResult}
|
||||||
|
*/
|
||||||
|
parseOptions(argv: string[]): commander.ParseOptionsResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an object containing options as key-value pairs
|
||||||
|
*
|
||||||
|
* @returns {{[key: string]: any}}
|
||||||
|
*/
|
||||||
|
opts(): { [key: string]: any };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the description to `str`.
|
||||||
|
*
|
||||||
|
* @param {string} str
|
||||||
|
* @param {{[argName: string]: string}} argsDescription
|
||||||
|
* @return {(Command | string)}
|
||||||
|
*/
|
||||||
|
description(str: string, argsDescription?: {[argName: string]: string}): Command;
|
||||||
|
description(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an alias for the command.
|
||||||
|
*
|
||||||
|
* @param {string} alias
|
||||||
|
* @return {(Command | string)}
|
||||||
|
*/
|
||||||
|
alias(alias: string): Command;
|
||||||
|
alias(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set or get the command usage.
|
||||||
|
*
|
||||||
|
* @param {string} str
|
||||||
|
* @return {(Command | string)}
|
||||||
|
*/
|
||||||
|
usage(str: string): Command;
|
||||||
|
usage(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the command.
|
||||||
|
*
|
||||||
|
* @param {string} str
|
||||||
|
* @return {Command}
|
||||||
|
*/
|
||||||
|
name(str: string): Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the command.
|
||||||
|
*
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
name(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output help information for this command.
|
||||||
|
*
|
||||||
|
* @param {(str: string) => string} [cb]
|
||||||
|
*/
|
||||||
|
outputHelp(cb?: (str: string) => string): void;
|
||||||
|
|
||||||
|
/** Output help information and exit.
|
||||||
|
*
|
||||||
|
* @param {(str: string) => string} [cb]
|
||||||
|
*/
|
||||||
|
help(cb?: (str: string) => string): never;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
declare namespace commander {
|
||||||
|
|
||||||
|
type Command = local.Command
|
||||||
|
|
||||||
|
type Option = local.Option
|
||||||
|
|
||||||
|
interface CommandOptions {
|
||||||
|
noHelp?: boolean;
|
||||||
|
isDefault?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParseOptionsResult {
|
||||||
|
args: string[];
|
||||||
|
unknown: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CommanderStatic extends Command {
|
||||||
|
Command: typeof local.Command;
|
||||||
|
Option: typeof local.Option;
|
||||||
|
CommandOptions: CommandOptions;
|
||||||
|
ParseOptionsResult: ParseOptionsResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const commander: commander.CommanderStatic;
|
||||||
|
export = commander;
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
Copyright © 2011, Paul Vorbach. All rights reserved.
|
||||||
|
Copyright © 2009, Jeff Mott. All rights reserved.
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
* Neither the name Crypto-JS nor the names of its contributors may be used to
|
||||||
|
endorse or promote products derived from this software without specific prior
|
||||||
|
written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@ -0,0 +1 @@
|
|||||||
|
**crypt** provides utilities for encryption and hashing
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
(function() {
|
||||||
|
var base64map
|
||||||
|
= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
|
||||||
|
|
||||||
|
crypt = {
|
||||||
|
// Bit-wise rotation left
|
||||||
|
rotl: function(n, b) {
|
||||||
|
return (n << b) | (n >>> (32 - b));
|
||||||
|
},
|
||||||
|
|
||||||
|
// Bit-wise rotation right
|
||||||
|
rotr: function(n, b) {
|
||||||
|
return (n << (32 - b)) | (n >>> b);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Swap big-endian to little-endian and vice versa
|
||||||
|
endian: function(n) {
|
||||||
|
// If number given, swap endian
|
||||||
|
if (n.constructor == Number) {
|
||||||
|
return crypt.rotl(n, 8) & 0x00FF00FF | crypt.rotl(n, 24) & 0xFF00FF00;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else, assume array and swap all items
|
||||||
|
for (var i = 0; i < n.length; i++)
|
||||||
|
n[i] = crypt.endian(n[i]);
|
||||||
|
return n;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Generate an array of any length of random bytes
|
||||||
|
randomBytes: function(n) {
|
||||||
|
for (var bytes = []; n > 0; n--)
|
||||||
|
bytes.push(Math.floor(Math.random() * 256));
|
||||||
|
return bytes;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Convert a byte array to big-endian 32-bit words
|
||||||
|
bytesToWords: function(bytes) {
|
||||||
|
for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
|
||||||
|
words[b >>> 5] |= bytes[i] << (24 - b % 32);
|
||||||
|
return words;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Convert big-endian 32-bit words to a byte array
|
||||||
|
wordsToBytes: function(words) {
|
||||||
|
for (var bytes = [], b = 0; b < words.length * 32; b += 8)
|
||||||
|
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
|
||||||
|
return bytes;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Convert a byte array to a hex string
|
||||||
|
bytesToHex: function(bytes) {
|
||||||
|
for (var hex = [], i = 0; i < bytes.length; i++) {
|
||||||
|
hex.push((bytes[i] >>> 4).toString(16));
|
||||||
|
hex.push((bytes[i] & 0xF).toString(16));
|
||||||
|
}
|
||||||
|
return hex.join('');
|
||||||
|
},
|
||||||
|
|
||||||
|
// Convert a hex string to a byte array
|
||||||
|
hexToBytes: function(hex) {
|
||||||
|
for (var bytes = [], c = 0; c < hex.length; c += 2)
|
||||||
|
bytes.push(parseInt(hex.substr(c, 2), 16));
|
||||||
|
return bytes;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Convert a byte array to a base-64 string
|
||||||
|
bytesToBase64: function(bytes) {
|
||||||
|
for (var base64 = [], i = 0; i < bytes.length; i += 3) {
|
||||||
|
var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
|
||||||
|
for (var j = 0; j < 4; j++)
|
||||||
|
if (i * 8 + j * 6 <= bytes.length * 8)
|
||||||
|
base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F));
|
||||||
|
else
|
||||||
|
base64.push('=');
|
||||||
|
}
|
||||||
|
return base64.join('');
|
||||||
|
},
|
||||||
|
|
||||||
|
// Convert a base-64 string to a byte array
|
||||||
|
base64ToBytes: function(base64) {
|
||||||
|
// Remove non-base-64 characters
|
||||||
|
base64 = base64.replace(/[^A-Z0-9+\/]/ig, '');
|
||||||
|
|
||||||
|
for (var bytes = [], i = 0, imod4 = 0; i < base64.length;
|
||||||
|
imod4 = ++i % 4) {
|
||||||
|
if (imod4 == 0) continue;
|
||||||
|
bytes.push(((base64map.indexOf(base64.charAt(i - 1))
|
||||||
|
& (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2))
|
||||||
|
| (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)));
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = crypt;
|
||||||
|
})();
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"author": "Paul Vorbach <paul@vorb.de> (http://vorb.de)",
|
||||||
|
"name": "crypt",
|
||||||
|
"description": "utilities for encryption and hashing",
|
||||||
|
"tags": [
|
||||||
|
"hash",
|
||||||
|
"security"
|
||||||
|
],
|
||||||
|
"version": "0.0.2",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/pvorb/node-crypt.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/pvorb/node-crypt/issues"
|
||||||
|
},
|
||||||
|
"main": "crypt.js",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 老雷
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* cssfilter
|
||||||
|
*
|
||||||
|
* @author 老雷<leizongmin@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
var DEFAULT = require('./default');
|
||||||
|
var parseStyle = require('./parser');
|
||||||
|
var _ = require('./util');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回值是否为空
|
||||||
|
*
|
||||||
|
* @param {Object} obj
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
function isNull (obj) {
|
||||||
|
return (obj === undefined || obj === null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 浅拷贝对象
|
||||||
|
*
|
||||||
|
* @param {Object} obj
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
function shallowCopyObject (obj) {
|
||||||
|
var ret = {};
|
||||||
|
for (var i in obj) {
|
||||||
|
ret[i] = obj[i];
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建CSS过滤器
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* - {Object} whiteList
|
||||||
|
* - {Function} onAttr
|
||||||
|
* - {Function} onIgnoreAttr
|
||||||
|
* - {Function} safeAttrValue
|
||||||
|
*/
|
||||||
|
function FilterCSS (options) {
|
||||||
|
options = shallowCopyObject(options || {});
|
||||||
|
options.whiteList = options.whiteList || DEFAULT.whiteList;
|
||||||
|
options.onAttr = options.onAttr || DEFAULT.onAttr;
|
||||||
|
options.onIgnoreAttr = options.onIgnoreAttr || DEFAULT.onIgnoreAttr;
|
||||||
|
options.safeAttrValue = options.safeAttrValue || DEFAULT.safeAttrValue;
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
FilterCSS.prototype.process = function (css) {
|
||||||
|
// 兼容各种奇葩输入
|
||||||
|
css = css || '';
|
||||||
|
css = css.toString();
|
||||||
|
if (!css) return '';
|
||||||
|
|
||||||
|
var me = this;
|
||||||
|
var options = me.options;
|
||||||
|
var whiteList = options.whiteList;
|
||||||
|
var onAttr = options.onAttr;
|
||||||
|
var onIgnoreAttr = options.onIgnoreAttr;
|
||||||
|
var safeAttrValue = options.safeAttrValue;
|
||||||
|
|
||||||
|
var retCSS = parseStyle(css, function (sourcePosition, position, name, value, source) {
|
||||||
|
|
||||||
|
var check = whiteList[name];
|
||||||
|
var isWhite = false;
|
||||||
|
if (check === true) isWhite = check;
|
||||||
|
else if (typeof check === 'function') isWhite = check(value);
|
||||||
|
else if (check instanceof RegExp) isWhite = check.test(value);
|
||||||
|
if (isWhite !== true) isWhite = false;
|
||||||
|
|
||||||
|
// 如果过滤后 value 为空则直接忽略
|
||||||
|
value = safeAttrValue(name, value);
|
||||||
|
if (!value) return;
|
||||||
|
|
||||||
|
var opts = {
|
||||||
|
position: position,
|
||||||
|
sourcePosition: sourcePosition,
|
||||||
|
source: source,
|
||||||
|
isWhite: isWhite
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isWhite) {
|
||||||
|
|
||||||
|
var ret = onAttr(name, value, opts);
|
||||||
|
if (isNull(ret)) {
|
||||||
|
return name + ':' + value;
|
||||||
|
} else {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
var ret = onIgnoreAttr(name, value, opts);
|
||||||
|
if (!isNull(ret)) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return retCSS;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = FilterCSS;
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
module.exports = {
|
||||||
|
indexOf: function (arr, item) {
|
||||||
|
var i, j;
|
||||||
|
if (Array.prototype.indexOf) {
|
||||||
|
return arr.indexOf(item);
|
||||||
|
}
|
||||||
|
for (i = 0, j = arr.length; i < j; i++) {
|
||||||
|
if (arr[i] === item) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
forEach: function (arr, fn, scope) {
|
||||||
|
var i, j;
|
||||||
|
if (Array.prototype.forEach) {
|
||||||
|
return arr.forEach(fn, scope);
|
||||||
|
}
|
||||||
|
for (i = 0, j = arr.length; i < j; i++) {
|
||||||
|
fn.call(scope, arr[i], i, arr);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trim: function (str) {
|
||||||
|
if (String.prototype.trim) {
|
||||||
|
return str.trim();
|
||||||
|
}
|
||||||
|
return str.replace(/(^\s*)|(\s*$)/g, '');
|
||||||
|
},
|
||||||
|
trimRight: function (str) {
|
||||||
|
if (String.prototype.trimRight) {
|
||||||
|
return str.trimRight();
|
||||||
|
}
|
||||||
|
return str.replace(/(\s*$)/g, '');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "cssfilter",
|
||||||
|
"version": "0.0.10",
|
||||||
|
"description": "Sanitize untrusted CSS with a configuration specified by a Whitelist. 根据白名单过滤CSS",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"test": "istanbul cover _mocha --report lcovonly -- -t 5000 -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage",
|
||||||
|
"build": "./build",
|
||||||
|
"prepublish": "npm run test && npm run build"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/leizongmin/js-css-filter.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"sanitization",
|
||||||
|
"xss",
|
||||||
|
"sanitize",
|
||||||
|
"sanitisation",
|
||||||
|
"input",
|
||||||
|
"security",
|
||||||
|
"escape",
|
||||||
|
"encode",
|
||||||
|
"filter",
|
||||||
|
"validator",
|
||||||
|
"html",
|
||||||
|
"css",
|
||||||
|
"injection",
|
||||||
|
"whitelist"
|
||||||
|
],
|
||||||
|
"author": "Zongmin Lei <leizongmin@gmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/leizongmin/js-css-filter/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/leizongmin/js-css-filter",
|
||||||
|
"devDependencies": {
|
||||||
|
"blanket": "^1.1.6",
|
||||||
|
"browserify": "^13.1.1",
|
||||||
|
"coveralls": "^2.11.14",
|
||||||
|
"istanbul": "^0.4.5",
|
||||||
|
"mocha": "^3.1.2",
|
||||||
|
"should": "^11.1.1",
|
||||||
|
"uglify-js": "^2.7.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) Feross Aboukhadijeh
|
||||||
|
|
||||||
|
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.
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
# is-buffer [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url]
|
||||||
|
|
||||||
|
[travis-image]: https://img.shields.io/travis/feross/is-buffer/master.svg
|
||||||
|
[travis-url]: https://travis-ci.org/feross/is-buffer
|
||||||
|
[npm-image]: https://img.shields.io/npm/v/is-buffer.svg
|
||||||
|
[npm-url]: https://npmjs.org/package/is-buffer
|
||||||
|
[downloads-image]: https://img.shields.io/npm/dm/is-buffer.svg
|
||||||
|
[downloads-url]: https://npmjs.org/package/is-buffer
|
||||||
|
[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg
|
||||||
|
[standard-url]: https://standardjs.com
|
||||||
|
|
||||||
|
#### Determine if an object is a [`Buffer`](http://nodejs.org/api/buffer.html) (including the [browserify Buffer](https://github.com/feross/buffer))
|
||||||
|
|
||||||
|
[![saucelabs][saucelabs-image]][saucelabs-url]
|
||||||
|
|
||||||
|
[saucelabs-image]: https://saucelabs.com/browser-matrix/is-buffer.svg
|
||||||
|
[saucelabs-url]: https://saucelabs.com/u/is-buffer
|
||||||
|
|
||||||
|
## Why not use `Buffer.isBuffer`?
|
||||||
|
|
||||||
|
This module lets you check if an object is a `Buffer` without using `Buffer.isBuffer` (which includes the whole [buffer](https://github.com/feross/buffer) module in [browserify](http://browserify.org/)).
|
||||||
|
|
||||||
|
It's future-proof and works in node too!
|
||||||
|
|
||||||
|
## install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install is-buffer
|
||||||
|
```
|
||||||
|
|
||||||
|
## usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
var isBuffer = require('is-buffer')
|
||||||
|
|
||||||
|
isBuffer(new Buffer(4)) // true
|
||||||
|
|
||||||
|
isBuffer(undefined) // false
|
||||||
|
isBuffer(null) // false
|
||||||
|
isBuffer('') // false
|
||||||
|
isBuffer(true) // false
|
||||||
|
isBuffer(false) // false
|
||||||
|
isBuffer(0) // false
|
||||||
|
isBuffer(1) // false
|
||||||
|
isBuffer(1.0) // false
|
||||||
|
isBuffer('string') // false
|
||||||
|
isBuffer({}) // false
|
||||||
|
isBuffer(function foo () {}) // false
|
||||||
|
```
|
||||||
|
|
||||||
|
## license
|
||||||
|
|
||||||
|
MIT. Copyright (C) [Feross Aboukhadijeh](http://feross.org).
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/*!
|
||||||
|
* Determine if an object is a Buffer
|
||||||
|
*
|
||||||
|
* @author Feross Aboukhadijeh <https://feross.org>
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The _isBuffer check is for Safari 5-7 support, because it's missing
|
||||||
|
// Object.prototype.constructor. Remove this eventually
|
||||||
|
module.exports = function (obj) {
|
||||||
|
return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBuffer (obj) {
|
||||||
|
return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Node v0.10 support. Remove this eventually.
|
||||||
|
function isSlowBuffer (obj) {
|
||||||
|
return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0))
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"name": "is-buffer",
|
||||||
|
"description": "Determine if an object is a Buffer",
|
||||||
|
"version": "1.1.6",
|
||||||
|
"author": {
|
||||||
|
"name": "Feross Aboukhadijeh",
|
||||||
|
"email": "feross@feross.org",
|
||||||
|
"url": "http://feross.org/"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/feross/is-buffer/issues"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"standard": "*",
|
||||||
|
"tape": "^4.0.0",
|
||||||
|
"zuul": "^3.0.0"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"buffer",
|
||||||
|
"buffers",
|
||||||
|
"type",
|
||||||
|
"core buffer",
|
||||||
|
"browser buffer",
|
||||||
|
"browserify",
|
||||||
|
"typed array",
|
||||||
|
"uint32array",
|
||||||
|
"int16array",
|
||||||
|
"int32array",
|
||||||
|
"float32array",
|
||||||
|
"float64array",
|
||||||
|
"browser",
|
||||||
|
"arraybuffer",
|
||||||
|
"dataview"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "index.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/feross/is-buffer.git"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "standard && npm run test-node && npm run test-browser",
|
||||||
|
"test-browser": "zuul -- test/*.js",
|
||||||
|
"test-browser-local": "zuul --local -- test/*.js",
|
||||||
|
"test-node": "tape test/*.js"
|
||||||
|
},
|
||||||
|
"testling": {
|
||||||
|
"files": "test/*.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
var isBuffer = require('../')
|
||||||
|
var test = require('tape')
|
||||||
|
|
||||||
|
test('is-buffer', function (t) {
|
||||||
|
t.equal(isBuffer(Buffer.alloc(4)), true, 'new Buffer(4)')
|
||||||
|
t.equal(isBuffer(Buffer.allocUnsafeSlow(100)), true, 'SlowBuffer(100)')
|
||||||
|
|
||||||
|
t.equal(isBuffer(undefined), false, 'undefined')
|
||||||
|
t.equal(isBuffer(null), false, 'null')
|
||||||
|
t.equal(isBuffer(''), false, 'empty string')
|
||||||
|
t.equal(isBuffer(true), false, 'true')
|
||||||
|
t.equal(isBuffer(false), false, 'false')
|
||||||
|
t.equal(isBuffer(0), false, '0')
|
||||||
|
t.equal(isBuffer(1), false, '1')
|
||||||
|
t.equal(isBuffer(1.0), false, '1.0')
|
||||||
|
t.equal(isBuffer('string'), false, 'string')
|
||||||
|
t.equal(isBuffer({}), false, '{}')
|
||||||
|
t.equal(isBuffer([]), false, '[]')
|
||||||
|
t.equal(isBuffer(function foo () {}), false, 'function foo () {}')
|
||||||
|
t.equal(isBuffer({ isBuffer: null }), false, '{ isBuffer: null }')
|
||||||
|
t.equal(isBuffer({ isBuffer: function () { throw new Error() } }), false, '{ isBuffer: function () { throw new Error() } }')
|
||||||
|
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- 0.12
|
||||||
|
- 4
|
||||||
|
- 5
|
||||||
|
- 6
|
||||||
|
- 7
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
Copyright © 2011-2012, Paul Vorbach.
|
||||||
|
Copyright © 2009, Jeff Mott.
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
* Neither the name Crypto-JS nor the names of its contributors may be used to
|
||||||
|
endorse or promote products derived from this software without specific prior
|
||||||
|
written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
<input type="file" id="input">
|
||||||
|
<output id="output"></output>
|
||||||
|
<style>
|
||||||
|
output::before {
|
||||||
|
content: "output:";
|
||||||
|
}
|
||||||
|
output {
|
||||||
|
display: block;
|
||||||
|
padding: 1em;
|
||||||
|
margin: 1em;
|
||||||
|
outline: 1px solid gray;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="../dist/md5.min.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
function readAsArrayBuffer(file){
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.readAsArrayBuffer(file)
|
||||||
|
reader.onload = function(e) {
|
||||||
|
resolve(e.target.result)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
input.onchange = function(e) {
|
||||||
|
var file = input.files[0];
|
||||||
|
readAsArrayBuffer(file)
|
||||||
|
.then(buffer => {
|
||||||
|
console.log(buffer);
|
||||||
|
var now = performance.now();
|
||||||
|
var hash = MD5(buffer);
|
||||||
|
var after = performance.now() - now;
|
||||||
|
output.innerHTML = `
|
||||||
|
file: ${file.name}
|
||||||
|
size: ${file.size} bytes
|
||||||
|
type: ${file.type}
|
||||||
|
md5: ${hash}
|
||||||
|
duration: ${after.toFixed(2)} ms
|
||||||
|
`;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,160 @@
|
|||||||
|
(function(){
|
||||||
|
var crypt = require('crypt'),
|
||||||
|
utf8 = require('charenc').utf8,
|
||||||
|
isBuffer = require('is-buffer'),
|
||||||
|
bin = require('charenc').bin,
|
||||||
|
|
||||||
|
// The core
|
||||||
|
md5 = function (message, options) {
|
||||||
|
// Convert to byte array
|
||||||
|
if (message.constructor == String)
|
||||||
|
if (options && options.encoding === 'binary')
|
||||||
|
message = bin.stringToBytes(message);
|
||||||
|
else
|
||||||
|
message = utf8.stringToBytes(message);
|
||||||
|
else if (isBuffer(message))
|
||||||
|
message = Array.prototype.slice.call(message, 0);
|
||||||
|
else if (!Array.isArray(message) && message.constructor !== Uint8Array)
|
||||||
|
message = message.toString();
|
||||||
|
// else, assume byte array already
|
||||||
|
|
||||||
|
var m = crypt.bytesToWords(message),
|
||||||
|
l = message.length * 8,
|
||||||
|
a = 1732584193,
|
||||||
|
b = -271733879,
|
||||||
|
c = -1732584194,
|
||||||
|
d = 271733878;
|
||||||
|
|
||||||
|
// Swap endian
|
||||||
|
for (var i = 0; i < m.length; i++) {
|
||||||
|
m[i] = ((m[i] << 8) | (m[i] >>> 24)) & 0x00FF00FF |
|
||||||
|
((m[i] << 24) | (m[i] >>> 8)) & 0xFF00FF00;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Padding
|
||||||
|
m[l >>> 5] |= 0x80 << (l % 32);
|
||||||
|
m[(((l + 64) >>> 9) << 4) + 14] = l;
|
||||||
|
|
||||||
|
// Method shortcuts
|
||||||
|
var FF = md5._ff,
|
||||||
|
GG = md5._gg,
|
||||||
|
HH = md5._hh,
|
||||||
|
II = md5._ii;
|
||||||
|
|
||||||
|
for (var i = 0; i < m.length; i += 16) {
|
||||||
|
|
||||||
|
var aa = a,
|
||||||
|
bb = b,
|
||||||
|
cc = c,
|
||||||
|
dd = d;
|
||||||
|
|
||||||
|
a = FF(a, b, c, d, m[i+ 0], 7, -680876936);
|
||||||
|
d = FF(d, a, b, c, m[i+ 1], 12, -389564586);
|
||||||
|
c = FF(c, d, a, b, m[i+ 2], 17, 606105819);
|
||||||
|
b = FF(b, c, d, a, m[i+ 3], 22, -1044525330);
|
||||||
|
a = FF(a, b, c, d, m[i+ 4], 7, -176418897);
|
||||||
|
d = FF(d, a, b, c, m[i+ 5], 12, 1200080426);
|
||||||
|
c = FF(c, d, a, b, m[i+ 6], 17, -1473231341);
|
||||||
|
b = FF(b, c, d, a, m[i+ 7], 22, -45705983);
|
||||||
|
a = FF(a, b, c, d, m[i+ 8], 7, 1770035416);
|
||||||
|
d = FF(d, a, b, c, m[i+ 9], 12, -1958414417);
|
||||||
|
c = FF(c, d, a, b, m[i+10], 17, -42063);
|
||||||
|
b = FF(b, c, d, a, m[i+11], 22, -1990404162);
|
||||||
|
a = FF(a, b, c, d, m[i+12], 7, 1804603682);
|
||||||
|
d = FF(d, a, b, c, m[i+13], 12, -40341101);
|
||||||
|
c = FF(c, d, a, b, m[i+14], 17, -1502002290);
|
||||||
|
b = FF(b, c, d, a, m[i+15], 22, 1236535329);
|
||||||
|
|
||||||
|
a = GG(a, b, c, d, m[i+ 1], 5, -165796510);
|
||||||
|
d = GG(d, a, b, c, m[i+ 6], 9, -1069501632);
|
||||||
|
c = GG(c, d, a, b, m[i+11], 14, 643717713);
|
||||||
|
b = GG(b, c, d, a, m[i+ 0], 20, -373897302);
|
||||||
|
a = GG(a, b, c, d, m[i+ 5], 5, -701558691);
|
||||||
|
d = GG(d, a, b, c, m[i+10], 9, 38016083);
|
||||||
|
c = GG(c, d, a, b, m[i+15], 14, -660478335);
|
||||||
|
b = GG(b, c, d, a, m[i+ 4], 20, -405537848);
|
||||||
|
a = GG(a, b, c, d, m[i+ 9], 5, 568446438);
|
||||||
|
d = GG(d, a, b, c, m[i+14], 9, -1019803690);
|
||||||
|
c = GG(c, d, a, b, m[i+ 3], 14, -187363961);
|
||||||
|
b = GG(b, c, d, a, m[i+ 8], 20, 1163531501);
|
||||||
|
a = GG(a, b, c, d, m[i+13], 5, -1444681467);
|
||||||
|
d = GG(d, a, b, c, m[i+ 2], 9, -51403784);
|
||||||
|
c = GG(c, d, a, b, m[i+ 7], 14, 1735328473);
|
||||||
|
b = GG(b, c, d, a, m[i+12], 20, -1926607734);
|
||||||
|
|
||||||
|
a = HH(a, b, c, d, m[i+ 5], 4, -378558);
|
||||||
|
d = HH(d, a, b, c, m[i+ 8], 11, -2022574463);
|
||||||
|
c = HH(c, d, a, b, m[i+11], 16, 1839030562);
|
||||||
|
b = HH(b, c, d, a, m[i+14], 23, -35309556);
|
||||||
|
a = HH(a, b, c, d, m[i+ 1], 4, -1530992060);
|
||||||
|
d = HH(d, a, b, c, m[i+ 4], 11, 1272893353);
|
||||||
|
c = HH(c, d, a, b, m[i+ 7], 16, -155497632);
|
||||||
|
b = HH(b, c, d, a, m[i+10], 23, -1094730640);
|
||||||
|
a = HH(a, b, c, d, m[i+13], 4, 681279174);
|
||||||
|
d = HH(d, a, b, c, m[i+ 0], 11, -358537222);
|
||||||
|
c = HH(c, d, a, b, m[i+ 3], 16, -722521979);
|
||||||
|
b = HH(b, c, d, a, m[i+ 6], 23, 76029189);
|
||||||
|
a = HH(a, b, c, d, m[i+ 9], 4, -640364487);
|
||||||
|
d = HH(d, a, b, c, m[i+12], 11, -421815835);
|
||||||
|
c = HH(c, d, a, b, m[i+15], 16, 530742520);
|
||||||
|
b = HH(b, c, d, a, m[i+ 2], 23, -995338651);
|
||||||
|
|
||||||
|
a = II(a, b, c, d, m[i+ 0], 6, -198630844);
|
||||||
|
d = II(d, a, b, c, m[i+ 7], 10, 1126891415);
|
||||||
|
c = II(c, d, a, b, m[i+14], 15, -1416354905);
|
||||||
|
b = II(b, c, d, a, m[i+ 5], 21, -57434055);
|
||||||
|
a = II(a, b, c, d, m[i+12], 6, 1700485571);
|
||||||
|
d = II(d, a, b, c, m[i+ 3], 10, -1894986606);
|
||||||
|
c = II(c, d, a, b, m[i+10], 15, -1051523);
|
||||||
|
b = II(b, c, d, a, m[i+ 1], 21, -2054922799);
|
||||||
|
a = II(a, b, c, d, m[i+ 8], 6, 1873313359);
|
||||||
|
d = II(d, a, b, c, m[i+15], 10, -30611744);
|
||||||
|
c = II(c, d, a, b, m[i+ 6], 15, -1560198380);
|
||||||
|
b = II(b, c, d, a, m[i+13], 21, 1309151649);
|
||||||
|
a = II(a, b, c, d, m[i+ 4], 6, -145523070);
|
||||||
|
d = II(d, a, b, c, m[i+11], 10, -1120210379);
|
||||||
|
c = II(c, d, a, b, m[i+ 2], 15, 718787259);
|
||||||
|
b = II(b, c, d, a, m[i+ 9], 21, -343485551);
|
||||||
|
|
||||||
|
a = (a + aa) >>> 0;
|
||||||
|
b = (b + bb) >>> 0;
|
||||||
|
c = (c + cc) >>> 0;
|
||||||
|
d = (d + dd) >>> 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypt.endian([a, b, c, d]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Auxiliary functions
|
||||||
|
md5._ff = function (a, b, c, d, x, s, t) {
|
||||||
|
var n = a + (b & c | ~b & d) + (x >>> 0) + t;
|
||||||
|
return ((n << s) | (n >>> (32 - s))) + b;
|
||||||
|
};
|
||||||
|
md5._gg = function (a, b, c, d, x, s, t) {
|
||||||
|
var n = a + (b & d | c & ~d) + (x >>> 0) + t;
|
||||||
|
return ((n << s) | (n >>> (32 - s))) + b;
|
||||||
|
};
|
||||||
|
md5._hh = function (a, b, c, d, x, s, t) {
|
||||||
|
var n = a + (b ^ c ^ d) + (x >>> 0) + t;
|
||||||
|
return ((n << s) | (n >>> (32 - s))) + b;
|
||||||
|
};
|
||||||
|
md5._ii = function (a, b, c, d, x, s, t) {
|
||||||
|
var n = a + (c ^ (b | ~d)) + (x >>> 0) + t;
|
||||||
|
return ((n << s) | (n >>> (32 - s))) + b;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Package private blocksize
|
||||||
|
md5._blocksize = 16;
|
||||||
|
md5._digestsize = 16;
|
||||||
|
|
||||||
|
module.exports = function (message, options) {
|
||||||
|
if (message === undefined || message === null)
|
||||||
|
throw new Error('Illegal argument ' + message);
|
||||||
|
|
||||||
|
var digestbytes = crypt.wordsToBytes(md5(message, options));
|
||||||
|
return options && options.asBytes ? digestbytes :
|
||||||
|
options && options.asString ? bin.bytesToString(digestbytes) :
|
||||||
|
crypt.bytesToHex(digestbytes);
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "md5",
|
||||||
|
"description": "js function for hashing messages with MD5",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"author": "Paul Vorbach <paul@vorba.ch> (http://paul.vorba.ch)",
|
||||||
|
"contributors": [
|
||||||
|
"salba"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"md5",
|
||||||
|
"hash",
|
||||||
|
"encryption",
|
||||||
|
"message digest"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/pvorb/node-md5.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/pvorb/node-md5/issues"
|
||||||
|
},
|
||||||
|
"main": "md5.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "mocha",
|
||||||
|
"webpack": "webpack -p"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"charenc": "0.0.2",
|
||||||
|
"crypt": "0.0.2",
|
||||||
|
"is-buffer": "~1.1.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"mocha": "~2.3.4",
|
||||||
|
"webpack": "~2.4.1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {},
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
var md5 = require('./md5.js');
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
|
describe('md5', function () {
|
||||||
|
|
||||||
|
it('should throw an error for an undefined value', function() {
|
||||||
|
assert.throws(function() {
|
||||||
|
md5(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow the hashing of the string `undefined`', function() {
|
||||||
|
assert.equal('5e543256c480ac577d30f76f9120eb74', md5('undefined'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error for `null`', function() {
|
||||||
|
assert.throws(function() {
|
||||||
|
md5(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the expected MD5 hash for "message"', function() {
|
||||||
|
assert.equal('78e731027d8fd50ed642340b7c9a63b3', md5('message'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not return the same hash for random numbers twice', function() {
|
||||||
|
var msg1 = Math.floor((Math.random() * 100000) + 1) + (new Date).getTime();
|
||||||
|
var msg2 = Math.floor((Math.random() * 100000) + 1) + (new Date).getTime();
|
||||||
|
|
||||||
|
if (msg1 !== msg2) {
|
||||||
|
assert.notEqual(md5(msg1), md5(msg2));
|
||||||
|
} else {
|
||||||
|
assert.equal(md5(msg1), md5(msg1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support Node.js Buffers', function() {
|
||||||
|
var buffer = new Buffer('message áßäöü', 'utf8');
|
||||||
|
|
||||||
|
assert.equal(md5(buffer), md5('message áßäöü'));
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be able to use a binary encoded string', function() {
|
||||||
|
var hash1 = md5('abc', { asString: true });
|
||||||
|
var hash2 = md5(hash1 + 'a', { asString: true, encoding : 'binary' });
|
||||||
|
var hash3 = md5(hash1 + 'a', { encoding : 'binary' });
|
||||||
|
assert.equal(hash3, '131f0ac52813044f5110e4aec638c169');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support Uint8Array', function() {
|
||||||
|
// Polyfills
|
||||||
|
if (!Array.from) {
|
||||||
|
Array.from = function(src, fn) {
|
||||||
|
var result = new Array(src.length);
|
||||||
|
for (var i = 0; i < src.length; ++i)
|
||||||
|
result[i] = fn(src[i]);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!Uint8Array.from) {
|
||||||
|
Uint8Array.from = function(src) {
|
||||||
|
var result = new Uint8Array(src.length);
|
||||||
|
for (var i = 0; i < src.length; ++i)
|
||||||
|
result[i] = src[i];
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = 'foobarbaz';
|
||||||
|
var u8arr = Uint8Array.from(
|
||||||
|
Array.from(message, function(c) { return c.charCodeAt(0); }));
|
||||||
|
var u8aHash = md5(u8arr);
|
||||||
|
assert.equal(u8aHash, md5(message));
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
const {resolve} = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: [
|
||||||
|
'./md5.js'
|
||||||
|
],
|
||||||
|
output: {
|
||||||
|
path: resolve('./dist'),
|
||||||
|
filename: 'md5.min.js',
|
||||||
|
libraryTarget: "var",
|
||||||
|
library: "MD5"
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
dist
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
npm-debug.log
|
||||||
|
dump.rdb
|
||||||
|
node_modules
|
||||||
|
results.tap
|
||||||
|
results.xml
|
||||||
|
npm-shrinkwrap.json
|
||||||
|
config.json
|
||||||
|
.DS_Store
|
||||||
|
*/.DS_Store
|
||||||
|
*/*/.DS_Store
|
||||||
|
._*
|
||||||
|
*/._*
|
||||||
|
*/*/._*
|
||||||
|
coverage.*
|
||||||
|
lib-cov
|
||||||
|
complexity.md
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
language: node_js
|
||||||
|
|
||||||
|
node_js:
|
||||||
|
- 0.10
|
||||||
|
- 4.0
|
||||||
|
- 4
|
||||||
|
|
||||||
|
sudo: false
|
||||||
@ -0,0 +1,104 @@
|
|||||||
|
## **5.2.1**
|
||||||
|
- [Fix] ensure `key[]=x&key[]&key[]=y` results in 3, not 2, values
|
||||||
|
|
||||||
|
## [**5.2.0**](https://github.com/ljharb/qs/issues?milestone=30&state=closed)
|
||||||
|
- [**#64**](https://github.com/ljharb/qs/issues/64) Add option to sort object keys in the query string
|
||||||
|
|
||||||
|
## [**5.1.0**](https://github.com/ljharb/qs/issues?milestone=29&state=closed)
|
||||||
|
- [**#117**](https://github.com/ljharb/qs/issues/117) make URI encoding stringified results optional
|
||||||
|
- [**#106**](https://github.com/ljharb/qs/issues/106) Add flag `skipNulls` to optionally skip null values in stringify
|
||||||
|
|
||||||
|
## [**5.0.0**](https://github.com/ljharb/qs/issues?milestone=28&state=closed)
|
||||||
|
- [**#114**](https://github.com/ljharb/qs/issues/114) default allowDots to false
|
||||||
|
- [**#100**](https://github.com/ljharb/qs/issues/100) include dist to npm
|
||||||
|
|
||||||
|
## [**4.0.0**](https://github.com/ljharb/qs/issues?milestone=26&state=closed)
|
||||||
|
- [**#98**](https://github.com/ljharb/qs/issues/98) make returning plain objects and allowing prototype overwriting properties optional
|
||||||
|
|
||||||
|
## [**3.1.0**](https://github.com/ljharb/qs/issues?milestone=24&state=closed)
|
||||||
|
- [**#89**](https://github.com/ljharb/qs/issues/89) Add option to disable "Transform dot notation to bracket notation"
|
||||||
|
|
||||||
|
## [**3.0.0**](https://github.com/ljharb/qs/issues?milestone=23&state=closed)
|
||||||
|
- [**#80**](https://github.com/ljharb/qs/issues/80) qs.parse silently drops properties
|
||||||
|
- [**#77**](https://github.com/ljharb/qs/issues/77) Perf boost
|
||||||
|
- [**#60**](https://github.com/ljharb/qs/issues/60) Add explicit option to disable array parsing
|
||||||
|
- [**#74**](https://github.com/ljharb/qs/issues/74) Bad parse when turning array into object
|
||||||
|
- [**#81**](https://github.com/ljharb/qs/issues/81) Add a `filter` option
|
||||||
|
- [**#68**](https://github.com/ljharb/qs/issues/68) Fixed issue with recursion and passing strings into objects.
|
||||||
|
- [**#66**](https://github.com/ljharb/qs/issues/66) Add mixed array and object dot notation support Closes: #47
|
||||||
|
- [**#76**](https://github.com/ljharb/qs/issues/76) RFC 3986
|
||||||
|
- [**#85**](https://github.com/ljharb/qs/issues/85) No equal sign
|
||||||
|
- [**#84**](https://github.com/ljharb/qs/issues/84) update license attribute
|
||||||
|
|
||||||
|
## [**2.4.1**](https://github.com/ljharb/qs/issues?milestone=20&state=closed)
|
||||||
|
- [**#73**](https://github.com/ljharb/qs/issues/73) Property 'hasOwnProperty' of object #<Object> is not a function
|
||||||
|
|
||||||
|
## [**2.4.0**](https://github.com/ljharb/qs/issues?milestone=19&state=closed)
|
||||||
|
- [**#70**](https://github.com/ljharb/qs/issues/70) Add arrayFormat option
|
||||||
|
|
||||||
|
## [**2.3.3**](https://github.com/ljharb/qs/issues?milestone=18&state=closed)
|
||||||
|
- [**#59**](https://github.com/ljharb/qs/issues/59) make sure array indexes are >= 0, closes #57
|
||||||
|
- [**#58**](https://github.com/ljharb/qs/issues/58) make qs usable for browser loader
|
||||||
|
|
||||||
|
## [**2.3.2**](https://github.com/ljharb/qs/issues?milestone=17&state=closed)
|
||||||
|
- [**#55**](https://github.com/ljharb/qs/issues/55) allow merging a string into an object
|
||||||
|
|
||||||
|
## [**2.3.1**](https://github.com/ljharb/qs/issues?milestone=16&state=closed)
|
||||||
|
- [**#52**](https://github.com/ljharb/qs/issues/52) Return "undefined" and "false" instead of throwing "TypeError".
|
||||||
|
|
||||||
|
## [**2.3.0**](https://github.com/ljharb/qs/issues?milestone=15&state=closed)
|
||||||
|
- [**#50**](https://github.com/ljharb/qs/issues/50) add option to omit array indices, closes #46
|
||||||
|
|
||||||
|
## [**2.2.5**](https://github.com/ljharb/qs/issues?milestone=14&state=closed)
|
||||||
|
- [**#39**](https://github.com/ljharb/qs/issues/39) Is there an alternative to Buffer.isBuffer?
|
||||||
|
- [**#49**](https://github.com/ljharb/qs/issues/49) refactor utils.merge, fixes #45
|
||||||
|
- [**#41**](https://github.com/ljharb/qs/issues/41) avoid browserifying Buffer, for #39
|
||||||
|
|
||||||
|
## [**2.2.4**](https://github.com/ljharb/qs/issues?milestone=13&state=closed)
|
||||||
|
- [**#38**](https://github.com/ljharb/qs/issues/38) how to handle object keys beginning with a number
|
||||||
|
|
||||||
|
## [**2.2.3**](https://github.com/ljharb/qs/issues?milestone=12&state=closed)
|
||||||
|
- [**#37**](https://github.com/ljharb/qs/issues/37) parser discards first empty value in array
|
||||||
|
- [**#36**](https://github.com/ljharb/qs/issues/36) Update to lab 4.x
|
||||||
|
|
||||||
|
## [**2.2.2**](https://github.com/ljharb/qs/issues?milestone=11&state=closed)
|
||||||
|
- [**#33**](https://github.com/ljharb/qs/issues/33) Error when plain object in a value
|
||||||
|
- [**#34**](https://github.com/ljharb/qs/issues/34) use Object.prototype.hasOwnProperty.call instead of obj.hasOwnProperty
|
||||||
|
- [**#24**](https://github.com/ljharb/qs/issues/24) Changelog? Semver?
|
||||||
|
|
||||||
|
## [**2.2.1**](https://github.com/ljharb/qs/issues?milestone=10&state=closed)
|
||||||
|
- [**#32**](https://github.com/ljharb/qs/issues/32) account for circular references properly, closes #31
|
||||||
|
- [**#31**](https://github.com/ljharb/qs/issues/31) qs.parse stackoverflow on circular objects
|
||||||
|
|
||||||
|
## [**2.2.0**](https://github.com/ljharb/qs/issues?milestone=9&state=closed)
|
||||||
|
- [**#26**](https://github.com/ljharb/qs/issues/26) Don't use Buffer global if it's not present
|
||||||
|
- [**#30**](https://github.com/ljharb/qs/issues/30) Bug when merging non-object values into arrays
|
||||||
|
- [**#29**](https://github.com/ljharb/qs/issues/29) Don't call Utils.clone at the top of Utils.merge
|
||||||
|
- [**#23**](https://github.com/ljharb/qs/issues/23) Ability to not limit parameters?
|
||||||
|
|
||||||
|
## [**2.1.0**](https://github.com/ljharb/qs/issues?milestone=8&state=closed)
|
||||||
|
- [**#22**](https://github.com/ljharb/qs/issues/22) Enable using a RegExp as delimiter
|
||||||
|
|
||||||
|
## [**2.0.0**](https://github.com/ljharb/qs/issues?milestone=7&state=closed)
|
||||||
|
- [**#18**](https://github.com/ljharb/qs/issues/18) Why is there arrayLimit?
|
||||||
|
- [**#20**](https://github.com/ljharb/qs/issues/20) Configurable parametersLimit
|
||||||
|
- [**#21**](https://github.com/ljharb/qs/issues/21) make all limits optional, for #18, for #20
|
||||||
|
|
||||||
|
## [**1.2.2**](https://github.com/ljharb/qs/issues?milestone=6&state=closed)
|
||||||
|
- [**#19**](https://github.com/ljharb/qs/issues/19) Don't overwrite null values
|
||||||
|
|
||||||
|
## [**1.2.1**](https://github.com/ljharb/qs/issues?milestone=5&state=closed)
|
||||||
|
- [**#16**](https://github.com/ljharb/qs/issues/16) ignore non-string delimiters
|
||||||
|
- [**#15**](https://github.com/ljharb/qs/issues/15) Close code block
|
||||||
|
|
||||||
|
## [**1.2.0**](https://github.com/ljharb/qs/issues?milestone=4&state=closed)
|
||||||
|
- [**#12**](https://github.com/ljharb/qs/issues/12) Add optional delim argument
|
||||||
|
- [**#13**](https://github.com/ljharb/qs/issues/13) fix #11: flattened keys in array are now correctly parsed
|
||||||
|
|
||||||
|
## [**1.1.0**](https://github.com/ljharb/qs/issues?milestone=3&state=closed)
|
||||||
|
- [**#7**](https://github.com/ljharb/qs/issues/7) Empty values of a POST array disappear after being submitted
|
||||||
|
- [**#9**](https://github.com/ljharb/qs/issues/9) Should not omit equals signs (=) when value is null
|
||||||
|
- [**#6**](https://github.com/ljharb/qs/issues/6) Minor grammar fix in README
|
||||||
|
|
||||||
|
## [**1.0.2**](https://github.com/ljharb/qs/issues?milestone=2&state=closed)
|
||||||
|
- [**#5**](https://github.com/ljharb/qs/issues/5) array holes incorrectly copied into object on large index
|
||||||
@ -0,0 +1 @@
|
|||||||
|
Please view our [hapijs contributing guide](https://github.com/hapijs/hapi/blob/master/CONTRIBUTING.md).
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
Copyright (c) 2014 Nathan LaFreniere and other contributors.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* The names of any contributors may not be used to endorse or promote
|
||||||
|
products derived from this software without specific prior written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
* * *
|
||||||
|
|
||||||
|
The complete list of contributors can be found at: https://github.com/hapijs/qs/graphs/contributors
|
||||||
@ -0,0 +1,331 @@
|
|||||||
|
# qs
|
||||||
|
|
||||||
|
A querystring parsing and stringifying library with some added security.
|
||||||
|
|
||||||
|
[](http://travis-ci.org/hapijs/qs)
|
||||||
|
|
||||||
|
Lead Maintainer: [Nathan LaFreniere](https://github.com/nlf)
|
||||||
|
|
||||||
|
The **qs** module was originally created and maintained by [TJ Holowaychuk](https://github.com/visionmedia/node-querystring).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var Qs = require('qs');
|
||||||
|
|
||||||
|
var obj = Qs.parse('a=c'); // { a: 'c' }
|
||||||
|
var str = Qs.stringify(obj); // 'a=c'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parsing Objects
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse(string, [options]);
|
||||||
|
```
|
||||||
|
|
||||||
|
**qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`.
|
||||||
|
For example, the string `'foo[bar]=baz'` converts to:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
foo: {
|
||||||
|
bar: 'baz'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When using the `plainObjects` option the parsed value is returned as a plain object, created via `Object.create(null)` and as such you should be aware that prototype methods will not exist on it and a user may set those names to whatever value they like:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a.hasOwnProperty=b', { plainObjects: true });
|
||||||
|
// { a: { hasOwnProperty: 'b' } }
|
||||||
|
```
|
||||||
|
|
||||||
|
By default parameters that would overwrite properties on the object prototype are ignored, if you wish to keep the data from those fields either use `plainObjects` as mentioned above, or set `allowPrototypes` to `true` which will allow user input to overwrite those properties. *WARNING* It is generally a bad idea to enable this option as it can cause problems when attempting to use the properties that have been overwritten. Always be careful with this option.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a.hasOwnProperty=b', { allowPrototypes: true });
|
||||||
|
// { a: { hasOwnProperty: 'b' } }
|
||||||
|
```
|
||||||
|
|
||||||
|
URI encoded strings work too:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a%5Bb%5D=c');
|
||||||
|
// { a: { b: 'c' } }
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also nest your objects, like `'foo[bar][baz]=foobarbaz'`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
foo: {
|
||||||
|
bar: {
|
||||||
|
baz: 'foobarbaz'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, when nesting objects **qs** will only parse up to 5 children deep. This means if you attempt to parse a string like
|
||||||
|
`'a[b][c][d][e][f][g][h][i]=j'` your resulting object will be:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
a: {
|
||||||
|
b: {
|
||||||
|
c: {
|
||||||
|
d: {
|
||||||
|
e: {
|
||||||
|
f: {
|
||||||
|
'[g][h][i]': 'j'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This depth can be overridden by passing a `depth` option to `Qs.parse(string, [options])`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 });
|
||||||
|
// { a: { b: { '[c][d][e][f][g][h][i]': 'j' } } }
|
||||||
|
```
|
||||||
|
|
||||||
|
The depth limit helps mitigate abuse when **qs** is used to parse user input, and it is recommended to keep it a reasonably small number.
|
||||||
|
|
||||||
|
For similar reasons, by default **qs** will only parse up to 1000 parameters. This can be overridden by passing a `parameterLimit` option:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a=b&c=d', { parameterLimit: 1 });
|
||||||
|
// { a: 'b' }
|
||||||
|
```
|
||||||
|
|
||||||
|
An optional delimiter can also be passed:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a=b;c=d', { delimiter: ';' });
|
||||||
|
// { a: 'b', c: 'd' }
|
||||||
|
```
|
||||||
|
|
||||||
|
Delimiters can be a regular expression too:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ });
|
||||||
|
// { a: 'b', c: 'd', e: 'f' }
|
||||||
|
```
|
||||||
|
|
||||||
|
Option `allowDots` can be used to enable dot notation:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a.b=c', { allowDots: true });
|
||||||
|
// { a: { b: 'c' } }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parsing Arrays
|
||||||
|
|
||||||
|
**qs** can also parse arrays using a similar `[]` notation:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a[]=b&a[]=c');
|
||||||
|
// { a: ['b', 'c'] }
|
||||||
|
```
|
||||||
|
|
||||||
|
You may specify an index as well:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a[1]=c&a[0]=b');
|
||||||
|
// { a: ['b', 'c'] }
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the only difference between an index in an array and a key in an object is that the value between the brackets must be a number
|
||||||
|
to create an array. When creating arrays with specific indices, **qs** will compact a sparse array to only the existing values preserving
|
||||||
|
their order:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a[1]=b&a[15]=c');
|
||||||
|
// { a: ['b', 'c'] }
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that an empty string is also a value, and will be preserved:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a[]=&a[]=b');
|
||||||
|
// { a: ['', 'b'] }
|
||||||
|
Qs.parse('a[0]=b&a[1]=&a[2]=c');
|
||||||
|
// { a: ['b', '', 'c'] }
|
||||||
|
```
|
||||||
|
|
||||||
|
**qs** will also limit specifying indices in an array to a maximum index of `20`. Any array members with an index of greater than `20` will
|
||||||
|
instead be converted to an object with the index as the key:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a[100]=b');
|
||||||
|
// { a: { '100': 'b' } }
|
||||||
|
```
|
||||||
|
|
||||||
|
This limit can be overridden by passing an `arrayLimit` option:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a[1]=b', { arrayLimit: 0 });
|
||||||
|
// { a: { '1': 'b' } }
|
||||||
|
```
|
||||||
|
|
||||||
|
To disable array parsing entirely, set `parseArrays` to `false`.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a[]=b', { parseArrays: false });
|
||||||
|
// { a: { '0': 'b' } }
|
||||||
|
```
|
||||||
|
|
||||||
|
If you mix notations, **qs** will merge the two items into an object:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a[0]=b&a[b]=c');
|
||||||
|
// { a: { '0': 'b', b: 'c' } }
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also create arrays of objects:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a[][b]=c');
|
||||||
|
// { a: [{ b: 'c' }] }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stringifying
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.stringify(object, [options]);
|
||||||
|
```
|
||||||
|
|
||||||
|
When stringifying, **qs** by default URI encodes output. Objects are stringified as you would expect:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.stringify({ a: 'b' });
|
||||||
|
// 'a=b'
|
||||||
|
Qs.stringify({ a: { b: 'c' } });
|
||||||
|
// 'a%5Bb%5D=c'
|
||||||
|
```
|
||||||
|
|
||||||
|
This encoding can be disabled by setting the `encode` option to `false`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.stringify({ a: { b: 'c' } }, { encode: false });
|
||||||
|
// 'a[b]=c'
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples beyond this point will be shown as though the output is not URI encoded for clarity. Please note that the return values in these cases *will* be URI encoded during real usage.
|
||||||
|
|
||||||
|
When arrays are stringified, by default they are given explicit indices:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.stringify({ a: ['b', 'c', 'd'] });
|
||||||
|
// 'a[0]=b&a[1]=c&a[2]=d'
|
||||||
|
```
|
||||||
|
|
||||||
|
You may override this by setting the `indices` option to `false`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false });
|
||||||
|
// 'a=b&a=c&a=d'
|
||||||
|
```
|
||||||
|
|
||||||
|
You may use the `arrayFormat` option to specify the format of the output array
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })
|
||||||
|
// 'a[0]=b&a[1]=c'
|
||||||
|
Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })
|
||||||
|
// 'a[]=b&a[]=c'
|
||||||
|
Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })
|
||||||
|
// 'a=b&a=c'
|
||||||
|
```
|
||||||
|
|
||||||
|
Empty strings and null values will omit the value, but the equals sign (=) remains in place:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.stringify({ a: '' });
|
||||||
|
// 'a='
|
||||||
|
```
|
||||||
|
|
||||||
|
Properties that are set to `undefined` will be omitted entirely:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.stringify({ a: null, b: undefined });
|
||||||
|
// 'a='
|
||||||
|
```
|
||||||
|
|
||||||
|
The delimiter may be overridden with stringify as well:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' });
|
||||||
|
// 'a=b;c=d'
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, you can use the `filter` option to restrict which keys will be included in the stringified output.
|
||||||
|
If you pass a function, it will be called for each key to obtain the replacement value. Otherwise, if you
|
||||||
|
pass an array, it will be used to select properties and array indices for stringification:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function filterFunc(prefix, value) {
|
||||||
|
if (prefix == 'b') {
|
||||||
|
// Return an `undefined` value to omit a property.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (prefix == 'e[f]') {
|
||||||
|
return value.getTime();
|
||||||
|
}
|
||||||
|
if (prefix == 'e[g][0]') {
|
||||||
|
return value * 2;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
Qs.stringify({ a: 'b', c: 'd', e: { f: new Date(123), g: [2] } }, { filter: filterFunc })
|
||||||
|
// 'a=b&c=d&e[f]=123&e[g][0]=4'
|
||||||
|
Qs.stringify({ a: 'b', c: 'd', e: 'f' }, { filter: ['a', 'e'] })
|
||||||
|
// 'a=b&e=f'
|
||||||
|
Qs.stringify({ a: ['b', 'c', 'd'], e: 'f' }, { filter: ['a', 0, 2] })
|
||||||
|
// 'a[0]=b&a[2]=d'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Handling of `null` values
|
||||||
|
|
||||||
|
By default, `null` values are treated like empty strings:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.stringify({ a: null, b: '' });
|
||||||
|
// 'a=&b='
|
||||||
|
```
|
||||||
|
|
||||||
|
Parsing does not distinguish between parameters with and without equal signs. Both are converted to empty strings.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a&b=')
|
||||||
|
// { a: '', b: '' }
|
||||||
|
```
|
||||||
|
|
||||||
|
To distinguish between `null` values and empty strings use the `strictNullHandling` flag. In the result string the `null`
|
||||||
|
values have no `=` sign:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.stringify({ a: null, b: '' }, { strictNullHandling: true });
|
||||||
|
// 'a&b='
|
||||||
|
```
|
||||||
|
|
||||||
|
To parse values without `=` back to `null` use the `strictNullHandling` flag:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Qs.parse('a&b=', { strictNullHandling: true });
|
||||||
|
// { a: null, b: '' }
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
To completely skip rendering keys with `null` values, use the `skipNulls` flag:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
qs.stringify({ a: 'b', c: null}, { skipNulls: true })
|
||||||
|
// 'a=b'
|
||||||
|
```
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "qs",
|
||||||
|
"main": "dist/qs.js",
|
||||||
|
"version": "5.1.0",
|
||||||
|
"homepage": "https://github.com/hapijs/qs",
|
||||||
|
"authors": [
|
||||||
|
"Nathan LaFreniere <quitlahok@gmail.com>"
|
||||||
|
],
|
||||||
|
"description": "A querystring parser that supports nesting and arrays, with a depth limit",
|
||||||
|
"keywords": [
|
||||||
|
"querystring",
|
||||||
|
"qs"
|
||||||
|
],
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"ignore": [
|
||||||
|
"**/.*",
|
||||||
|
"node_modules",
|
||||||
|
"bower_components",
|
||||||
|
"test",
|
||||||
|
"tests"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "qs",
|
||||||
|
"repository": "hapijs/qs",
|
||||||
|
"description": "query-string parser / stringifier with nesting support",
|
||||||
|
"version": "5.1.0",
|
||||||
|
"keywords": ["querystring", "query", "parser"],
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"scripts": [
|
||||||
|
"lib/index.js",
|
||||||
|
"lib/parse.js",
|
||||||
|
"lib/stringify.js",
|
||||||
|
"lib/utils.js"
|
||||||
|
],
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
}
|
||||||
@ -0,0 +1,551 @@
|
|||||||
|
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Qs = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||||
|
// Load modules
|
||||||
|
|
||||||
|
var Stringify = require('./stringify');
|
||||||
|
var Parse = require('./parse');
|
||||||
|
|
||||||
|
|
||||||
|
// Declare internals
|
||||||
|
|
||||||
|
var internals = {};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
stringify: Stringify,
|
||||||
|
parse: Parse
|
||||||
|
};
|
||||||
|
|
||||||
|
},{"./parse":2,"./stringify":3}],2:[function(require,module,exports){
|
||||||
|
// Load modules
|
||||||
|
|
||||||
|
var Utils = require('./utils');
|
||||||
|
|
||||||
|
|
||||||
|
// Declare internals
|
||||||
|
|
||||||
|
var internals = {
|
||||||
|
delimiter: '&',
|
||||||
|
depth: 5,
|
||||||
|
arrayLimit: 20,
|
||||||
|
parameterLimit: 1000,
|
||||||
|
strictNullHandling: false,
|
||||||
|
plainObjects: false,
|
||||||
|
allowPrototypes: false,
|
||||||
|
allowDots: false
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
internals.parseValues = function (str, options) {
|
||||||
|
|
||||||
|
var obj = {};
|
||||||
|
var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit);
|
||||||
|
|
||||||
|
for (var i = 0, il = parts.length; i < il; ++i) {
|
||||||
|
var part = parts[i];
|
||||||
|
var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1;
|
||||||
|
|
||||||
|
var key, val;
|
||||||
|
if (pos === -1) {
|
||||||
|
key = Utils.decode(part);
|
||||||
|
val = options.strictNullHandling ? null : '';
|
||||||
|
} else {
|
||||||
|
key = Utils.decode(part.slice(0, pos));
|
||||||
|
val = Utils.decode(part.slice(pos + 1));
|
||||||
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||||
|
obj[key] = [].concat(obj[key]).concat(val);
|
||||||
|
} else {
|
||||||
|
obj[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
internals.parseObject = function (chain, val, options) {
|
||||||
|
|
||||||
|
if (!chain.length) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
var root = chain.shift();
|
||||||
|
|
||||||
|
var obj;
|
||||||
|
if (root === '[]') {
|
||||||
|
obj = [];
|
||||||
|
obj = obj.concat(internals.parseObject(chain, val, options));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
obj = options.plainObjects ? Object.create(null) : {};
|
||||||
|
var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root;
|
||||||
|
var index = parseInt(cleanRoot, 10);
|
||||||
|
var indexString = '' + index;
|
||||||
|
if (!isNaN(index) &&
|
||||||
|
root !== cleanRoot &&
|
||||||
|
indexString === cleanRoot &&
|
||||||
|
index >= 0 &&
|
||||||
|
(options.parseArrays &&
|
||||||
|
index <= options.arrayLimit)) {
|
||||||
|
|
||||||
|
obj = [];
|
||||||
|
obj[index] = internals.parseObject(chain, val, options);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
obj[cleanRoot] = internals.parseObject(chain, val, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
internals.parseKeys = function (key, val, options) {
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform dot notation to bracket notation
|
||||||
|
|
||||||
|
if (options.allowDots) {
|
||||||
|
key = key.replace(/\.([^\.\[]+)/g, '[$1]');
|
||||||
|
}
|
||||||
|
|
||||||
|
// The regex chunks
|
||||||
|
|
||||||
|
var parent = /^([^\[\]]*)/;
|
||||||
|
var child = /(\[[^\[\]]*\])/g;
|
||||||
|
|
||||||
|
// Get the parent
|
||||||
|
|
||||||
|
var segment = parent.exec(key);
|
||||||
|
|
||||||
|
// Stash the parent if it exists
|
||||||
|
|
||||||
|
var keys = [];
|
||||||
|
if (segment[1]) {
|
||||||
|
// If we aren't using plain objects, optionally prefix keys
|
||||||
|
// that would overwrite object prototype properties
|
||||||
|
if (!options.plainObjects &&
|
||||||
|
Object.prototype.hasOwnProperty(segment[1])) {
|
||||||
|
|
||||||
|
if (!options.allowPrototypes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys.push(segment[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through children appending to the array until we hit depth
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
while ((segment = child.exec(key)) !== null && i < options.depth) {
|
||||||
|
|
||||||
|
++i;
|
||||||
|
if (!options.plainObjects &&
|
||||||
|
Object.prototype.hasOwnProperty(segment[1].replace(/\[|\]/g, ''))) {
|
||||||
|
|
||||||
|
if (!options.allowPrototypes) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keys.push(segment[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's a remainder, just add whatever is left
|
||||||
|
|
||||||
|
if (segment) {
|
||||||
|
keys.push('[' + key.slice(segment.index) + ']');
|
||||||
|
}
|
||||||
|
|
||||||
|
return internals.parseObject(keys, val, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = function (str, options) {
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : internals.delimiter;
|
||||||
|
options.depth = typeof options.depth === 'number' ? options.depth : internals.depth;
|
||||||
|
options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : internals.arrayLimit;
|
||||||
|
options.parseArrays = options.parseArrays !== false;
|
||||||
|
options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : internals.allowDots;
|
||||||
|
options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : internals.plainObjects;
|
||||||
|
options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : internals.allowPrototypes;
|
||||||
|
options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : internals.parameterLimit;
|
||||||
|
options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
|
||||||
|
|
||||||
|
if (str === '' ||
|
||||||
|
str === null ||
|
||||||
|
typeof str === 'undefined') {
|
||||||
|
|
||||||
|
return options.plainObjects ? Object.create(null) : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var tempObj = typeof str === 'string' ? internals.parseValues(str, options) : str;
|
||||||
|
var obj = options.plainObjects ? Object.create(null) : {};
|
||||||
|
|
||||||
|
// Iterate over the keys and setup the new object
|
||||||
|
|
||||||
|
var keys = Object.keys(tempObj);
|
||||||
|
for (var i = 0, il = keys.length; i < il; ++i) {
|
||||||
|
var key = keys[i];
|
||||||
|
var newObj = internals.parseKeys(key, tempObj[key], options);
|
||||||
|
obj = Utils.merge(obj, newObj, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Utils.compact(obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
},{"./utils":4}],3:[function(require,module,exports){
|
||||||
|
// Load modules
|
||||||
|
|
||||||
|
var Utils = require('./utils');
|
||||||
|
|
||||||
|
|
||||||
|
// Declare internals
|
||||||
|
|
||||||
|
var internals = {
|
||||||
|
delimiter: '&',
|
||||||
|
arrayPrefixGenerators: {
|
||||||
|
brackets: function (prefix, key) {
|
||||||
|
|
||||||
|
return prefix + '[]';
|
||||||
|
},
|
||||||
|
indices: function (prefix, key) {
|
||||||
|
|
||||||
|
return prefix + '[' + key + ']';
|
||||||
|
},
|
||||||
|
repeat: function (prefix, key) {
|
||||||
|
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
strictNullHandling: false,
|
||||||
|
skipNulls: false,
|
||||||
|
encode: true
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
internals.stringify = function (obj, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort) {
|
||||||
|
|
||||||
|
if (typeof filter === 'function') {
|
||||||
|
obj = filter(prefix, obj);
|
||||||
|
}
|
||||||
|
else if (Utils.isBuffer(obj)) {
|
||||||
|
obj = obj.toString();
|
||||||
|
}
|
||||||
|
else if (obj instanceof Date) {
|
||||||
|
obj = obj.toISOString();
|
||||||
|
}
|
||||||
|
else if (obj === null) {
|
||||||
|
if (strictNullHandling) {
|
||||||
|
return encode ? Utils.encode(prefix) : prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj === 'string' ||
|
||||||
|
typeof obj === 'number' ||
|
||||||
|
typeof obj === 'boolean') {
|
||||||
|
|
||||||
|
if (encode) {
|
||||||
|
return [Utils.encode(prefix) + '=' + Utils.encode(obj)];
|
||||||
|
}
|
||||||
|
return [prefix + '=' + obj];
|
||||||
|
}
|
||||||
|
|
||||||
|
var values = [];
|
||||||
|
|
||||||
|
if (typeof obj === 'undefined') {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
var objKeys;
|
||||||
|
if (Array.isArray(filter)) {
|
||||||
|
objKeys = filter;
|
||||||
|
} else {
|
||||||
|
var keys = Object.keys(obj);
|
||||||
|
objKeys = sort ? keys.sort(sort) : keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0, il = objKeys.length; i < il; ++i) {
|
||||||
|
var key = objKeys[i];
|
||||||
|
|
||||||
|
if (skipNulls &&
|
||||||
|
obj[key] === null) {
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
values = values.concat(internals.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encode, filter));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
values = values.concat(internals.stringify(obj[key], prefix + '[' + key + ']', generateArrayPrefix, strictNullHandling, skipNulls, encode, filter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = function (obj, options) {
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
var delimiter = typeof options.delimiter === 'undefined' ? internals.delimiter : options.delimiter;
|
||||||
|
var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
|
||||||
|
var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : internals.skipNulls;
|
||||||
|
var encode = typeof options.encode === 'boolean' ? options.encode : internals.encode;
|
||||||
|
var sort = typeof options.sort === 'function' ? options.sort : null;
|
||||||
|
var objKeys;
|
||||||
|
var filter;
|
||||||
|
if (typeof options.filter === 'function') {
|
||||||
|
filter = options.filter;
|
||||||
|
obj = filter('', obj);
|
||||||
|
}
|
||||||
|
else if (Array.isArray(options.filter)) {
|
||||||
|
objKeys = filter = options.filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = [];
|
||||||
|
|
||||||
|
if (typeof obj !== 'object' ||
|
||||||
|
obj === null) {
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
var arrayFormat;
|
||||||
|
if (options.arrayFormat in internals.arrayPrefixGenerators) {
|
||||||
|
arrayFormat = options.arrayFormat;
|
||||||
|
}
|
||||||
|
else if ('indices' in options) {
|
||||||
|
arrayFormat = options.indices ? 'indices' : 'repeat';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
arrayFormat = 'indices';
|
||||||
|
}
|
||||||
|
|
||||||
|
var generateArrayPrefix = internals.arrayPrefixGenerators[arrayFormat];
|
||||||
|
|
||||||
|
if (!objKeys) {
|
||||||
|
objKeys = Object.keys(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sort) {
|
||||||
|
objKeys.sort(sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0, il = objKeys.length; i < il; ++i) {
|
||||||
|
var key = objKeys[i];
|
||||||
|
|
||||||
|
if (skipNulls &&
|
||||||
|
obj[key] === null) {
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
keys = keys.concat(internals.stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort));
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys.join(delimiter);
|
||||||
|
};
|
||||||
|
|
||||||
|
},{"./utils":4}],4:[function(require,module,exports){
|
||||||
|
// Load modules
|
||||||
|
|
||||||
|
|
||||||
|
// Declare internals
|
||||||
|
|
||||||
|
var internals = {};
|
||||||
|
internals.hexTable = new Array(256);
|
||||||
|
for (var h = 0; h < 256; ++h) {
|
||||||
|
internals.hexTable[h] = '%' + ((h < 16 ? '0' : '') + h.toString(16)).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
exports.arrayToObject = function (source, options) {
|
||||||
|
|
||||||
|
var obj = options.plainObjects ? Object.create(null) : {};
|
||||||
|
for (var i = 0, il = source.length; i < il; ++i) {
|
||||||
|
if (typeof source[i] !== 'undefined') {
|
||||||
|
|
||||||
|
obj[i] = source[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.merge = function (target, source, options) {
|
||||||
|
|
||||||
|
if (!source) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof source !== 'object') {
|
||||||
|
if (Array.isArray(target)) {
|
||||||
|
target.push(source);
|
||||||
|
}
|
||||||
|
else if (typeof target === 'object') {
|
||||||
|
target[source] = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
target = [target, source];
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof target !== 'object') {
|
||||||
|
target = [target].concat(source);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(target) &&
|
||||||
|
!Array.isArray(source)) {
|
||||||
|
|
||||||
|
target = exports.arrayToObject(target, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = Object.keys(source);
|
||||||
|
for (var k = 0, kl = keys.length; k < kl; ++k) {
|
||||||
|
var key = keys[k];
|
||||||
|
var value = source[key];
|
||||||
|
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(target, key)) {
|
||||||
|
target[key] = value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
target[key] = exports.merge(target[key], value, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.decode = function (str) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(str.replace(/\+/g, ' '));
|
||||||
|
} catch (e) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.encode = function (str) {
|
||||||
|
|
||||||
|
// This code was originally written by Brian White (mscdex) for the io.js core querystring library.
|
||||||
|
// It has been adapted here for stricter adherence to RFC 3986
|
||||||
|
if (str.length === 0) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof str !== 'string') {
|
||||||
|
str = '' + str;
|
||||||
|
}
|
||||||
|
|
||||||
|
var out = '';
|
||||||
|
for (var i = 0, il = str.length; i < il; ++i) {
|
||||||
|
var c = str.charCodeAt(i);
|
||||||
|
|
||||||
|
if (c === 0x2D || // -
|
||||||
|
c === 0x2E || // .
|
||||||
|
c === 0x5F || // _
|
||||||
|
c === 0x7E || // ~
|
||||||
|
(c >= 0x30 && c <= 0x39) || // 0-9
|
||||||
|
(c >= 0x41 && c <= 0x5A) || // a-z
|
||||||
|
(c >= 0x61 && c <= 0x7A)) { // A-Z
|
||||||
|
|
||||||
|
out += str[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c < 0x80) {
|
||||||
|
out += internals.hexTable[c];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c < 0x800) {
|
||||||
|
out += internals.hexTable[0xC0 | (c >> 6)] + internals.hexTable[0x80 | (c & 0x3F)];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c < 0xD800 || c >= 0xE000) {
|
||||||
|
out += internals.hexTable[0xE0 | (c >> 12)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
c = 0x10000 + (((c & 0x3FF) << 10) | (str.charCodeAt(i) & 0x3FF));
|
||||||
|
out += internals.hexTable[0xF0 | (c >> 18)] + internals.hexTable[0x80 | ((c >> 12) & 0x3F)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.compact = function (obj, refs) {
|
||||||
|
|
||||||
|
if (typeof obj !== 'object' ||
|
||||||
|
obj === null) {
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
refs = refs || [];
|
||||||
|
var lookup = refs.indexOf(obj);
|
||||||
|
if (lookup !== -1) {
|
||||||
|
return refs[lookup];
|
||||||
|
}
|
||||||
|
|
||||||
|
refs.push(obj);
|
||||||
|
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
var compacted = [];
|
||||||
|
|
||||||
|
for (var i = 0, il = obj.length; i < il; ++i) {
|
||||||
|
if (typeof obj[i] !== 'undefined') {
|
||||||
|
compacted.push(obj[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return compacted;
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = Object.keys(obj);
|
||||||
|
for (i = 0, il = keys.length; i < il; ++i) {
|
||||||
|
var key = keys[i];
|
||||||
|
obj[key] = exports.compact(obj[key], refs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.isRegExp = function (obj) {
|
||||||
|
|
||||||
|
return Object.prototype.toString.call(obj) === '[object RegExp]';
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.isBuffer = function (obj) {
|
||||||
|
|
||||||
|
if (obj === null ||
|
||||||
|
typeof obj === 'undefined') {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!(obj.constructor &&
|
||||||
|
obj.constructor.isBuffer &&
|
||||||
|
obj.constructor.isBuffer(obj));
|
||||||
|
};
|
||||||
|
|
||||||
|
},{}]},{},[1])(1)
|
||||||
|
});
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
// Load modules
|
||||||
|
|
||||||
|
var Stringify = require('./stringify');
|
||||||
|
var Parse = require('./parse');
|
||||||
|
|
||||||
|
|
||||||
|
// Declare internals
|
||||||
|
|
||||||
|
var internals = {};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
stringify: Stringify,
|
||||||
|
parse: Parse
|
||||||
|
};
|
||||||
@ -0,0 +1,182 @@
|
|||||||
|
// Load modules
|
||||||
|
|
||||||
|
var Utils = require('./utils');
|
||||||
|
|
||||||
|
|
||||||
|
// Declare internals
|
||||||
|
|
||||||
|
var internals = {
|
||||||
|
delimiter: '&',
|
||||||
|
depth: 5,
|
||||||
|
arrayLimit: 20,
|
||||||
|
parameterLimit: 1000,
|
||||||
|
strictNullHandling: false,
|
||||||
|
plainObjects: false,
|
||||||
|
allowPrototypes: false,
|
||||||
|
allowDots: false
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
internals.parseValues = function (str, options) {
|
||||||
|
|
||||||
|
var obj = {};
|
||||||
|
var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit);
|
||||||
|
|
||||||
|
for (var i = 0, il = parts.length; i < il; ++i) {
|
||||||
|
var part = parts[i];
|
||||||
|
var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1;
|
||||||
|
|
||||||
|
var key, val;
|
||||||
|
if (pos === -1) {
|
||||||
|
key = Utils.decode(part);
|
||||||
|
val = options.strictNullHandling ? null : '';
|
||||||
|
} else {
|
||||||
|
key = Utils.decode(part.slice(0, pos));
|
||||||
|
val = Utils.decode(part.slice(pos + 1));
|
||||||
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||||
|
obj[key] = [].concat(obj[key]).concat(val);
|
||||||
|
} else {
|
||||||
|
obj[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
internals.parseObject = function (chain, val, options) {
|
||||||
|
|
||||||
|
if (!chain.length) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
var root = chain.shift();
|
||||||
|
|
||||||
|
var obj;
|
||||||
|
if (root === '[]') {
|
||||||
|
obj = [];
|
||||||
|
obj = obj.concat(internals.parseObject(chain, val, options));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
obj = options.plainObjects ? Object.create(null) : {};
|
||||||
|
var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root;
|
||||||
|
var index = parseInt(cleanRoot, 10);
|
||||||
|
var indexString = '' + index;
|
||||||
|
if (!isNaN(index) &&
|
||||||
|
root !== cleanRoot &&
|
||||||
|
indexString === cleanRoot &&
|
||||||
|
index >= 0 &&
|
||||||
|
(options.parseArrays &&
|
||||||
|
index <= options.arrayLimit)) {
|
||||||
|
|
||||||
|
obj = [];
|
||||||
|
obj[index] = internals.parseObject(chain, val, options);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
obj[cleanRoot] = internals.parseObject(chain, val, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
internals.parseKeys = function (key, val, options) {
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform dot notation to bracket notation
|
||||||
|
|
||||||
|
if (options.allowDots) {
|
||||||
|
key = key.replace(/\.([^\.\[]+)/g, '[$1]');
|
||||||
|
}
|
||||||
|
|
||||||
|
// The regex chunks
|
||||||
|
|
||||||
|
var parent = /^([^\[\]]*)/;
|
||||||
|
var child = /(\[[^\[\]]*\])/g;
|
||||||
|
|
||||||
|
// Get the parent
|
||||||
|
|
||||||
|
var segment = parent.exec(key);
|
||||||
|
|
||||||
|
// Stash the parent if it exists
|
||||||
|
|
||||||
|
var keys = [];
|
||||||
|
if (segment[1]) {
|
||||||
|
// If we aren't using plain objects, optionally prefix keys
|
||||||
|
// that would overwrite object prototype properties
|
||||||
|
if (!options.plainObjects &&
|
||||||
|
Object.prototype.hasOwnProperty(segment[1])) {
|
||||||
|
|
||||||
|
if (!options.allowPrototypes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys.push(segment[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through children appending to the array until we hit depth
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
while ((segment = child.exec(key)) !== null && i < options.depth) {
|
||||||
|
|
||||||
|
++i;
|
||||||
|
if (!options.plainObjects &&
|
||||||
|
Object.prototype.hasOwnProperty(segment[1].replace(/\[|\]/g, ''))) {
|
||||||
|
|
||||||
|
if (!options.allowPrototypes) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keys.push(segment[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's a remainder, just add whatever is left
|
||||||
|
|
||||||
|
if (segment) {
|
||||||
|
keys.push('[' + key.slice(segment.index) + ']');
|
||||||
|
}
|
||||||
|
|
||||||
|
return internals.parseObject(keys, val, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = function (str, options) {
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : internals.delimiter;
|
||||||
|
options.depth = typeof options.depth === 'number' ? options.depth : internals.depth;
|
||||||
|
options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : internals.arrayLimit;
|
||||||
|
options.parseArrays = options.parseArrays !== false;
|
||||||
|
options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : internals.allowDots;
|
||||||
|
options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : internals.plainObjects;
|
||||||
|
options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : internals.allowPrototypes;
|
||||||
|
options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : internals.parameterLimit;
|
||||||
|
options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
|
||||||
|
|
||||||
|
if (str === '' ||
|
||||||
|
str === null ||
|
||||||
|
typeof str === 'undefined') {
|
||||||
|
|
||||||
|
return options.plainObjects ? Object.create(null) : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var tempObj = typeof str === 'string' ? internals.parseValues(str, options) : str;
|
||||||
|
var obj = options.plainObjects ? Object.create(null) : {};
|
||||||
|
|
||||||
|
// Iterate over the keys and setup the new object
|
||||||
|
|
||||||
|
var keys = Object.keys(tempObj);
|
||||||
|
for (var i = 0, il = keys.length; i < il; ++i) {
|
||||||
|
var key = keys[i];
|
||||||
|
var newObj = internals.parseKeys(key, tempObj[key], options);
|
||||||
|
obj = Utils.merge(obj, newObj, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Utils.compact(obj);
|
||||||
|
};
|
||||||
@ -0,0 +1,154 @@
|
|||||||
|
// Load modules
|
||||||
|
|
||||||
|
var Utils = require('./utils');
|
||||||
|
|
||||||
|
|
||||||
|
// Declare internals
|
||||||
|
|
||||||
|
var internals = {
|
||||||
|
delimiter: '&',
|
||||||
|
arrayPrefixGenerators: {
|
||||||
|
brackets: function (prefix, key) {
|
||||||
|
|
||||||
|
return prefix + '[]';
|
||||||
|
},
|
||||||
|
indices: function (prefix, key) {
|
||||||
|
|
||||||
|
return prefix + '[' + key + ']';
|
||||||
|
},
|
||||||
|
repeat: function (prefix, key) {
|
||||||
|
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
strictNullHandling: false,
|
||||||
|
skipNulls: false,
|
||||||
|
encode: true
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
internals.stringify = function (obj, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort) {
|
||||||
|
|
||||||
|
if (typeof filter === 'function') {
|
||||||
|
obj = filter(prefix, obj);
|
||||||
|
}
|
||||||
|
else if (Utils.isBuffer(obj)) {
|
||||||
|
obj = obj.toString();
|
||||||
|
}
|
||||||
|
else if (obj instanceof Date) {
|
||||||
|
obj = obj.toISOString();
|
||||||
|
}
|
||||||
|
else if (obj === null) {
|
||||||
|
if (strictNullHandling) {
|
||||||
|
return encode ? Utils.encode(prefix) : prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj === 'string' ||
|
||||||
|
typeof obj === 'number' ||
|
||||||
|
typeof obj === 'boolean') {
|
||||||
|
|
||||||
|
if (encode) {
|
||||||
|
return [Utils.encode(prefix) + '=' + Utils.encode(obj)];
|
||||||
|
}
|
||||||
|
return [prefix + '=' + obj];
|
||||||
|
}
|
||||||
|
|
||||||
|
var values = [];
|
||||||
|
|
||||||
|
if (typeof obj === 'undefined') {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
var objKeys;
|
||||||
|
if (Array.isArray(filter)) {
|
||||||
|
objKeys = filter;
|
||||||
|
} else {
|
||||||
|
var keys = Object.keys(obj);
|
||||||
|
objKeys = sort ? keys.sort(sort) : keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0, il = objKeys.length; i < il; ++i) {
|
||||||
|
var key = objKeys[i];
|
||||||
|
|
||||||
|
if (skipNulls &&
|
||||||
|
obj[key] === null) {
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
values = values.concat(internals.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encode, filter));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
values = values.concat(internals.stringify(obj[key], prefix + '[' + key + ']', generateArrayPrefix, strictNullHandling, skipNulls, encode, filter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = function (obj, options) {
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
var delimiter = typeof options.delimiter === 'undefined' ? internals.delimiter : options.delimiter;
|
||||||
|
var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
|
||||||
|
var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : internals.skipNulls;
|
||||||
|
var encode = typeof options.encode === 'boolean' ? options.encode : internals.encode;
|
||||||
|
var sort = typeof options.sort === 'function' ? options.sort : null;
|
||||||
|
var objKeys;
|
||||||
|
var filter;
|
||||||
|
if (typeof options.filter === 'function') {
|
||||||
|
filter = options.filter;
|
||||||
|
obj = filter('', obj);
|
||||||
|
}
|
||||||
|
else if (Array.isArray(options.filter)) {
|
||||||
|
objKeys = filter = options.filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = [];
|
||||||
|
|
||||||
|
if (typeof obj !== 'object' ||
|
||||||
|
obj === null) {
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
var arrayFormat;
|
||||||
|
if (options.arrayFormat in internals.arrayPrefixGenerators) {
|
||||||
|
arrayFormat = options.arrayFormat;
|
||||||
|
}
|
||||||
|
else if ('indices' in options) {
|
||||||
|
arrayFormat = options.indices ? 'indices' : 'repeat';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
arrayFormat = 'indices';
|
||||||
|
}
|
||||||
|
|
||||||
|
var generateArrayPrefix = internals.arrayPrefixGenerators[arrayFormat];
|
||||||
|
|
||||||
|
if (!objKeys) {
|
||||||
|
objKeys = Object.keys(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sort) {
|
||||||
|
objKeys.sort(sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0, il = objKeys.length; i < il; ++i) {
|
||||||
|
var key = objKeys[i];
|
||||||
|
|
||||||
|
if (skipNulls &&
|
||||||
|
obj[key] === null) {
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
keys = keys.concat(internals.stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort));
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys.join(delimiter);
|
||||||
|
};
|
||||||
@ -0,0 +1,190 @@
|
|||||||
|
// Load modules
|
||||||
|
|
||||||
|
|
||||||
|
// Declare internals
|
||||||
|
|
||||||
|
var internals = {};
|
||||||
|
internals.hexTable = new Array(256);
|
||||||
|
for (var h = 0; h < 256; ++h) {
|
||||||
|
internals.hexTable[h] = '%' + ((h < 16 ? '0' : '') + h.toString(16)).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
exports.arrayToObject = function (source, options) {
|
||||||
|
|
||||||
|
var obj = options.plainObjects ? Object.create(null) : {};
|
||||||
|
for (var i = 0, il = source.length; i < il; ++i) {
|
||||||
|
if (typeof source[i] !== 'undefined') {
|
||||||
|
|
||||||
|
obj[i] = source[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.merge = function (target, source, options) {
|
||||||
|
|
||||||
|
if (!source) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof source !== 'object') {
|
||||||
|
if (Array.isArray(target)) {
|
||||||
|
target.push(source);
|
||||||
|
}
|
||||||
|
else if (typeof target === 'object') {
|
||||||
|
target[source] = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
target = [target, source];
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof target !== 'object') {
|
||||||
|
target = [target].concat(source);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(target) &&
|
||||||
|
!Array.isArray(source)) {
|
||||||
|
|
||||||
|
target = exports.arrayToObject(target, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = Object.keys(source);
|
||||||
|
for (var k = 0, kl = keys.length; k < kl; ++k) {
|
||||||
|
var key = keys[k];
|
||||||
|
var value = source[key];
|
||||||
|
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(target, key)) {
|
||||||
|
target[key] = value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
target[key] = exports.merge(target[key], value, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.decode = function (str) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(str.replace(/\+/g, ' '));
|
||||||
|
} catch (e) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.encode = function (str) {
|
||||||
|
|
||||||
|
// This code was originally written by Brian White (mscdex) for the io.js core querystring library.
|
||||||
|
// It has been adapted here for stricter adherence to RFC 3986
|
||||||
|
if (str.length === 0) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof str !== 'string') {
|
||||||
|
str = '' + str;
|
||||||
|
}
|
||||||
|
|
||||||
|
var out = '';
|
||||||
|
for (var i = 0, il = str.length; i < il; ++i) {
|
||||||
|
var c = str.charCodeAt(i);
|
||||||
|
|
||||||
|
if (c === 0x2D || // -
|
||||||
|
c === 0x2E || // .
|
||||||
|
c === 0x5F || // _
|
||||||
|
c === 0x7E || // ~
|
||||||
|
(c >= 0x30 && c <= 0x39) || // 0-9
|
||||||
|
(c >= 0x41 && c <= 0x5A) || // a-z
|
||||||
|
(c >= 0x61 && c <= 0x7A)) { // A-Z
|
||||||
|
|
||||||
|
out += str[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c < 0x80) {
|
||||||
|
out += internals.hexTable[c];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c < 0x800) {
|
||||||
|
out += internals.hexTable[0xC0 | (c >> 6)] + internals.hexTable[0x80 | (c & 0x3F)];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c < 0xD800 || c >= 0xE000) {
|
||||||
|
out += internals.hexTable[0xE0 | (c >> 12)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
c = 0x10000 + (((c & 0x3FF) << 10) | (str.charCodeAt(i) & 0x3FF));
|
||||||
|
out += internals.hexTable[0xF0 | (c >> 18)] + internals.hexTable[0x80 | ((c >> 12) & 0x3F)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.compact = function (obj, refs) {
|
||||||
|
|
||||||
|
if (typeof obj !== 'object' ||
|
||||||
|
obj === null) {
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
refs = refs || [];
|
||||||
|
var lookup = refs.indexOf(obj);
|
||||||
|
if (lookup !== -1) {
|
||||||
|
return refs[lookup];
|
||||||
|
}
|
||||||
|
|
||||||
|
refs.push(obj);
|
||||||
|
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
var compacted = [];
|
||||||
|
|
||||||
|
for (var i = 0, il = obj.length; i < il; ++i) {
|
||||||
|
if (typeof obj[i] !== 'undefined') {
|
||||||
|
compacted.push(obj[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return compacted;
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = Object.keys(obj);
|
||||||
|
for (i = 0, il = keys.length; i < il; ++i) {
|
||||||
|
var key = keys[i];
|
||||||
|
obj[key] = exports.compact(obj[key], refs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.isRegExp = function (obj) {
|
||||||
|
|
||||||
|
return Object.prototype.toString.call(obj) === '[object RegExp]';
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.isBuffer = function (obj) {
|
||||||
|
|
||||||
|
if (obj === null ||
|
||||||
|
typeof obj === 'undefined') {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!(obj.constructor &&
|
||||||
|
obj.constructor.isBuffer &&
|
||||||
|
obj.constructor.isBuffer(obj));
|
||||||
|
};
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "qs",
|
||||||
|
"description": "A querystring parser that supports nesting and arrays, with a depth limit",
|
||||||
|
"homepage": "https://github.com/hapijs/qs",
|
||||||
|
"version": "5.2.1",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/hapijs/qs.git"
|
||||||
|
},
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"keywords": [
|
||||||
|
"querystring",
|
||||||
|
"qs"
|
||||||
|
],
|
||||||
|
"engines": ">=0.10.40",
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"browserify": "^10.2.1",
|
||||||
|
"code": "1.x.x",
|
||||||
|
"lab": "5.x.x"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "lab -a code -t 100 -L",
|
||||||
|
"test-tap": "lab -a code -r tap -o tests.tap",
|
||||||
|
"test-cov-html": "lab -a code -r html -o coverage.html",
|
||||||
|
"dist": "browserify --standalone Qs lib/index.js > dist/qs.js"
|
||||||
|
},
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
}
|
||||||
@ -0,0 +1,483 @@
|
|||||||
|
/* eslint no-extend-native:0 */
|
||||||
|
// Load modules
|
||||||
|
|
||||||
|
var Code = require('code');
|
||||||
|
var Lab = require('lab');
|
||||||
|
var Qs = require('../');
|
||||||
|
|
||||||
|
|
||||||
|
// Declare internals
|
||||||
|
|
||||||
|
var internals = {};
|
||||||
|
|
||||||
|
|
||||||
|
// Test shortcuts
|
||||||
|
|
||||||
|
var lab = exports.lab = Lab.script();
|
||||||
|
var expect = Code.expect;
|
||||||
|
var describe = lab.experiment;
|
||||||
|
var it = lab.test;
|
||||||
|
|
||||||
|
|
||||||
|
describe('parse()', function () {
|
||||||
|
|
||||||
|
it('parses a simple string', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('0=foo')).to.deep.equal({ '0': 'foo' });
|
||||||
|
expect(Qs.parse('foo=c++')).to.deep.equal({ foo: 'c ' });
|
||||||
|
expect(Qs.parse('a[>=]=23')).to.deep.equal({ a: { '>=': '23' } });
|
||||||
|
expect(Qs.parse('a[<=>]==23')).to.deep.equal({ a: { '<=>': '=23' } });
|
||||||
|
expect(Qs.parse('a[==]=23')).to.deep.equal({ a: { '==': '23' } });
|
||||||
|
expect(Qs.parse('foo', { strictNullHandling: true })).to.deep.equal({ foo: null });
|
||||||
|
expect(Qs.parse('foo' )).to.deep.equal({ foo: '' });
|
||||||
|
expect(Qs.parse('foo=')).to.deep.equal({ foo: '' });
|
||||||
|
expect(Qs.parse('foo=bar')).to.deep.equal({ foo: 'bar' });
|
||||||
|
expect(Qs.parse(' foo = bar = baz ')).to.deep.equal({ ' foo ': ' bar = baz ' });
|
||||||
|
expect(Qs.parse('foo=bar=baz')).to.deep.equal({ foo: 'bar=baz' });
|
||||||
|
expect(Qs.parse('foo=bar&bar=baz')).to.deep.equal({ foo: 'bar', bar: 'baz' });
|
||||||
|
expect(Qs.parse('foo2=bar2&baz2=')).to.deep.equal({ foo2: 'bar2', baz2: '' });
|
||||||
|
expect(Qs.parse('foo=bar&baz', { strictNullHandling: true })).to.deep.equal({ foo: 'bar', baz: null });
|
||||||
|
expect(Qs.parse('foo=bar&baz')).to.deep.equal({ foo: 'bar', baz: '' });
|
||||||
|
expect(Qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World')).to.deep.equal({
|
||||||
|
cht: 'p3',
|
||||||
|
chd: 't:60,40',
|
||||||
|
chs: '250x100',
|
||||||
|
chl: 'Hello|World'
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows enabling dot notation', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a.b=c')).to.deep.equal({ 'a.b': 'c' });
|
||||||
|
expect(Qs.parse('a.b=c', { allowDots: true })).to.deep.equal({ a: { b: 'c' } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses a single nested string', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[b]=c')).to.deep.equal({ a: { b: 'c' } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses a double nested string', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[b][c]=d')).to.deep.equal({ a: { b: { c: 'd' } } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('defaults to a depth of 5', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[b][c][d][e][f][g][h]=i')).to.deep.equal({ a: { b: { c: { d: { e: { f: { '[g][h]': 'i' } } } } } } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('only parses one level when depth = 1', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[b][c]=d', { depth: 1 })).to.deep.equal({ a: { b: { '[c]': 'd' } } });
|
||||||
|
expect(Qs.parse('a[b][c][d]=e', { depth: 1 })).to.deep.equal({ a: { b: { '[c][d]': 'e' } } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses a simple array', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a=b&a=c')).to.deep.equal({ a: ['b', 'c'] });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses an explicit array', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[]=b')).to.deep.equal({ a: ['b'] });
|
||||||
|
expect(Qs.parse('a[]=b&a[]=c')).to.deep.equal({ a: ['b', 'c'] });
|
||||||
|
expect(Qs.parse('a[]=b&a[]=c&a[]=d')).to.deep.equal({ a: ['b', 'c', 'd'] });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses a mix of simple and explicit arrays', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a=b&a[]=c')).to.deep.equal({ a: ['b', 'c'] });
|
||||||
|
expect(Qs.parse('a[]=b&a=c')).to.deep.equal({ a: ['b', 'c'] });
|
||||||
|
expect(Qs.parse('a[0]=b&a=c')).to.deep.equal({ a: ['b', 'c'] });
|
||||||
|
expect(Qs.parse('a=b&a[0]=c')).to.deep.equal({ a: ['b', 'c'] });
|
||||||
|
expect(Qs.parse('a[1]=b&a=c')).to.deep.equal({ a: ['b', 'c'] });
|
||||||
|
expect(Qs.parse('a=b&a[1]=c')).to.deep.equal({ a: ['b', 'c'] });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses a nested array', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[b][]=c&a[b][]=d')).to.deep.equal({ a: { b: ['c', 'd'] } });
|
||||||
|
expect(Qs.parse('a[>=]=25')).to.deep.equal({ a: { '>=': '25' } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows to specify array indices', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[1]=c&a[0]=b&a[2]=d')).to.deep.equal({ a: ['b', 'c', 'd'] });
|
||||||
|
expect(Qs.parse('a[1]=c&a[0]=b')).to.deep.equal({ a: ['b', 'c'] });
|
||||||
|
expect(Qs.parse('a[1]=c')).to.deep.equal({ a: ['c'] });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('limits specific array indices to 20', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[20]=a')).to.deep.equal({ a: ['a'] });
|
||||||
|
expect(Qs.parse('a[21]=a')).to.deep.equal({ a: { '21': 'a' } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports keys that begin with a number', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[12b]=c')).to.deep.equal({ a: { '12b': 'c' } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports encoded = signs', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('he%3Dllo=th%3Dere')).to.deep.equal({ 'he=llo': 'th=ere' });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is ok with url encoded strings', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[b%20c]=d')).to.deep.equal({ a: { 'b c': 'd' } });
|
||||||
|
expect(Qs.parse('a[b]=c%20d')).to.deep.equal({ a: { b: 'c d' } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows brackets in the value', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('pets=["tobi"]')).to.deep.equal({ pets: '["tobi"]' });
|
||||||
|
expect(Qs.parse('operators=[">=", "<="]')).to.deep.equal({ operators: '[">=", "<="]' });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows empty values', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('')).to.deep.equal({});
|
||||||
|
expect(Qs.parse(null)).to.deep.equal({});
|
||||||
|
expect(Qs.parse(undefined)).to.deep.equal({});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('transforms arrays to objects', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('foo[0]=bar&foo[bad]=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } });
|
||||||
|
expect(Qs.parse('foo[bad]=baz&foo[0]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
|
||||||
|
expect(Qs.parse('foo[bad]=baz&foo[]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
|
||||||
|
expect(Qs.parse('foo[]=bar&foo[bad]=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } });
|
||||||
|
expect(Qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar', '1': 'foo' } });
|
||||||
|
expect(Qs.parse('foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb')).to.deep.equal({ foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] });
|
||||||
|
expect(Qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c')).to.deep.equal({ a: { '0': 'b', t: 'u', c: true } });
|
||||||
|
expect(Qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y')).to.deep.equal({ a: { '0': 'b', '1': 'c', x: 'y' } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('transforms arrays to objects (dot notation)', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('foo[0].baz=bar&fool.bad=baz', { allowDots: true })).to.deep.equal({ foo: [{ baz: 'bar' }], fool: { bad: 'baz' } });
|
||||||
|
expect(Qs.parse('foo[0].baz=bar&fool.bad.boo=baz', { allowDots: true })).to.deep.equal({ foo: [{ baz: 'bar' }], fool: { bad: { boo: 'baz' } } });
|
||||||
|
expect(Qs.parse('foo[0][0].baz=bar&fool.bad=baz', { allowDots: true })).to.deep.equal({ foo: [[{ baz: 'bar' }]], fool: { bad: 'baz' } });
|
||||||
|
expect(Qs.parse('foo[0].baz[0]=15&foo[0].bar=2', { allowDots: true })).to.deep.equal({ foo: [{ baz: ['15'], bar: '2' }] });
|
||||||
|
expect(Qs.parse('foo[0].baz[0]=15&foo[0].baz[1]=16&foo[0].bar=2', { allowDots: true })).to.deep.equal({ foo: [{ baz: ['15', '16'], bar: '2' }] });
|
||||||
|
expect(Qs.parse('foo.bad=baz&foo[0]=bar', { allowDots: true })).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
|
||||||
|
expect(Qs.parse('foo.bad=baz&foo[]=bar', { allowDots: true })).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
|
||||||
|
expect(Qs.parse('foo[]=bar&foo.bad=baz', { allowDots: true })).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } });
|
||||||
|
expect(Qs.parse('foo.bad=baz&foo[]=bar&foo[]=foo', { allowDots: true })).to.deep.equal({ foo: { bad: 'baz', '0': 'bar', '1': 'foo' } });
|
||||||
|
expect(Qs.parse('foo[0].a=a&foo[0].b=b&foo[1].a=aa&foo[1].b=bb', { allowDots: true })).to.deep.equal({ foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can add keys to objects', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[b]=c&a=d')).to.deep.equal({ a: { b: 'c', d: true } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly prunes undefined values when converting an array to an object', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[2]=b&a[99999999]=c')).to.deep.equal({ a: { '2': 'b', '99999999': 'c' } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports malformed uri characters', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('{%:%}', { strictNullHandling: true })).to.deep.equal({ '{%:%}': null });
|
||||||
|
expect(Qs.parse('{%:%}=')).to.deep.equal({ '{%:%}': '' });
|
||||||
|
expect(Qs.parse('foo=%:%}')).to.deep.equal({ foo: '%:%}' });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('doesn\'t produce empty keys', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('_r=1&')).to.deep.equal({ '_r': '1' });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cannot access Object prototype', function (done) {
|
||||||
|
|
||||||
|
Qs.parse('constructor[prototype][bad]=bad');
|
||||||
|
Qs.parse('bad[constructor][prototype][bad]=bad');
|
||||||
|
expect(typeof Object.prototype.bad).to.equal('undefined');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses arrays of objects', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[][b]=c')).to.deep.equal({ a: [{ b: 'c' }] });
|
||||||
|
expect(Qs.parse('a[0][b]=c')).to.deep.equal({ a: [{ b: 'c' }] });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows for empty strings in arrays', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[]=b&a[]=&a[]=c')).to.deep.equal({ a: ['b', '', 'c'] });
|
||||||
|
|
||||||
|
expect(Qs.parse('a[0]=b&a[1]&a[2]=c&a[19]=', { strictNullHandling: true, arrayLimit: 20 })).to.deep.equal({ a: ['b', null, 'c', ''] });
|
||||||
|
expect(Qs.parse('a[]=b&a[]&a[]=c&a[]=', { strictNullHandling: true, arrayLimit: 0 })).to.deep.equal({ a: ['b', null, 'c', ''] });
|
||||||
|
|
||||||
|
expect(Qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]', { strictNullHandling: true, arrayLimit: 20 })).to.deep.equal({ a: ['b', '', 'c', null] });
|
||||||
|
expect(Qs.parse('a[]=b&a[]=&a[]=c&a[]', { strictNullHandling: true, arrayLimit: 0 })).to.deep.equal({ a: ['b', '', 'c', null] });
|
||||||
|
|
||||||
|
expect(Qs.parse('a[]=&a[]=b&a[]=c')).to.deep.equal({ a: ['', 'b', 'c'] });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('compacts sparse arrays', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[10]=1&a[2]=2')).to.deep.equal({ a: ['2', '1'] });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses semi-parsed strings', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse({ 'a[b]': 'c' })).to.deep.equal({ a: { b: 'c' } });
|
||||||
|
expect(Qs.parse({ 'a[b]': 'c', 'a[d]': 'e' })).to.deep.equal({ a: { b: 'c', d: 'e' } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses buffers correctly', function (done) {
|
||||||
|
|
||||||
|
var b = new Buffer('test');
|
||||||
|
expect(Qs.parse({ a: b })).to.deep.equal({ a: b });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('continues parsing when no parent is found', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('[]=&a=b')).to.deep.equal({ '0': '', a: 'b' });
|
||||||
|
expect(Qs.parse('[]&a=b', { strictNullHandling: true })).to.deep.equal({ '0': null, a: 'b' });
|
||||||
|
expect(Qs.parse('[foo]=bar')).to.deep.equal({ foo: 'bar' });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not error when parsing a very long array', function (done) {
|
||||||
|
|
||||||
|
var str = 'a[]=a';
|
||||||
|
while (Buffer.byteLength(str) < 128 * 1024) {
|
||||||
|
str += '&' + str;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(function () {
|
||||||
|
|
||||||
|
Qs.parse(str);
|
||||||
|
}).to.not.throw();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not throw when a native prototype has an enumerable property', { parallel: false }, function (done) {
|
||||||
|
|
||||||
|
Object.prototype.crash = '';
|
||||||
|
Array.prototype.crash = '';
|
||||||
|
expect(Qs.parse.bind(null, 'a=b')).to.not.throw();
|
||||||
|
expect(Qs.parse('a=b')).to.deep.equal({ a: 'b' });
|
||||||
|
expect(Qs.parse.bind(null, 'a[][b]=c')).to.not.throw();
|
||||||
|
expect(Qs.parse('a[][b]=c')).to.deep.equal({ a: [{ b: 'c' }] });
|
||||||
|
delete Object.prototype.crash;
|
||||||
|
delete Array.prototype.crash;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses a string with an alternative string delimiter', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a=b;c=d', { delimiter: ';' })).to.deep.equal({ a: 'b', c: 'd' });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses a string with an alternative RegExp delimiter', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a=b; c=d', { delimiter: /[;,] */ })).to.deep.equal({ a: 'b', c: 'd' });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not use non-splittable objects as delimiters', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a=b&c=d', { delimiter: true })).to.deep.equal({ a: 'b', c: 'd' });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows overriding parameter limit', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a=b&c=d', { parameterLimit: 1 })).to.deep.equal({ a: 'b' });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows setting the parameter limit to Infinity', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a=b&c=d', { parameterLimit: Infinity })).to.deep.equal({ a: 'b', c: 'd' });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows overriding array limit', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[0]=b', { arrayLimit: -1 })).to.deep.equal({ a: { '0': 'b' } });
|
||||||
|
expect(Qs.parse('a[-1]=b', { arrayLimit: -1 })).to.deep.equal({ a: { '-1': 'b' } });
|
||||||
|
expect(Qs.parse('a[0]=b&a[1]=c', { arrayLimit: 0 })).to.deep.equal({ a: { '0': 'b', '1': 'c' } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows disabling array parsing', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[0]=b&a[1]=c', { parseArrays: false })).to.deep.equal({ a: { '0': 'b', '1': 'c' } });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses an object', function (done) {
|
||||||
|
|
||||||
|
var input = {
|
||||||
|
'user[name]': { 'pop[bob]': 3 },
|
||||||
|
'user[email]': null
|
||||||
|
};
|
||||||
|
|
||||||
|
var expected = {
|
||||||
|
'user': {
|
||||||
|
'name': { 'pop[bob]': 3 },
|
||||||
|
'email': null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = Qs.parse(input);
|
||||||
|
|
||||||
|
expect(result).to.deep.equal(expected);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses an object in dot notation', function (done) {
|
||||||
|
|
||||||
|
var input = {
|
||||||
|
'user.name': { 'pop[bob]': 3 },
|
||||||
|
'user.email.': null
|
||||||
|
};
|
||||||
|
|
||||||
|
var expected = {
|
||||||
|
'user': {
|
||||||
|
'name': { 'pop[bob]': 3 },
|
||||||
|
'email': null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = Qs.parse(input, { allowDots: true });
|
||||||
|
|
||||||
|
expect(result).to.deep.equal(expected);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses an object and not child values', function (done) {
|
||||||
|
|
||||||
|
var input = {
|
||||||
|
'user[name]': { 'pop[bob]': { 'test': 3 } },
|
||||||
|
'user[email]': null
|
||||||
|
};
|
||||||
|
|
||||||
|
var expected = {
|
||||||
|
'user': {
|
||||||
|
'name': { 'pop[bob]': { 'test': 3 } },
|
||||||
|
'email': null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = Qs.parse(input);
|
||||||
|
|
||||||
|
expect(result).to.deep.equal(expected);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not blow up when Buffer global is missing', function (done) {
|
||||||
|
|
||||||
|
var tempBuffer = global.Buffer;
|
||||||
|
delete global.Buffer;
|
||||||
|
var result = Qs.parse('a=b&c=d');
|
||||||
|
global.Buffer = tempBuffer;
|
||||||
|
expect(result).to.deep.equal({ a: 'b', c: 'd' });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not crash when parsing circular references', function (done) {
|
||||||
|
|
||||||
|
var a = {};
|
||||||
|
a.b = a;
|
||||||
|
|
||||||
|
var parsed;
|
||||||
|
|
||||||
|
expect(function () {
|
||||||
|
|
||||||
|
parsed = Qs.parse({ 'foo[bar]': 'baz', 'foo[baz]': a });
|
||||||
|
}).to.not.throw();
|
||||||
|
|
||||||
|
expect(parsed).to.contain('foo');
|
||||||
|
expect(parsed.foo).to.contain('bar', 'baz');
|
||||||
|
expect(parsed.foo.bar).to.equal('baz');
|
||||||
|
expect(parsed.foo.baz).to.deep.equal(a);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses plain objects correctly', function (done) {
|
||||||
|
|
||||||
|
var a = Object.create(null);
|
||||||
|
a.b = 'c';
|
||||||
|
|
||||||
|
expect(Qs.parse(a)).to.deep.equal({ b: 'c' });
|
||||||
|
var result = Qs.parse({ a: a });
|
||||||
|
expect(result).to.contain('a');
|
||||||
|
expect(result.a).to.deep.equal(a);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses dates correctly', function (done) {
|
||||||
|
|
||||||
|
var now = new Date();
|
||||||
|
expect(Qs.parse({ a: now })).to.deep.equal({ a: now });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses regular expressions correctly', function (done) {
|
||||||
|
|
||||||
|
var re = /^test$/;
|
||||||
|
expect(Qs.parse({ a: re })).to.deep.equal({ a: re });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can allow overwriting prototype properties', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true })).to.deep.equal({ a: { hasOwnProperty: 'b' } }, { prototype: false });
|
||||||
|
expect(Qs.parse('hasOwnProperty=b', { allowPrototypes: true })).to.deep.equal({ hasOwnProperty: 'b' }, { prototype: false });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can return plain objects', function (done) {
|
||||||
|
|
||||||
|
var expected = Object.create(null);
|
||||||
|
expected.a = Object.create(null);
|
||||||
|
expected.a.b = 'c';
|
||||||
|
expected.a.hasOwnProperty = 'd';
|
||||||
|
expect(Qs.parse('a[b]=c&a[hasOwnProperty]=d', { plainObjects: true })).to.deep.equal(expected);
|
||||||
|
expect(Qs.parse(null, { plainObjects: true })).to.deep.equal(Object.create(null));
|
||||||
|
var expectedArray = Object.create(null);
|
||||||
|
expectedArray.a = Object.create(null);
|
||||||
|
expectedArray.a['0'] = 'b';
|
||||||
|
expectedArray.a.c = 'd';
|
||||||
|
expect(Qs.parse('a[]=b&a[c]=d', { plainObjects: true })).to.deep.equal(expectedArray);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,293 @@
|
|||||||
|
/* eslint no-extend-native:0 */
|
||||||
|
// Load modules
|
||||||
|
|
||||||
|
var Code = require('code');
|
||||||
|
var Lab = require('lab');
|
||||||
|
var Qs = require('../');
|
||||||
|
|
||||||
|
|
||||||
|
// Declare internals
|
||||||
|
|
||||||
|
var internals = {};
|
||||||
|
|
||||||
|
|
||||||
|
// Test shortcuts
|
||||||
|
|
||||||
|
var lab = exports.lab = Lab.script();
|
||||||
|
var expect = Code.expect;
|
||||||
|
var describe = lab.experiment;
|
||||||
|
var it = lab.test;
|
||||||
|
|
||||||
|
|
||||||
|
describe('stringify()', function () {
|
||||||
|
|
||||||
|
it('stringifies a querystring object', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: 'b' })).to.equal('a=b');
|
||||||
|
expect(Qs.stringify({ a: 1 })).to.equal('a=1');
|
||||||
|
expect(Qs.stringify({ a: 1, b: 2 })).to.equal('a=1&b=2');
|
||||||
|
expect(Qs.stringify({ a: 'A_Z' })).to.equal('a=A_Z');
|
||||||
|
expect(Qs.stringify({ a: '€' })).to.equal('a=%E2%82%AC');
|
||||||
|
expect(Qs.stringify({ a: '' })).to.equal('a=%EE%80%80');
|
||||||
|
expect(Qs.stringify({ a: 'א' })).to.equal('a=%D7%90');
|
||||||
|
expect(Qs.stringify({ a: '𐐷' })).to.equal('a=%F0%90%90%B7');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies a nested object', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c');
|
||||||
|
expect(Qs.stringify({ a: { b: { c: { d: 'e' } } } })).to.equal('a%5Bb%5D%5Bc%5D%5Bd%5D=e');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies an array value', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: ['b', 'c', 'd'] })).to.equal('a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('omits nulls when asked', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: 'b', c: null }, { skipNulls: true })).to.equal('a=b');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('omits nested nulls when asked', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: { b: 'c', d: null } }, { skipNulls: true })).to.equal('a%5Bb%5D=c');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('omits array indices when asked', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false })).to.equal('a=b&a=c&a=d');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies a nested array value', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: { b: ['c', 'd'] } })).to.equal('a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies an object inside an array', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: [{ b: 'c' }] })).to.equal('a%5B0%5D%5Bb%5D=c');
|
||||||
|
expect(Qs.stringify({ a: [{ b: { c: [1] } }] })).to.equal('a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not omit object keys when indices = false', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: [{ b: 'c' }] }, { indices: false })).to.equal('a%5Bb%5D=c');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses indices notation for arrays when indices=true', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: ['b', 'c'] }, { indices: true })).to.equal('a%5B0%5D=b&a%5B1%5D=c');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses indices notation for arrays when no arrayFormat is specified', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: ['b', 'c'] })).to.equal('a%5B0%5D=b&a%5B1%5D=c');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses indices notation for arrays when no arrayFormat=indices', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })).to.equal('a%5B0%5D=b&a%5B1%5D=c');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses repeat notation for arrays when no arrayFormat=repeat', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })).to.equal('a=b&a=c');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses brackets notation for arrays when no arrayFormat=brackets', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })).to.equal('a%5B%5D=b&a%5B%5D=c');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies a complicated object', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: { b: 'c', d: 'e' } })).to.equal('a%5Bb%5D=c&a%5Bd%5D=e');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies an empty value', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: '' })).to.equal('a=');
|
||||||
|
expect(Qs.stringify({ a: null }, { strictNullHandling: true })).to.equal('a');
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: '', b: '' })).to.equal('a=&b=');
|
||||||
|
expect(Qs.stringify({ a: null, b: '' }, { strictNullHandling: true })).to.equal('a&b=');
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: { b: '' } })).to.equal('a%5Bb%5D=');
|
||||||
|
expect(Qs.stringify({ a: { b: null } }, { strictNullHandling: true })).to.equal('a%5Bb%5D');
|
||||||
|
expect(Qs.stringify({ a: { b: null } }, { strictNullHandling: false })).to.equal('a%5Bb%5D=');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies an empty object', function (done) {
|
||||||
|
|
||||||
|
var obj = Object.create(null);
|
||||||
|
obj.a = 'b';
|
||||||
|
expect(Qs.stringify(obj)).to.equal('a=b');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an empty string for invalid input', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify(undefined)).to.equal('');
|
||||||
|
expect(Qs.stringify(false)).to.equal('');
|
||||||
|
expect(Qs.stringify(null)).to.equal('');
|
||||||
|
expect(Qs.stringify('')).to.equal('');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies an object with an empty object as a child', function (done) {
|
||||||
|
|
||||||
|
var obj = {
|
||||||
|
a: Object.create(null)
|
||||||
|
};
|
||||||
|
|
||||||
|
obj.a.b = 'c';
|
||||||
|
expect(Qs.stringify(obj)).to.equal('a%5Bb%5D=c');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('drops keys with a value of undefined', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: undefined })).to.equal('');
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: true })).to.equal('a%5Bc%5D');
|
||||||
|
expect(Qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: false })).to.equal('a%5Bc%5D=');
|
||||||
|
expect(Qs.stringify({ a: { b: undefined, c: '' } })).to.equal('a%5Bc%5D=');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('url encodes values', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: 'b c' })).to.equal('a=b%20c');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies a date', function (done) {
|
||||||
|
|
||||||
|
var now = new Date();
|
||||||
|
var str = 'a=' + encodeURIComponent(now.toISOString());
|
||||||
|
expect(Qs.stringify({ a: now })).to.equal(str);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies the weird object from qs', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ 'my weird field': '~q1!2"\'w$5&7/z8)?' })).to.equal('my%20weird%20field=~q1%212%22%27w%245%267%2Fz8%29%3F');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skips properties that are part of the object prototype', function (done) {
|
||||||
|
|
||||||
|
Object.prototype.crash = 'test';
|
||||||
|
expect(Qs.stringify({ a: 'b' })).to.equal('a=b');
|
||||||
|
expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c');
|
||||||
|
delete Object.prototype.crash;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies boolean values', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: true })).to.equal('a=true');
|
||||||
|
expect(Qs.stringify({ a: { b: true } })).to.equal('a%5Bb%5D=true');
|
||||||
|
expect(Qs.stringify({ b: false })).to.equal('b=false');
|
||||||
|
expect(Qs.stringify({ b: { c: false } })).to.equal('b%5Bc%5D=false');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies buffer values', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: new Buffer('test') })).to.equal('a=test');
|
||||||
|
expect(Qs.stringify({ a: { b: new Buffer('test') } })).to.equal('a%5Bb%5D=test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies an object using an alternative delimiter', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' })).to.equal('a=b;c=d');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('doesn\'t blow up when Buffer global is missing', function (done) {
|
||||||
|
|
||||||
|
var tempBuffer = global.Buffer;
|
||||||
|
delete global.Buffer;
|
||||||
|
var result = Qs.stringify({ a: 'b', c: 'd' });
|
||||||
|
global.Buffer = tempBuffer;
|
||||||
|
expect(result).to.equal('a=b&c=d');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('selects properties when filter=array', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: 'b' }, { filter: ['a'] })).to.equal('a=b');
|
||||||
|
expect(Qs.stringify({ a: 1 }, { filter: [] })).to.equal('');
|
||||||
|
expect(Qs.stringify({ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, { filter: ['a', 'b', 0, 2] })).to.equal('a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3');
|
||||||
|
done();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports custom representations when filter=function', function (done) {
|
||||||
|
|
||||||
|
var calls = 0;
|
||||||
|
var obj = { a: 'b', c: 'd', e: { f: new Date(1257894000000) } };
|
||||||
|
var filterFunc = function (prefix, value) {
|
||||||
|
|
||||||
|
calls++;
|
||||||
|
if (calls === 1) {
|
||||||
|
expect(prefix).to.be.empty();
|
||||||
|
expect(value).to.equal(obj);
|
||||||
|
}
|
||||||
|
else if (prefix === 'c') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (value instanceof Date) {
|
||||||
|
expect(prefix).to.equal('e[f]');
|
||||||
|
return value.getTime();
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(Qs.stringify(obj, { filter: filterFunc })).to.equal('a=b&e%5Bf%5D=1257894000000');
|
||||||
|
expect(calls).to.equal(5);
|
||||||
|
done();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can disable uri encoding', function (done) {
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: 'b' }, { encode: false })).to.equal('a=b');
|
||||||
|
expect(Qs.stringify({ a: { b: 'c' } }, { encode: false })).to.equal('a[b]=c');
|
||||||
|
expect(Qs.stringify({ a: 'b', c: null }, { strictNullHandling: true, encode: false })).to.equal('a=b&c');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can sort the keys', function (done) {
|
||||||
|
|
||||||
|
var sort = function alphabeticalSort (a, b) {
|
||||||
|
|
||||||
|
return a.localeCompare(b);
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(Qs.stringify({ a: 'c', z: 'y', b : 'f' }, { sort : sort })).to.equal('a=c&b=f&z=y');
|
||||||
|
expect(Qs.stringify({ a: 'c', z: { j: 'a', i:'b' }, b : 'f' }, { sort : sort })).to.equal('a=c&b=f&z%5Bi%5D=b&z%5Bj%5D=a');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
// Load modules
|
||||||
|
|
||||||
|
var Code = require('code');
|
||||||
|
var Lab = require('lab');
|
||||||
|
var Utils = require('../lib/utils');
|
||||||
|
|
||||||
|
|
||||||
|
// Declare internals
|
||||||
|
|
||||||
|
var internals = {};
|
||||||
|
|
||||||
|
|
||||||
|
// Test shortcuts
|
||||||
|
|
||||||
|
var lab = exports.lab = Lab.script();
|
||||||
|
var expect = Code.expect;
|
||||||
|
var describe = lab.experiment;
|
||||||
|
var it = lab.test;
|
||||||
|
|
||||||
|
|
||||||
|
describe('merge()', function () {
|
||||||
|
|
||||||
|
it('can merge two objects with the same key', function (done) {
|
||||||
|
|
||||||
|
expect(Utils.merge({ a: 'b' }, { a: 'c' })).to.deep.equal({ a: ['b', 'c'] });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
Copyright (c) 2012-2018 Zongmin Lei(雷宗民) <leizongmin@gmail.com>
|
||||||
|
http://ucdok.com
|
||||||
|
|
||||||
|
The MIT License
|
||||||
|
|
||||||
|
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.
|
||||||
@ -0,0 +1,508 @@
|
|||||||
|
[![NPM version][npm-image]][npm-url]
|
||||||
|
[](https://github.com/leizongmin/js-xss/actions/workflows/nodejs.yml)
|
||||||
|
[![Test coverage][coveralls-image]][coveralls-url]
|
||||||
|
[![David deps][david-image]][david-url]
|
||||||
|
[![node version][node-image]][node-url]
|
||||||
|
[![npm download][download-image]][download-url]
|
||||||
|
[![npm license][license-image]][download-url]
|
||||||
|
|
||||||
|
[npm-image]: https://img.shields.io/npm/v/xss.svg?style=flat-square
|
||||||
|
[npm-url]: https://npmjs.org/package/xss
|
||||||
|
[coveralls-image]: https://img.shields.io/coveralls/leizongmin/js-xss.svg?style=flat-square
|
||||||
|
[coveralls-url]: https://coveralls.io/r/leizongmin/js-xss?branch=master
|
||||||
|
[david-image]: https://img.shields.io/david/leizongmin/js-xss.svg?style=flat-square
|
||||||
|
[david-url]: https://david-dm.org/leizongmin/js-xss
|
||||||
|
[node-image]: https://img.shields.io/badge/node.js-%3E=_0.10-green.svg?style=flat-square
|
||||||
|
[node-url]: http://nodejs.org/download/
|
||||||
|
[download-image]: https://img.shields.io/npm/dm/xss.svg?style=flat-square
|
||||||
|
[download-url]: https://npmjs.org/package/xss
|
||||||
|
[license-image]: https://img.shields.io/npm/l/xss.svg
|
||||||
|
|
||||||
|
# Sanitize untrusted HTML (to prevent XSS) with a configuration specified by a Whitelist.
|
||||||
|
|
||||||
|
[](https://greenkeeper.io/)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`xss` is a module used to filter input from users to prevent XSS attacks.
|
||||||
|
([What is XSS attack?](http://en.wikipedia.org/wiki/Cross-site_scripting))
|
||||||
|
|
||||||
|
**Project Homepage:** http://jsxss.com
|
||||||
|
|
||||||
|
**Try Online:** http://jsxss.com/en/try.html
|
||||||
|
|
||||||
|
**[中文版文档](https://github.com/leizongmin/js-xss/blob/master/README.zh.md)**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Specifies HTML tags and their attributes allowed with whitelist
|
||||||
|
- Handle any tags or attributes using custom function.
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
- [XSS Filter Evasion Cheat Sheet](https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet)
|
||||||
|
- [Data URI scheme](http://en.wikipedia.org/wiki/Data_URI_scheme)
|
||||||
|
- [XSS with Data URI Scheme](http://hi.baidu.com/badzzzz/item/bdbafe83144619c199255f7b)
|
||||||
|
|
||||||
|
## Benchmark (for references only)
|
||||||
|
|
||||||
|
- the xss module: 22.53 MB/s
|
||||||
|
- `xss()` function from module `validator@0.3.7`: 6.9 MB/s
|
||||||
|
|
||||||
|
For test code please refer to `benchmark` directory.
|
||||||
|
|
||||||
|
## They are using xss module
|
||||||
|
|
||||||
|
- **nodeclub** - A Node.js bbs using MongoDB - https://github.com/cnodejs/nodeclub
|
||||||
|
- **cnpmjs.org** - Private npm registry and web for Enterprise - https://github.com/cnpm/cnpmjs.org
|
||||||
|
- **cocalc.com** - Collaborative Calculation and Data Science - https://cocalc.com
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
### NPM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install xss
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bower
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bower install xss
|
||||||
|
```
|
||||||
|
|
||||||
|
Or
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bower install https://github.com/leizongmin/js-xss.git
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usages
|
||||||
|
|
||||||
|
### On Node.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var xss = require("xss");
|
||||||
|
var html = xss('<script>alert("xss");</script>');
|
||||||
|
console.log(html);
|
||||||
|
```
|
||||||
|
|
||||||
|
### On Browser
|
||||||
|
|
||||||
|
Shim mode (reference file `test/test.html`):
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="https://rawgit.com/leizongmin/js-xss/master/dist/xss.js"></script>
|
||||||
|
<script>
|
||||||
|
// apply function filterXSS in the same way
|
||||||
|
var html = filterXSS('<script>alert("xss");</scr' + "ipt>");
|
||||||
|
alert(html);
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
AMD mode - shim:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
require.config({
|
||||||
|
baseUrl: "./",
|
||||||
|
paths: {
|
||||||
|
xss: "https://rawgit.com/leizongmin/js-xss/master/dist/xss.js",
|
||||||
|
},
|
||||||
|
shim: {
|
||||||
|
xss: { exports: "filterXSS" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
require(["xss"], function (xss) {
|
||||||
|
var html = xss('<script>alert("xss");</scr' + "ipt>");
|
||||||
|
alert(html);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Notes: please don't use the URL https://rawgit.com/leizongmin/js-xss/master/dist/xss.js in production environment.**
|
||||||
|
|
||||||
|
## Command Line Tool
|
||||||
|
|
||||||
|
### Process File
|
||||||
|
|
||||||
|
You can use the xss command line tool to process a file. Usage:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xss -i <input_file> -o <output_file>
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xss -i origin.html -o target.html
|
||||||
|
```
|
||||||
|
|
||||||
|
### Active Test
|
||||||
|
|
||||||
|
Run the following command, them you can type HTML
|
||||||
|
code in the command-line, and check the filtered output:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xss -t
|
||||||
|
```
|
||||||
|
|
||||||
|
For more details, please run `$ xss -h` to see it.
|
||||||
|
|
||||||
|
## Custom filter rules
|
||||||
|
|
||||||
|
When using the `xss()` function, the second parameter could be used to specify
|
||||||
|
custom rules:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
options = {}; // Custom rules
|
||||||
|
html = xss('<script>alert("xss");</script>', options);
|
||||||
|
```
|
||||||
|
|
||||||
|
To avoid passing `options` every time, you can also do it in a faster way by
|
||||||
|
creating a `FilterXSS` instance:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
options = {}; // Custom rules
|
||||||
|
myxss = new xss.FilterXSS(options);
|
||||||
|
// then apply myxss.process()
|
||||||
|
html = myxss.process('<script>alert("xss");</script>');
|
||||||
|
```
|
||||||
|
|
||||||
|
Details of parameters in `options` would be described below.
|
||||||
|
|
||||||
|
### Whitelist
|
||||||
|
|
||||||
|
By specifying a `whiteList`, e.g. `{ 'tagName': [ 'attr-1', 'attr-2' ] }`. Tags
|
||||||
|
and attributes not in the whitelist would be filter out. For example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// only tag a and its attributes href, title, target are allowed
|
||||||
|
var options = {
|
||||||
|
whiteList: {
|
||||||
|
a: ["href", "title", "target"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// With the configuration specified above, the following HTML:
|
||||||
|
// <a href="#" onclick="hello()"><i>Hello</i></a>
|
||||||
|
// would become:
|
||||||
|
// <a href="#"><i>Hello</i></a>
|
||||||
|
```
|
||||||
|
|
||||||
|
For the default whitelist, please refer `xss.whiteList`.
|
||||||
|
|
||||||
|
`allowList` is also supported, and has the same function as `whiteList`.
|
||||||
|
|
||||||
|
### Customize the handler function for matched tags
|
||||||
|
|
||||||
|
By specifying the handler function with `onTag`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function onTag(tag, html, options) {
|
||||||
|
// tag is the name of current tag, e.g. 'a' for tag <a>
|
||||||
|
// html is the HTML of this tag, e.g. '<a>' for tag <a>
|
||||||
|
// options is some addition informations:
|
||||||
|
// isWhite boolean, whether the tag is in whitelist
|
||||||
|
// isClosing boolean, whether the tag is a closing tag, e.g. true for </a>
|
||||||
|
// position integer, the position of the tag in output result
|
||||||
|
// sourcePosition integer, the position of the tag in input HTML source
|
||||||
|
// If a string is returned, the current tag would be replaced with the string
|
||||||
|
// If return nothing, the default measure would be taken:
|
||||||
|
// If in whitelist: filter attributes using onTagAttr, as described below
|
||||||
|
// If not in whitelist: handle by onIgnoreTag, as described below
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize the handler function for attributes of matched tags
|
||||||
|
|
||||||
|
By specifying the handler function with `onTagAttr`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function onTagAttr(tag, name, value, isWhiteAttr) {
|
||||||
|
// tag is the name of current tag, e.g. 'a' for tag <a>
|
||||||
|
// name is the name of current attribute, e.g. 'href' for href="#"
|
||||||
|
// isWhiteAttr whether the attribute is in whitelist
|
||||||
|
// If a string is returned, the attribute would be replaced with the string
|
||||||
|
// If return nothing, the default measure would be taken:
|
||||||
|
// If in whitelist: filter the value using safeAttrValue as described below
|
||||||
|
// If not in whitelist: handle by onIgnoreTagAttr, as described below
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize the handler function for tags not in the whitelist
|
||||||
|
|
||||||
|
By specifying the handler function with `onIgnoreTag`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function onIgnoreTag(tag, html, options) {
|
||||||
|
// Parameters are the same with onTag
|
||||||
|
// If a string is returned, the tag would be replaced with the string
|
||||||
|
// If return nothing, the default measure would be taken (specifies using
|
||||||
|
// escape, as described below)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize the handler function for attributes not in the whitelist
|
||||||
|
|
||||||
|
By specifying the handler function with `onIgnoreTagAttr`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function onIgnoreTagAttr(tag, name, value, isWhiteAttr) {
|
||||||
|
// Parameters are the same with onTagAttr
|
||||||
|
// If a string is returned, the value would be replaced with this string
|
||||||
|
// If return nothing, then keep default (remove the attribute)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize escaping function for HTML
|
||||||
|
|
||||||
|
By specifying the handler function with `escapeHtml`. Following is the default
|
||||||
|
function **(Modification is not recommended)**:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function escapeHtml(html) {
|
||||||
|
return html.replace(/</g, "<").replace(/>/g, ">");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize escaping function for value of attributes
|
||||||
|
|
||||||
|
By specifying the handler function with `safeAttrValue`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function safeAttrValue(tag, name, value) {
|
||||||
|
// Parameters are the same with onTagAttr (without options)
|
||||||
|
// Return the value as a string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize CSS filter
|
||||||
|
|
||||||
|
If you allow the attribute `style`, the value will be processed by [cssfilter](https://github.com/leizongmin/js-css-filter) module. The cssfilter module includes a default css whitelist. You can specify the options for cssfilter module like this:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
myxss = new xss.FilterXSS({
|
||||||
|
css: {
|
||||||
|
whiteList: {
|
||||||
|
position: /^fixed|relative$/,
|
||||||
|
top: true,
|
||||||
|
left: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
html = myxss.process('<script>alert("xss");</script>');
|
||||||
|
```
|
||||||
|
|
||||||
|
If you don't want to filter out the `style` content, just specify `false` to the `css` option:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
myxss = new xss.FilterXSS({
|
||||||
|
css: false,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
For more help, please see https://github.com/leizongmin/js-css-filter
|
||||||
|
|
||||||
|
### Quick Start
|
||||||
|
|
||||||
|
#### Filter out tags not in the whitelist
|
||||||
|
|
||||||
|
By using `stripIgnoreTag` parameter:
|
||||||
|
|
||||||
|
- `true` filter out tags not in the whitelist
|
||||||
|
- `false`: by default: escape the tag using configured `escape` function
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
If `stripIgnoreTag = true` is set, the following code:
|
||||||
|
|
||||||
|
```html
|
||||||
|
code:
|
||||||
|
<script>
|
||||||
|
alert(/xss/);
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
would output filtered:
|
||||||
|
|
||||||
|
```html
|
||||||
|
code:alert(/xss/);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Filter out tags and tag bodies not in the whitelist
|
||||||
|
|
||||||
|
By using `stripIgnoreTagBody` parameter:
|
||||||
|
|
||||||
|
- `false|null|undefined` by default: do nothing
|
||||||
|
- `'*'|true`: filter out all tags not in the whitelist
|
||||||
|
- `['tag1', 'tag2']`: filter out only specified tags not in the whitelist
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
If `stripIgnoreTagBody = ['script']` is set, the following code:
|
||||||
|
|
||||||
|
```html
|
||||||
|
code:
|
||||||
|
<script>
|
||||||
|
alert(/xss/);
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
would output filtered:
|
||||||
|
|
||||||
|
```html
|
||||||
|
code:
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Filter out HTML comments
|
||||||
|
|
||||||
|
By using `allowCommentTag` parameter:
|
||||||
|
|
||||||
|
- `true`: do nothing
|
||||||
|
- `false` by default: filter out HTML comments
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
If `allowCommentTag = false` is set, the following code:
|
||||||
|
|
||||||
|
```html
|
||||||
|
code:<!-- something -->
|
||||||
|
END
|
||||||
|
```
|
||||||
|
|
||||||
|
would output filtered:
|
||||||
|
|
||||||
|
```html
|
||||||
|
code: END
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Allow attributes of whitelist tags start with `data-`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var source = '<div a="1" b="2" data-a="3" data-b="4">hello</div>';
|
||||||
|
var html = xss(source, {
|
||||||
|
onIgnoreTagAttr: function (tag, name, value, isWhiteAttr) {
|
||||||
|
if (name.substr(0, 5) === "data-") {
|
||||||
|
// escape its value using built-in escapeAttrValue function
|
||||||
|
return name + '="' + xss.escapeAttrValue(value) + '"';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("%s\nconvert to:\n%s", source, html);
|
||||||
|
```
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div a="1" b="2" data-a="3" data-b="4">hello</div>
|
||||||
|
convert to:
|
||||||
|
<div data-a="3" data-b="4">hello</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Allow tags start with `x-`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var source = "<x><x-1>he<x-2 checked></x-2>wwww</x-1><a>";
|
||||||
|
var html = xss(source, {
|
||||||
|
onIgnoreTag: function (tag, html, options) {
|
||||||
|
if (tag.substr(0, 2) === "x-") {
|
||||||
|
// do not filter its attributes
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("%s\nconvert to:\n%s", source, html);
|
||||||
|
```
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<x
|
||||||
|
><x-1>he<x-2 checked></x-2>wwww</x-1
|
||||||
|
><a>
|
||||||
|
convert to: <x><x-1>he<x-2 checked></x-2>wwww</x-1><a></a></a
|
||||||
|
></x>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parse images in HTML
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var source =
|
||||||
|
'<img src="img1">a<img src="img2">b<img src="img3">c<img src="img4">d';
|
||||||
|
var list = [];
|
||||||
|
var html = xss(source, {
|
||||||
|
onTagAttr: function (tag, name, value, isWhiteAttr) {
|
||||||
|
if (tag === "img" && name === "src") {
|
||||||
|
// Use the built-in friendlyAttrValue function to escape attribute
|
||||||
|
// values. It supports converting entity tags such as < to printable
|
||||||
|
// characters such as <
|
||||||
|
list.push(xss.friendlyAttrValue(value));
|
||||||
|
}
|
||||||
|
// Return nothing, means keep the default handling measure
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("image list:\n%s", list.join(", "));
|
||||||
|
```
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```html
|
||||||
|
image list: img1, img2, img3, img4
|
||||||
|
```
|
||||||
|
|
||||||
|
### Filter out HTML tags (keeps only plain text)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var source = "<strong>hello</strong><script>alert(/xss/);</script>end";
|
||||||
|
var html = xss(source, {
|
||||||
|
whiteList: {}, // empty, means filter out all tags
|
||||||
|
stripIgnoreTag: true, // filter out all HTML not in the whitelist
|
||||||
|
stripIgnoreTagBody: ["script"], // the script tag is a special case, we need
|
||||||
|
// to filter out its content
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("text: %s", html);
|
||||||
|
```
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```html
|
||||||
|
text: helloend
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
```text
|
||||||
|
Copyright (c) 2012-2018 Zongmin Lei(雷宗民) <leizongmin@gmail.com>
|
||||||
|
http://ucdok.com
|
||||||
|
|
||||||
|
The MIT License
|
||||||
|
|
||||||
|
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.
|
||||||
|
```
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令行工具
|
||||||
|
*
|
||||||
|
* @author Zongmin Lei<leizongmin@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var path = require('path');
|
||||||
|
var program = require('commander');
|
||||||
|
var xss = require('../');
|
||||||
|
var packageInfo = require('../package.json');
|
||||||
|
|
||||||
|
program
|
||||||
|
.version(packageInfo.version)
|
||||||
|
.option('-t, --test', 'active test')
|
||||||
|
.option('-i, --input <input_file>', 'input file name')
|
||||||
|
.option('-o, --output <output_file>', 'output filename')
|
||||||
|
.option('-c, --config <config_file>', 'load custom config')
|
||||||
|
.option('-s, --strip-ignore-tag', 'set stripIgnoreTag=true')
|
||||||
|
.option('-b, --strip-ignore-tag-body', 'set stripIgnoreTagBody=true');
|
||||||
|
|
||||||
|
program.on('--help', function () {
|
||||||
|
console.log(' Examples:');
|
||||||
|
console.log('');
|
||||||
|
console.log(' $ xss -t');
|
||||||
|
console.log(' $ xss -i origin.html');
|
||||||
|
console.log(' $ xss -i origin.html -o targer.html');
|
||||||
|
console.log(' $ xss -i origin.html -c config.js');
|
||||||
|
console.log(' $ xss -i origin.html -s');
|
||||||
|
console.log(' $ xss -i origin.html -s -b');
|
||||||
|
console.log('');
|
||||||
|
console.log(' For more details, please see: https://npmjs.org/package/xss')
|
||||||
|
});
|
||||||
|
|
||||||
|
program.parse(process.argv);
|
||||||
|
|
||||||
|
if (program.test) {
|
||||||
|
require('../lib/cli');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = {};
|
||||||
|
if (program.config) {
|
||||||
|
config = require(path.resolve(program.config));
|
||||||
|
}
|
||||||
|
if (program.input) {
|
||||||
|
var input = fs.readFileSync(program.input, 'utf8');
|
||||||
|
} else {
|
||||||
|
program.help();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (program['strip-ignore-tag']) {
|
||||||
|
config.stripIgnoreTag = true;
|
||||||
|
}
|
||||||
|
if (program['strip-ignore-tag-body']) {
|
||||||
|
config.stripIgnoreTagBody = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var output = xss(input, config);
|
||||||
|
|
||||||
|
if (program.output) {
|
||||||
|
fs.writeFileSync(program.output, output);
|
||||||
|
} else {
|
||||||
|
console.log(output);
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>测试</title>
|
||||||
|
<meta charset="utf8">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="result"></pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
<script src="xss.js"></script>
|
||||||
|
<script>
|
||||||
|
var code = '<script>alert("xss");</' + 'script>';
|
||||||
|
document.querySelector('#result').innerText = code + '\n被转换成了\n' + filterXSS(code);
|
||||||
|
</script>
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* command line tool
|
||||||
|
*
|
||||||
|
* @author Zongmin Lei<leizongmin@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
var xss = require("./");
|
||||||
|
var readline = require("readline");
|
||||||
|
|
||||||
|
var rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Enter a blank line to do xss(), enter "@quit" to exit.\n');
|
||||||
|
|
||||||
|
function take(c, n) {
|
||||||
|
var ret = "";
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
ret += c;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPrompt(line) {
|
||||||
|
line = line.toString();
|
||||||
|
rl.setPrompt("[" + line + "]" + take(" ", 5 - line.length));
|
||||||
|
rl.prompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
setPrompt(1);
|
||||||
|
|
||||||
|
var html = [];
|
||||||
|
rl.on("line", function (line) {
|
||||||
|
if (line === "@quit") return process.exit();
|
||||||
|
if (line === "") {
|
||||||
|
console.log("");
|
||||||
|
console.log(xss(html.join("\r\n")));
|
||||||
|
console.log("");
|
||||||
|
html = [];
|
||||||
|
} else {
|
||||||
|
html.push(line);
|
||||||
|
}
|
||||||
|
setPrompt(html.length + 1);
|
||||||
|
});
|
||||||
@ -0,0 +1,459 @@
|
|||||||
|
/**
|
||||||
|
* default settings
|
||||||
|
*
|
||||||
|
* @author Zongmin Lei<leizongmin@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
var FilterCSS = require("cssfilter").FilterCSS;
|
||||||
|
var getDefaultCSSWhiteList = require("cssfilter").getDefaultWhiteList;
|
||||||
|
var _ = require("./util");
|
||||||
|
|
||||||
|
function getDefaultWhiteList() {
|
||||||
|
return {
|
||||||
|
a: ["target", "href", "title"],
|
||||||
|
abbr: ["title"],
|
||||||
|
address: [],
|
||||||
|
area: ["shape", "coords", "href", "alt"],
|
||||||
|
article: [],
|
||||||
|
aside: [],
|
||||||
|
audio: [
|
||||||
|
"autoplay",
|
||||||
|
"controls",
|
||||||
|
"crossorigin",
|
||||||
|
"loop",
|
||||||
|
"muted",
|
||||||
|
"preload",
|
||||||
|
"src",
|
||||||
|
],
|
||||||
|
b: [],
|
||||||
|
bdi: ["dir"],
|
||||||
|
bdo: ["dir"],
|
||||||
|
big: [],
|
||||||
|
blockquote: ["cite"],
|
||||||
|
br: [],
|
||||||
|
caption: [],
|
||||||
|
center: [],
|
||||||
|
cite: [],
|
||||||
|
code: [],
|
||||||
|
col: ["align", "valign", "span", "width"],
|
||||||
|
colgroup: ["align", "valign", "span", "width"],
|
||||||
|
dd: [],
|
||||||
|
del: ["datetime"],
|
||||||
|
details: ["open"],
|
||||||
|
div: [],
|
||||||
|
dl: [],
|
||||||
|
dt: [],
|
||||||
|
em: [],
|
||||||
|
figcaption: [],
|
||||||
|
figure: [],
|
||||||
|
font: ["color", "size", "face"],
|
||||||
|
footer: [],
|
||||||
|
h1: [],
|
||||||
|
h2: [],
|
||||||
|
h3: [],
|
||||||
|
h4: [],
|
||||||
|
h5: [],
|
||||||
|
h6: [],
|
||||||
|
header: [],
|
||||||
|
hr: [],
|
||||||
|
i: [],
|
||||||
|
img: ["src", "alt", "title", "width", "height"],
|
||||||
|
ins: ["datetime"],
|
||||||
|
li: [],
|
||||||
|
mark: [],
|
||||||
|
nav: [],
|
||||||
|
ol: [],
|
||||||
|
p: [],
|
||||||
|
pre: [],
|
||||||
|
s: [],
|
||||||
|
section: [],
|
||||||
|
small: [],
|
||||||
|
span: [],
|
||||||
|
sub: [],
|
||||||
|
summary: [],
|
||||||
|
sup: [],
|
||||||
|
strong: [],
|
||||||
|
strike: [],
|
||||||
|
table: ["width", "border", "align", "valign"],
|
||||||
|
tbody: ["align", "valign"],
|
||||||
|
td: ["width", "rowspan", "colspan", "align", "valign"],
|
||||||
|
tfoot: ["align", "valign"],
|
||||||
|
th: ["width", "rowspan", "colspan", "align", "valign"],
|
||||||
|
thead: ["align", "valign"],
|
||||||
|
tr: ["rowspan", "align", "valign"],
|
||||||
|
tt: [],
|
||||||
|
u: [],
|
||||||
|
ul: [],
|
||||||
|
video: [
|
||||||
|
"autoplay",
|
||||||
|
"controls",
|
||||||
|
"crossorigin",
|
||||||
|
"loop",
|
||||||
|
"muted",
|
||||||
|
"playsinline",
|
||||||
|
"poster",
|
||||||
|
"preload",
|
||||||
|
"src",
|
||||||
|
"height",
|
||||||
|
"width",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultCSSFilter = new FilterCSS();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* default onTag function
|
||||||
|
*
|
||||||
|
* @param {String} tag
|
||||||
|
* @param {String} html
|
||||||
|
* @param {Object} options
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function onTag(tag, html, options) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* default onIgnoreTag function
|
||||||
|
*
|
||||||
|
* @param {String} tag
|
||||||
|
* @param {String} html
|
||||||
|
* @param {Object} options
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function onIgnoreTag(tag, html, options) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* default onTagAttr function
|
||||||
|
*
|
||||||
|
* @param {String} tag
|
||||||
|
* @param {String} name
|
||||||
|
* @param {String} value
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function onTagAttr(tag, name, value) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* default onIgnoreTagAttr function
|
||||||
|
*
|
||||||
|
* @param {String} tag
|
||||||
|
* @param {String} name
|
||||||
|
* @param {String} value
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function onIgnoreTagAttr(tag, name, value) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* default escapeHtml function
|
||||||
|
*
|
||||||
|
* @param {String} html
|
||||||
|
*/
|
||||||
|
function escapeHtml(html) {
|
||||||
|
return html.replace(REGEXP_LT, "<").replace(REGEXP_GT, ">");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* default safeAttrValue function
|
||||||
|
*
|
||||||
|
* @param {String} tag
|
||||||
|
* @param {String} name
|
||||||
|
* @param {String} value
|
||||||
|
* @param {Object} cssFilter
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function safeAttrValue(tag, name, value, cssFilter) {
|
||||||
|
// unescape attribute value firstly
|
||||||
|
value = friendlyAttrValue(value);
|
||||||
|
|
||||||
|
if (name === "href" || name === "src") {
|
||||||
|
// filter `href` and `src` attribute
|
||||||
|
// only allow the value that starts with `http://` | `https://` | `mailto:` | `/` | `#`
|
||||||
|
value = _.trim(value);
|
||||||
|
if (value === "#") return "#";
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
value.substr(0, 7) === "http://" ||
|
||||||
|
value.substr(0, 8) === "https://" ||
|
||||||
|
value.substr(0, 7) === "mailto:" ||
|
||||||
|
value.substr(0, 4) === "tel:" ||
|
||||||
|
value.substr(0, 11) === "data:image/" ||
|
||||||
|
value.substr(0, 6) === "ftp://" ||
|
||||||
|
value.substr(0, 2) === "./" ||
|
||||||
|
value.substr(0, 3) === "../" ||
|
||||||
|
value[0] === "#" ||
|
||||||
|
value[0] === "/"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
} else if (name === "background") {
|
||||||
|
// filter `background` attribute (maybe no use)
|
||||||
|
// `javascript:`
|
||||||
|
REGEXP_DEFAULT_ON_TAG_ATTR_4.lastIndex = 0;
|
||||||
|
if (REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
} else if (name === "style") {
|
||||||
|
// `expression()`
|
||||||
|
REGEXP_DEFAULT_ON_TAG_ATTR_7.lastIndex = 0;
|
||||||
|
if (REGEXP_DEFAULT_ON_TAG_ATTR_7.test(value)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
// `url()`
|
||||||
|
REGEXP_DEFAULT_ON_TAG_ATTR_8.lastIndex = 0;
|
||||||
|
if (REGEXP_DEFAULT_ON_TAG_ATTR_8.test(value)) {
|
||||||
|
REGEXP_DEFAULT_ON_TAG_ATTR_4.lastIndex = 0;
|
||||||
|
if (REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cssFilter !== false) {
|
||||||
|
cssFilter = cssFilter || defaultCSSFilter;
|
||||||
|
value = cssFilter.process(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// escape `<>"` before returns
|
||||||
|
value = escapeAttrValue(value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegExp list
|
||||||
|
var REGEXP_LT = /</g;
|
||||||
|
var REGEXP_GT = />/g;
|
||||||
|
var REGEXP_QUOTE = /"/g;
|
||||||
|
var REGEXP_QUOTE_2 = /"/g;
|
||||||
|
var REGEXP_ATTR_VALUE_1 = /&#([a-zA-Z0-9]*);?/gim;
|
||||||
|
var REGEXP_ATTR_VALUE_COLON = /:?/gim;
|
||||||
|
var REGEXP_ATTR_VALUE_NEWLINE = /&newline;?/gim;
|
||||||
|
// var REGEXP_DEFAULT_ON_TAG_ATTR_3 = /\/\*|\*\//gm;
|
||||||
|
var REGEXP_DEFAULT_ON_TAG_ATTR_4 =
|
||||||
|
/((j\s*a\s*v\s*a|v\s*b|l\s*i\s*v\s*e)\s*s\s*c\s*r\s*i\s*p\s*t\s*|m\s*o\s*c\s*h\s*a):/gi;
|
||||||
|
// var REGEXP_DEFAULT_ON_TAG_ATTR_5 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:/gi;
|
||||||
|
// var REGEXP_DEFAULT_ON_TAG_ATTR_6 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:\s*image\//gi;
|
||||||
|
var REGEXP_DEFAULT_ON_TAG_ATTR_7 =
|
||||||
|
/e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n\s*\(.*/gi;
|
||||||
|
var REGEXP_DEFAULT_ON_TAG_ATTR_8 = /u\s*r\s*l\s*\(.*/gi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* escape double quote
|
||||||
|
*
|
||||||
|
* @param {String} str
|
||||||
|
* @return {String} str
|
||||||
|
*/
|
||||||
|
function escapeQuote(str) {
|
||||||
|
return str.replace(REGEXP_QUOTE, """);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unescape double quote
|
||||||
|
*
|
||||||
|
* @param {String} str
|
||||||
|
* @return {String} str
|
||||||
|
*/
|
||||||
|
function unescapeQuote(str) {
|
||||||
|
return str.replace(REGEXP_QUOTE_2, '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* escape html entities
|
||||||
|
*
|
||||||
|
* @param {String} str
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function escapeHtmlEntities(str) {
|
||||||
|
return str.replace(REGEXP_ATTR_VALUE_1, function replaceUnicode(str, code) {
|
||||||
|
return code[0] === "x" || code[0] === "X"
|
||||||
|
? String.fromCharCode(parseInt(code.substr(1), 16))
|
||||||
|
: String.fromCharCode(parseInt(code, 10));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* escape html5 new danger entities
|
||||||
|
*
|
||||||
|
* @param {String} str
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function escapeDangerHtml5Entities(str) {
|
||||||
|
return str
|
||||||
|
.replace(REGEXP_ATTR_VALUE_COLON, ":")
|
||||||
|
.replace(REGEXP_ATTR_VALUE_NEWLINE, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clear nonprintable characters
|
||||||
|
*
|
||||||
|
* @param {String} str
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function clearNonPrintableCharacter(str) {
|
||||||
|
var str2 = "";
|
||||||
|
for (var i = 0, len = str.length; i < len; i++) {
|
||||||
|
str2 += str.charCodeAt(i) < 32 ? " " : str.charAt(i);
|
||||||
|
}
|
||||||
|
return _.trim(str2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get friendly attribute value
|
||||||
|
*
|
||||||
|
* @param {String} str
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function friendlyAttrValue(str) {
|
||||||
|
str = unescapeQuote(str);
|
||||||
|
str = escapeHtmlEntities(str);
|
||||||
|
str = escapeDangerHtml5Entities(str);
|
||||||
|
str = clearNonPrintableCharacter(str);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unescape attribute value
|
||||||
|
*
|
||||||
|
* @param {String} str
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function escapeAttrValue(str) {
|
||||||
|
str = escapeQuote(str);
|
||||||
|
str = escapeHtml(str);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `onIgnoreTag` function for removing all the tags that are not in whitelist
|
||||||
|
*/
|
||||||
|
function onIgnoreTagStripAll() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove tag body
|
||||||
|
* specify a `tags` list, if the tag is not in the `tags` list then process by the specify function (optional)
|
||||||
|
*
|
||||||
|
* @param {array} tags
|
||||||
|
* @param {function} next
|
||||||
|
*/
|
||||||
|
function StripTagBody(tags, next) {
|
||||||
|
if (typeof next !== "function") {
|
||||||
|
next = function () {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var isRemoveAllTag = !Array.isArray(tags);
|
||||||
|
function isRemoveTag(tag) {
|
||||||
|
if (isRemoveAllTag) return true;
|
||||||
|
return _.indexOf(tags, tag) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var removeList = [];
|
||||||
|
var posStart = false;
|
||||||
|
|
||||||
|
return {
|
||||||
|
onIgnoreTag: function (tag, html, options) {
|
||||||
|
if (isRemoveTag(tag)) {
|
||||||
|
if (options.isClosing) {
|
||||||
|
var ret = "[/removed]";
|
||||||
|
var end = options.position + ret.length;
|
||||||
|
removeList.push([
|
||||||
|
posStart !== false ? posStart : options.position,
|
||||||
|
end,
|
||||||
|
]);
|
||||||
|
posStart = false;
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
if (!posStart) {
|
||||||
|
posStart = options.position;
|
||||||
|
}
|
||||||
|
return "[removed]";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return next(tag, html, options);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remove: function (html) {
|
||||||
|
var rethtml = "";
|
||||||
|
var lastPos = 0;
|
||||||
|
_.forEach(removeList, function (pos) {
|
||||||
|
rethtml += html.slice(lastPos, pos[0]);
|
||||||
|
lastPos = pos[1];
|
||||||
|
});
|
||||||
|
rethtml += html.slice(lastPos);
|
||||||
|
return rethtml;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove html comments
|
||||||
|
*
|
||||||
|
* @param {String} html
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function stripCommentTag(html) {
|
||||||
|
var retHtml = "";
|
||||||
|
var lastPos = 0;
|
||||||
|
while (lastPos < html.length) {
|
||||||
|
var i = html.indexOf("<!--", lastPos);
|
||||||
|
if (i === -1) {
|
||||||
|
retHtml += html.slice(lastPos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
retHtml += html.slice(lastPos, i);
|
||||||
|
var j = html.indexOf("-->", i);
|
||||||
|
if (j === -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lastPos = j + 3;
|
||||||
|
}
|
||||||
|
return retHtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove invisible characters
|
||||||
|
*
|
||||||
|
* @param {String} html
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function stripBlankChar(html) {
|
||||||
|
var chars = html.split("");
|
||||||
|
chars = chars.filter(function (char) {
|
||||||
|
var c = char.charCodeAt(0);
|
||||||
|
if (c === 127) return false;
|
||||||
|
if (c <= 31) {
|
||||||
|
if (c === 10 || c === 13) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return chars.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.whiteList = getDefaultWhiteList();
|
||||||
|
exports.getDefaultWhiteList = getDefaultWhiteList;
|
||||||
|
exports.onTag = onTag;
|
||||||
|
exports.onIgnoreTag = onIgnoreTag;
|
||||||
|
exports.onTagAttr = onTagAttr;
|
||||||
|
exports.onIgnoreTagAttr = onIgnoreTagAttr;
|
||||||
|
exports.safeAttrValue = safeAttrValue;
|
||||||
|
exports.escapeHtml = escapeHtml;
|
||||||
|
exports.escapeQuote = escapeQuote;
|
||||||
|
exports.unescapeQuote = unescapeQuote;
|
||||||
|
exports.escapeHtmlEntities = escapeHtmlEntities;
|
||||||
|
exports.escapeDangerHtml5Entities = escapeDangerHtml5Entities;
|
||||||
|
exports.clearNonPrintableCharacter = clearNonPrintableCharacter;
|
||||||
|
exports.friendlyAttrValue = friendlyAttrValue;
|
||||||
|
exports.escapeAttrValue = escapeAttrValue;
|
||||||
|
exports.onIgnoreTagStripAll = onIgnoreTagStripAll;
|
||||||
|
exports.StripTagBody = StripTagBody;
|
||||||
|
exports.stripCommentTag = stripCommentTag;
|
||||||
|
exports.stripBlankChar = stripBlankChar;
|
||||||
|
exports.cssFilter = defaultCSSFilter;
|
||||||
|
exports.getDefaultCSSWhiteList = getDefaultCSSWhiteList;
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* xss
|
||||||
|
*
|
||||||
|
* @author Zongmin Lei<leizongmin@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
var DEFAULT = require("./default");
|
||||||
|
var parser = require("./parser");
|
||||||
|
var FilterXSS = require("./xss");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filter xss function
|
||||||
|
*
|
||||||
|
* @param {String} html
|
||||||
|
* @param {Object} options { whiteList, onTag, onTagAttr, onIgnoreTag, onIgnoreTagAttr, safeAttrValue, escapeHtml }
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function filterXSS(html, options) {
|
||||||
|
var xss = new FilterXSS(options);
|
||||||
|
return xss.process(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports = module.exports = filterXSS;
|
||||||
|
exports.filterXSS = filterXSS;
|
||||||
|
exports.FilterXSS = FilterXSS;
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
for (var i in DEFAULT) {
|
||||||
|
exports[i] = DEFAULT[i];
|
||||||
|
}
|
||||||
|
for (var j in parser) {
|
||||||
|
exports[j] = parser[j];
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
// using `xss` on the browser, output `filterXSS` to the globals
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
window.filterXSS = module.exports;
|
||||||
|
}
|
||||||
|
|
||||||
|
// using `xss` on the WebWorker, output `filterXSS` to the globals
|
||||||
|
function isWorkerEnv() {
|
||||||
|
return (
|
||||||
|
typeof self !== "undefined" &&
|
||||||
|
typeof DedicatedWorkerGlobalScope !== "undefined" &&
|
||||||
|
self instanceof DedicatedWorkerGlobalScope
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (isWorkerEnv()) {
|
||||||
|
self.filterXSS = module.exports;
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
module.exports = {
|
||||||
|
indexOf: function (arr, item) {
|
||||||
|
var i, j;
|
||||||
|
if (Array.prototype.indexOf) {
|
||||||
|
return arr.indexOf(item);
|
||||||
|
}
|
||||||
|
for (i = 0, j = arr.length; i < j; i++) {
|
||||||
|
if (arr[i] === item) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
forEach: function (arr, fn, scope) {
|
||||||
|
var i, j;
|
||||||
|
if (Array.prototype.forEach) {
|
||||||
|
return arr.forEach(fn, scope);
|
||||||
|
}
|
||||||
|
for (i = 0, j = arr.length; i < j; i++) {
|
||||||
|
fn.call(scope, arr[i], i, arr);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trim: function (str) {
|
||||||
|
if (String.prototype.trim) {
|
||||||
|
return str.trim();
|
||||||
|
}
|
||||||
|
return str.replace(/(^\s*)|(\s*$)/g, "");
|
||||||
|
},
|
||||||
|
spaceIndex: function (str) {
|
||||||
|
var reg = /\s|\n|\t/;
|
||||||
|
var match = reg.exec(str);
|
||||||
|
return match ? match.index : -1;
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,229 @@
|
|||||||
|
/**
|
||||||
|
* filter xss
|
||||||
|
*
|
||||||
|
* @author Zongmin Lei<leizongmin@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
var FilterCSS = require("cssfilter").FilterCSS;
|
||||||
|
var DEFAULT = require("./default");
|
||||||
|
var parser = require("./parser");
|
||||||
|
var parseTag = parser.parseTag;
|
||||||
|
var parseAttr = parser.parseAttr;
|
||||||
|
var _ = require("./util");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns `true` if the input value is `undefined` or `null`
|
||||||
|
*
|
||||||
|
* @param {Object} obj
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
function isNull(obj) {
|
||||||
|
return obj === undefined || obj === null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get attributes for a tag
|
||||||
|
*
|
||||||
|
* @param {String} html
|
||||||
|
* @return {Object}
|
||||||
|
* - {String} html
|
||||||
|
* - {Boolean} closing
|
||||||
|
*/
|
||||||
|
function getAttrs(html) {
|
||||||
|
var i = _.spaceIndex(html);
|
||||||
|
if (i === -1) {
|
||||||
|
return {
|
||||||
|
html: "",
|
||||||
|
closing: html[html.length - 2] === "/",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
html = _.trim(html.slice(i + 1, -1));
|
||||||
|
var isClosing = html[html.length - 1] === "/";
|
||||||
|
if (isClosing) html = _.trim(html.slice(0, -1));
|
||||||
|
return {
|
||||||
|
html: html,
|
||||||
|
closing: isClosing,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shallow copy
|
||||||
|
*
|
||||||
|
* @param {Object} obj
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
function shallowCopyObject(obj) {
|
||||||
|
var ret = {};
|
||||||
|
for (var i in obj) {
|
||||||
|
ret[i] = obj[i];
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function keysToLowerCase(obj) {
|
||||||
|
var ret = {};
|
||||||
|
for (var i in obj) {
|
||||||
|
if (Array.isArray(obj[i])) {
|
||||||
|
ret[i.toLowerCase()] = obj[i].map(function (item) {
|
||||||
|
return item.toLowerCase();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ret[i.toLowerCase()] = obj[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FilterXSS class
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* whiteList (or allowList), onTag, onTagAttr, onIgnoreTag,
|
||||||
|
* onIgnoreTagAttr, safeAttrValue, escapeHtml
|
||||||
|
* stripIgnoreTagBody, allowCommentTag, stripBlankChar
|
||||||
|
* css{whiteList, onAttr, onIgnoreAttr} `css=false` means don't use `cssfilter`
|
||||||
|
*/
|
||||||
|
function FilterXSS(options) {
|
||||||
|
options = shallowCopyObject(options || {});
|
||||||
|
|
||||||
|
if (options.stripIgnoreTag) {
|
||||||
|
if (options.onIgnoreTag) {
|
||||||
|
console.error(
|
||||||
|
'Notes: cannot use these two options "stripIgnoreTag" and "onIgnoreTag" at the same time'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
options.onIgnoreTag = DEFAULT.onIgnoreTagStripAll;
|
||||||
|
}
|
||||||
|
if (options.whiteList || options.allowList) {
|
||||||
|
options.whiteList = keysToLowerCase(options.whiteList || options.allowList);
|
||||||
|
} else {
|
||||||
|
options.whiteList = DEFAULT.whiteList;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.onTag = options.onTag || DEFAULT.onTag;
|
||||||
|
options.onTagAttr = options.onTagAttr || DEFAULT.onTagAttr;
|
||||||
|
options.onIgnoreTag = options.onIgnoreTag || DEFAULT.onIgnoreTag;
|
||||||
|
options.onIgnoreTagAttr = options.onIgnoreTagAttr || DEFAULT.onIgnoreTagAttr;
|
||||||
|
options.safeAttrValue = options.safeAttrValue || DEFAULT.safeAttrValue;
|
||||||
|
options.escapeHtml = options.escapeHtml || DEFAULT.escapeHtml;
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
if (options.css === false) {
|
||||||
|
this.cssFilter = false;
|
||||||
|
} else {
|
||||||
|
options.css = options.css || {};
|
||||||
|
this.cssFilter = new FilterCSS(options.css);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start process and returns result
|
||||||
|
*
|
||||||
|
* @param {String} html
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
FilterXSS.prototype.process = function (html) {
|
||||||
|
// compatible with the input
|
||||||
|
html = html || "";
|
||||||
|
html = html.toString();
|
||||||
|
if (!html) return "";
|
||||||
|
|
||||||
|
var me = this;
|
||||||
|
var options = me.options;
|
||||||
|
var whiteList = options.whiteList;
|
||||||
|
var onTag = options.onTag;
|
||||||
|
var onIgnoreTag = options.onIgnoreTag;
|
||||||
|
var onTagAttr = options.onTagAttr;
|
||||||
|
var onIgnoreTagAttr = options.onIgnoreTagAttr;
|
||||||
|
var safeAttrValue = options.safeAttrValue;
|
||||||
|
var escapeHtml = options.escapeHtml;
|
||||||
|
var cssFilter = me.cssFilter;
|
||||||
|
|
||||||
|
// remove invisible characters
|
||||||
|
if (options.stripBlankChar) {
|
||||||
|
html = DEFAULT.stripBlankChar(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove html comments
|
||||||
|
if (!options.allowCommentTag) {
|
||||||
|
html = DEFAULT.stripCommentTag(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if enable stripIgnoreTagBody
|
||||||
|
var stripIgnoreTagBody = false;
|
||||||
|
if (options.stripIgnoreTagBody) {
|
||||||
|
stripIgnoreTagBody = DEFAULT.StripTagBody(
|
||||||
|
options.stripIgnoreTagBody,
|
||||||
|
onIgnoreTag
|
||||||
|
);
|
||||||
|
onIgnoreTag = stripIgnoreTagBody.onIgnoreTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
var retHtml = parseTag(
|
||||||
|
html,
|
||||||
|
function (sourcePosition, position, tag, html, isClosing) {
|
||||||
|
var info = {
|
||||||
|
sourcePosition: sourcePosition,
|
||||||
|
position: position,
|
||||||
|
isClosing: isClosing,
|
||||||
|
isWhite: Object.prototype.hasOwnProperty.call(whiteList, tag),
|
||||||
|
};
|
||||||
|
|
||||||
|
// call `onTag()`
|
||||||
|
var ret = onTag(tag, html, info);
|
||||||
|
if (!isNull(ret)) return ret;
|
||||||
|
|
||||||
|
if (info.isWhite) {
|
||||||
|
if (info.isClosing) {
|
||||||
|
return "</" + tag + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
var attrs = getAttrs(html);
|
||||||
|
var whiteAttrList = whiteList[tag];
|
||||||
|
var attrsHtml = parseAttr(attrs.html, function (name, value) {
|
||||||
|
// call `onTagAttr()`
|
||||||
|
var isWhiteAttr = _.indexOf(whiteAttrList, name) !== -1;
|
||||||
|
var ret = onTagAttr(tag, name, value, isWhiteAttr);
|
||||||
|
if (!isNull(ret)) return ret;
|
||||||
|
|
||||||
|
if (isWhiteAttr) {
|
||||||
|
// call `safeAttrValue()`
|
||||||
|
value = safeAttrValue(tag, name, value, cssFilter);
|
||||||
|
if (value) {
|
||||||
|
return name + '="' + value + '"';
|
||||||
|
} else {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// call `onIgnoreTagAttr()`
|
||||||
|
ret = onIgnoreTagAttr(tag, name, value, isWhiteAttr);
|
||||||
|
if (!isNull(ret)) return ret;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// build new tag html
|
||||||
|
html = "<" + tag;
|
||||||
|
if (attrsHtml) html += " " + attrsHtml;
|
||||||
|
if (attrs.closing) html += " /";
|
||||||
|
html += ">";
|
||||||
|
return html;
|
||||||
|
} else {
|
||||||
|
// call `onIgnoreTag()`
|
||||||
|
ret = onIgnoreTag(tag, html, info);
|
||||||
|
if (!isNull(ret)) return ret;
|
||||||
|
return escapeHtml(html);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
escapeHtml
|
||||||
|
);
|
||||||
|
|
||||||
|
// if enable stripIgnoreTagBody
|
||||||
|
if (stripIgnoreTagBody) {
|
||||||
|
retHtml = stripIgnoreTagBody.remove(retHtml);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retHtml;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = FilterXSS;
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"name": "xss",
|
||||||
|
"main": "./lib/index.js",
|
||||||
|
"typings": "./typings/xss.d.ts",
|
||||||
|
"version": "1.0.14",
|
||||||
|
"description": "Sanitize untrusted HTML (to prevent XSS) with a configuration specified by a Whitelist",
|
||||||
|
"author": "Zongmin Lei <leizongmin@gmail.com> (http://ucdok.com)",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/leizongmin/js-xss.git"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"commander": "^2.20.3",
|
||||||
|
"cssfilter": "0.0.10"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"browserify": "^17.0.0",
|
||||||
|
"coveralls": "^3.1.1",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"eslint": "^8.16.0",
|
||||||
|
"mocha": "^8.4.0",
|
||||||
|
"nyc": "^15.1.0",
|
||||||
|
"uglify-js": "^3.15.5"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib",
|
||||||
|
"bin/xss",
|
||||||
|
"dist",
|
||||||
|
"typings/*.d.ts"
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"xss": "./bin/xss"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint lib/**",
|
||||||
|
"test": "export DEBUG=xss:* && mocha -t 5000",
|
||||||
|
"test-cov": "nyc --reporter=lcov mocha --exit \"test/*.js\" && nyc report",
|
||||||
|
"coveralls": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
|
||||||
|
"build": "./bin/build",
|
||||||
|
"prepublish": "npm run test && npm run build"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/leizongmin/js-xss/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/leizongmin/js-xss",
|
||||||
|
"keywords": [
|
||||||
|
"sanitization",
|
||||||
|
"xss",
|
||||||
|
"sanitize",
|
||||||
|
"sanitisation",
|
||||||
|
"input",
|
||||||
|
"security",
|
||||||
|
"escape",
|
||||||
|
"encode",
|
||||||
|
"filter",
|
||||||
|
"validator",
|
||||||
|
"html",
|
||||||
|
"injection",
|
||||||
|
"whitelist"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,203 @@
|
|||||||
|
/**
|
||||||
|
* xss
|
||||||
|
*
|
||||||
|
* @author Zongmin Lei<leizongmin@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare module "xss" {
|
||||||
|
global {
|
||||||
|
function filterXSS(html: string, options?: IFilterXSSOptions): string;
|
||||||
|
|
||||||
|
namespace XSS {
|
||||||
|
export interface IFilterXSSOptions {
|
||||||
|
allowList?: IWhiteList;
|
||||||
|
whiteList?: IWhiteList;
|
||||||
|
onTag?: OnTagHandler;
|
||||||
|
onTagAttr?: OnTagAttrHandler;
|
||||||
|
onIgnoreTag?: OnTagHandler;
|
||||||
|
onIgnoreTagAttr?: OnTagAttrHandler;
|
||||||
|
safeAttrValue?: SafeAttrValueHandler;
|
||||||
|
escapeHtml?: EscapeHandler;
|
||||||
|
stripIgnoreTag?: boolean;
|
||||||
|
stripIgnoreTagBody?: boolean | string[];
|
||||||
|
allowCommentTag?: boolean;
|
||||||
|
stripBlankChar?: boolean;
|
||||||
|
css?: {} | boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IWhiteList extends Record<string, string[] | undefined> {
|
||||||
|
a?: string[];
|
||||||
|
abbr?: string[];
|
||||||
|
address?: string[];
|
||||||
|
area?: string[];
|
||||||
|
article?: string[];
|
||||||
|
aside?: string[];
|
||||||
|
audio?: string[];
|
||||||
|
b?: string[];
|
||||||
|
bdi?: string[];
|
||||||
|
bdo?: string[];
|
||||||
|
big?: string[];
|
||||||
|
blockquote?: string[];
|
||||||
|
br?: string[];
|
||||||
|
caption?: string[];
|
||||||
|
center?: string[];
|
||||||
|
cite?: string[];
|
||||||
|
code?: string[];
|
||||||
|
col?: string[];
|
||||||
|
colgroup?: string[];
|
||||||
|
dd?: string[];
|
||||||
|
del?: string[];
|
||||||
|
details?: string[];
|
||||||
|
div?: string[];
|
||||||
|
dl?: string[];
|
||||||
|
dt?: string[];
|
||||||
|
em?: string[];
|
||||||
|
figure?: string[];
|
||||||
|
figcaption?: string[];
|
||||||
|
font?: string[];
|
||||||
|
footer?: string[];
|
||||||
|
h1?: string[];
|
||||||
|
h2?: string[];
|
||||||
|
h3?: string[];
|
||||||
|
h4?: string[];
|
||||||
|
h5?: string[];
|
||||||
|
h6?: string[];
|
||||||
|
header?: string[];
|
||||||
|
hr?: string[];
|
||||||
|
i?: string[];
|
||||||
|
img?: string[];
|
||||||
|
ins?: string[];
|
||||||
|
li?: string[];
|
||||||
|
mark?: string[];
|
||||||
|
nav?: string[];
|
||||||
|
ol?: string[];
|
||||||
|
p?: string[];
|
||||||
|
pre?: string[];
|
||||||
|
s?: string[];
|
||||||
|
section?: string[];
|
||||||
|
small?: string[];
|
||||||
|
span?: string[];
|
||||||
|
sub?: string[];
|
||||||
|
sup?: string[];
|
||||||
|
strong?: string[];
|
||||||
|
strike?: string[];
|
||||||
|
summary?: string[];
|
||||||
|
table?: string[];
|
||||||
|
tbody?: string[];
|
||||||
|
td?: string[];
|
||||||
|
tfoot?: string[];
|
||||||
|
th?: string[];
|
||||||
|
thead?: string[];
|
||||||
|
tr?: string[];
|
||||||
|
tt?: string[];
|
||||||
|
u?: string[];
|
||||||
|
ul?: string[];
|
||||||
|
video?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type OnTagHandler = (
|
||||||
|
tag: string,
|
||||||
|
html: string,
|
||||||
|
options: {
|
||||||
|
sourcePosition?: number;
|
||||||
|
position?: number;
|
||||||
|
isClosing?: boolean;
|
||||||
|
isWhite?: boolean;
|
||||||
|
}
|
||||||
|
) => string | void;
|
||||||
|
|
||||||
|
type OnTagAttrHandler = (
|
||||||
|
tag: string,
|
||||||
|
name: string,
|
||||||
|
value: string,
|
||||||
|
isWhiteAttr: boolean
|
||||||
|
) => string | void;
|
||||||
|
|
||||||
|
type SafeAttrValueHandler = (
|
||||||
|
tag: string,
|
||||||
|
name: string,
|
||||||
|
value: string,
|
||||||
|
cssFilter: ICSSFilter
|
||||||
|
) => string;
|
||||||
|
|
||||||
|
type EscapeHandler = (str: string) => string;
|
||||||
|
|
||||||
|
interface ICSSFilter {
|
||||||
|
process(value: string): string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface IFilterXSSOptions extends XSS.IFilterXSSOptions {}
|
||||||
|
|
||||||
|
export interface IWhiteList extends XSS.IWhiteList {}
|
||||||
|
|
||||||
|
export type OnTagHandler = XSS.OnTagHandler;
|
||||||
|
|
||||||
|
export type OnTagAttrHandler = XSS.OnTagAttrHandler;
|
||||||
|
|
||||||
|
export type SafeAttrValueHandler = XSS.SafeAttrValueHandler;
|
||||||
|
|
||||||
|
export type EscapeHandler = XSS.EscapeHandler;
|
||||||
|
|
||||||
|
export interface ICSSFilter extends XSS.ICSSFilter {}
|
||||||
|
|
||||||
|
export function StripTagBody(
|
||||||
|
tags: string[],
|
||||||
|
next: () => void
|
||||||
|
): {
|
||||||
|
onIgnoreTag(
|
||||||
|
tag: string,
|
||||||
|
html: string,
|
||||||
|
options: {
|
||||||
|
position: number;
|
||||||
|
isClosing: boolean;
|
||||||
|
}
|
||||||
|
): string;
|
||||||
|
remove(html: string): string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class FilterXSS {
|
||||||
|
constructor(options?: IFilterXSSOptions);
|
||||||
|
process(html: string): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function filterXSS(html: string, options?: IFilterXSSOptions): string;
|
||||||
|
export function parseTag(
|
||||||
|
html: string,
|
||||||
|
onTag: (
|
||||||
|
sourcePosition: number,
|
||||||
|
position: number,
|
||||||
|
tag: string,
|
||||||
|
html: string,
|
||||||
|
isClosing: boolean
|
||||||
|
) => string,
|
||||||
|
escapeHtml: EscapeHandler
|
||||||
|
): string;
|
||||||
|
export function parseAttr(
|
||||||
|
html: string,
|
||||||
|
onAttr: (name: string, value: string) => string
|
||||||
|
): string;
|
||||||
|
export const whiteList: IWhiteList;
|
||||||
|
export function getDefaultWhiteList(): IWhiteList;
|
||||||
|
export const onTag: OnTagHandler;
|
||||||
|
export const onIgnoreTag: OnTagHandler;
|
||||||
|
export const onTagAttr: OnTagAttrHandler;
|
||||||
|
export const onIgnoreTagAttr: OnTagAttrHandler;
|
||||||
|
export const safeAttrValue: SafeAttrValueHandler;
|
||||||
|
export const escapeHtml: EscapeHandler;
|
||||||
|
export const escapeQuote: EscapeHandler;
|
||||||
|
export const unescapeQuote: EscapeHandler;
|
||||||
|
export const escapeHtmlEntities: EscapeHandler;
|
||||||
|
export const escapeDangerHtml5Entities: EscapeHandler;
|
||||||
|
export const clearNonPrintableCharacter: EscapeHandler;
|
||||||
|
export const friendlyAttrValue: EscapeHandler;
|
||||||
|
export const escapeAttrValue: EscapeHandler;
|
||||||
|
export function onIgnoreTagStripAll(): string;
|
||||||
|
export const stripCommentTag: EscapeHandler;
|
||||||
|
export const stripBlankChar: EscapeHandler;
|
||||||
|
export const cssFilter: ICSSFilter;
|
||||||
|
export function getDefaultCSSWhiteList(): ICSSFilter;
|
||||||
|
|
||||||
|
const xss: (html: string, options?: IFilterXSSOptions) => string;
|
||||||
|
export default xss;
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "yangyangquan_kehu_uniapp",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "main.js",
|
||||||
|
"dependencies": {
|
||||||
|
"qs": "^5.2.1",
|
||||||
|
"xss": "^1.0.14",
|
||||||
|
"md5": "^2.3.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,452 @@
|
|||||||
|
<template>
|
||||||
|
<view class="all_trend_module">
|
||||||
|
<view class="center">
|
||||||
|
<view class="date">
|
||||||
|
<image
|
||||||
|
src="../../../static/home_left.png"
|
||||||
|
class="date_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
@tap="beforeDay(date)"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="date"
|
||||||
|
disabled
|
||||||
|
class="ipt_date"
|
||||||
|
@tap="selectDate"
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
src="../../../static/home_right.png"
|
||||||
|
class="date_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
@tap="afterDay(date)"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="nav">
|
||||||
|
<view
|
||||||
|
class="nav_select"
|
||||||
|
@tap="selectTrend(item.name)"
|
||||||
|
:class="{ nav_select1: item.is }"
|
||||||
|
v-for="(item, index) in navList"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="module_head">
|
||||||
|
<view>交易日期</view>
|
||||||
|
<view class="module_head_name">东财行业指数2级</view>
|
||||||
|
<view class="module_head_rank" @tap="marketRankSelect">
|
||||||
|
<view>板块排名</view>
|
||||||
|
<view class="module_head_icon" v-if="marketRank">
|
||||||
|
<image
|
||||||
|
src="../../../static/shengxu_true.png"
|
||||||
|
class="head_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
src="../../../static/jiangxu_false.png"
|
||||||
|
class="head_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="module_head_icon" v-else>
|
||||||
|
<image
|
||||||
|
src="../../../static/shengxu_false.png"
|
||||||
|
class="head_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
src="../../../static/jiangxu_true.png"
|
||||||
|
class="head_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-if="list.length > 0">
|
||||||
|
<view
|
||||||
|
class="module_list"
|
||||||
|
v-for="(item, index) in list"
|
||||||
|
:key="index"
|
||||||
|
@tap="
|
||||||
|
toTrendDetail(
|
||||||
|
item.securityName,
|
||||||
|
item.securityCode,
|
||||||
|
item.securityType
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<view class="module_list_date">{{ item.businessDate }}</view>
|
||||||
|
<view class="module_list_name">{{ item.securityName }}</view>
|
||||||
|
<view class="module_list_ranking">
|
||||||
|
<view>
|
||||||
|
{{ item.sort }}
|
||||||
|
</view>
|
||||||
|
<view class="list_ranking_right">
|
||||||
|
<image
|
||||||
|
v-if="item.sortChange > 0"
|
||||||
|
src="../../../static/home_paiming_shangsheng.png"
|
||||||
|
class="module_list_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
v-if="item.sortChange < 0"
|
||||||
|
src="../../../static/home_paiming_xiajiang.png"
|
||||||
|
class="module_list_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
<u-icon
|
||||||
|
name="minus"
|
||||||
|
v-if="item.sortChange == 0"
|
||||||
|
size="12"
|
||||||
|
></u-icon>
|
||||||
|
<view
|
||||||
|
v-if="item.sortChange != 0"
|
||||||
|
class="rise_decline"
|
||||||
|
:style="{ color: item.color }"
|
||||||
|
>
|
||||||
|
{{ Math.abs(item.sortChange) }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-else>
|
||||||
|
<u-empty
|
||||||
|
mode="data"
|
||||||
|
icon="http://cdn.uviewui.com/uview/empty/data.png"
|
||||||
|
marginTop="50"
|
||||||
|
>
|
||||||
|
</u-empty>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 选择时间 -->
|
||||||
|
<u-datetime-picker
|
||||||
|
:show="dateShow"
|
||||||
|
v-model="date"
|
||||||
|
mode="date"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="cancelDate"
|
||||||
|
@confirm="confirmDate"
|
||||||
|
></u-datetime-picker>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
navList: [
|
||||||
|
{
|
||||||
|
name: '当日趋势',
|
||||||
|
is: true,
|
||||||
|
current: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '5日趋势',
|
||||||
|
is: false,
|
||||||
|
current: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '10日趋势',
|
||||||
|
is: false,
|
||||||
|
current: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '15日趋势',
|
||||||
|
is: false,
|
||||||
|
current: 15,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '20日趋势',
|
||||||
|
is: false,
|
||||||
|
current: 20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '30日趋势',
|
||||||
|
is: false,
|
||||||
|
current: 30,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 数据
|
||||||
|
list: [],
|
||||||
|
// 板块列表排名升序or降序
|
||||||
|
marketRank: true,
|
||||||
|
trendCurrent: 1,
|
||||||
|
date: '',
|
||||||
|
pagenum: 1,
|
||||||
|
last_page: null,
|
||||||
|
dateShow: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
this.date = options.date
|
||||||
|
this.rending()
|
||||||
|
},
|
||||||
|
onReachBottom() {
|
||||||
|
if (this.pagenum < this.last_page) {
|
||||||
|
this.getnewGoods()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 大盘信息板块列表排名选择
|
||||||
|
marketRankSelect() {
|
||||||
|
this.marketRank = !this.marketRank
|
||||||
|
this.rending()
|
||||||
|
},
|
||||||
|
// 选择趋势
|
||||||
|
selectTrend(name) {
|
||||||
|
this.navList.forEach(item => {
|
||||||
|
if (item.name === name) {
|
||||||
|
item.is = true
|
||||||
|
this.trendCurrent = item.current
|
||||||
|
this.rending()
|
||||||
|
} else {
|
||||||
|
item.is = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
rending() {
|
||||||
|
this.$api
|
||||||
|
.post('/stock/trendPlate', {
|
||||||
|
businessDate: this.date,
|
||||||
|
pageModel: {
|
||||||
|
pageNo: this.pagenum,
|
||||||
|
pageSize: 20,
|
||||||
|
sortField: 'sort',
|
||||||
|
sortWay: this.marketRank ? 'asc' : 'desc',
|
||||||
|
},
|
||||||
|
trendType: this.trendCurrent,
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
console.log(r.list)
|
||||||
|
this.list = r.list
|
||||||
|
this.last_page = r.totalPage
|
||||||
|
// this.data = r
|
||||||
|
console.log(r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
rending1() {
|
||||||
|
this.$api
|
||||||
|
.post('/stock/trendPlate', {
|
||||||
|
businessDate: this.date,
|
||||||
|
pageModel: {
|
||||||
|
pageNo: this.pagenum,
|
||||||
|
pageSize: 20,
|
||||||
|
sortField: 'sort',
|
||||||
|
sortWay: this.marketRank ? 'asc' : 'desc',
|
||||||
|
},
|
||||||
|
trendType: this.trendCurrent,
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
console.log(r.list)
|
||||||
|
this.list = [...this.list, ...r.list]
|
||||||
|
this.last_page = r.totalPage
|
||||||
|
// this.data = r
|
||||||
|
console.log(r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getnewGoods() {
|
||||||
|
this.pagenum = this.pagenum + 1
|
||||||
|
// 1.展示loading效果
|
||||||
|
uni.showLoading({
|
||||||
|
title: '数据加载中...',
|
||||||
|
})
|
||||||
|
// 2.开启节流阀
|
||||||
|
this.isLoading = true
|
||||||
|
// 3.发起网络请求
|
||||||
|
this.rending1()
|
||||||
|
// 无论成功与否都会调用该方法
|
||||||
|
// 4.隐藏loading效果
|
||||||
|
uni.hideLoading()
|
||||||
|
// 5.关闭节流阀
|
||||||
|
this.isLoading = false
|
||||||
|
},
|
||||||
|
toTrendDetail(name, id, type) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/home/market/trendDetail?name=${name}&id=${id}&type=${type}&date=${this.date}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 前一日
|
||||||
|
beforeDay(date) {
|
||||||
|
this.pagenum = 1
|
||||||
|
this.date = this.getTime(new Date(date).getTime() - 24 * 60 * 60 * 1000)
|
||||||
|
this.rending()
|
||||||
|
},
|
||||||
|
// 后一日
|
||||||
|
afterDay(date) {
|
||||||
|
this.pagenum = 1
|
||||||
|
this.date = this.getTime(new Date(date).getTime() + 24 * 60 * 60 * 1000)
|
||||||
|
this.rending()
|
||||||
|
},
|
||||||
|
// 格式化时间戳
|
||||||
|
getTime(date) {
|
||||||
|
let time = new Date(date)
|
||||||
|
let y = time.getFullYear()
|
||||||
|
let m = time.getMonth() + 1
|
||||||
|
let d = time.getDate()
|
||||||
|
let h = time.getHours()
|
||||||
|
let mm = time.getMinutes()
|
||||||
|
let s = time.getSeconds()
|
||||||
|
if (s < 10) {
|
||||||
|
s = '0' + s
|
||||||
|
}
|
||||||
|
if (h < 10) {
|
||||||
|
h = '0' + h
|
||||||
|
}
|
||||||
|
if (mm < 10) {
|
||||||
|
mm = '0' + mm
|
||||||
|
}
|
||||||
|
if (m < 10) {
|
||||||
|
m = '0' + m
|
||||||
|
}
|
||||||
|
if (d < 10) {
|
||||||
|
d = '0' + d
|
||||||
|
}
|
||||||
|
return `${y}-${m}-${d}`
|
||||||
|
},
|
||||||
|
// 打开时间选择器
|
||||||
|
selectDate() {
|
||||||
|
this.dateShow = true
|
||||||
|
uni.hideTabBar({})
|
||||||
|
},
|
||||||
|
// 关闭时间选择器
|
||||||
|
cancelDate() {
|
||||||
|
this.dateShow = false
|
||||||
|
uni.showTabBar({})
|
||||||
|
},
|
||||||
|
// 确认当前选中时间
|
||||||
|
confirmDate(e) {
|
||||||
|
this.pagenum = 1
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.date = this.getTime(e.value)
|
||||||
|
this.rending()
|
||||||
|
})
|
||||||
|
this.dateShow = false
|
||||||
|
uni.showTabBar({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.center {
|
||||||
|
padding: 24rpx;
|
||||||
|
}
|
||||||
|
.nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.nav_select {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 24rpx;
|
||||||
|
padding: 10rpx 20rpx;
|
||||||
|
border: 2rpx solid #999999;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
.nav_select1 {
|
||||||
|
border-color: #ec7c00;
|
||||||
|
color: #ec7c00;
|
||||||
|
background-color: #fffcf9;
|
||||||
|
}
|
||||||
|
.module_head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 24rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 20rpx 28rpx 20rpx 36rpx;
|
||||||
|
width: 686rpx;
|
||||||
|
margin-left: -24rpx;
|
||||||
|
background-color: #fff9f3;
|
||||||
|
.module_head_name {
|
||||||
|
margin-left: 64rpx;
|
||||||
|
}
|
||||||
|
.module_head_rank {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.module_head_icon {
|
||||||
|
margin-left: 8rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.head_icon {
|
||||||
|
width: 18rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.module_list {
|
||||||
|
padding-top: 32rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 28rpx;
|
||||||
|
.module_list_date {
|
||||||
|
// margin-right: 120rpx;
|
||||||
|
}
|
||||||
|
.module_list_name {
|
||||||
|
width: 400rpx;
|
||||||
|
text-align: center;
|
||||||
|
// margin-left: 120rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module_list_ranking {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
}
|
||||||
|
.list_ranking_right {
|
||||||
|
margin-left: 24rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40rpx;
|
||||||
|
.rise_decline {
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin-left: 4rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.module_list_icon {
|
||||||
|
text-align: center;
|
||||||
|
width: 18rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
line-height: 10rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.date {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
.ipt_date {
|
||||||
|
width: 180rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.date_icon {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,232 @@
|
|||||||
|
<template>
|
||||||
|
<view class="trend_detail">
|
||||||
|
<view class="private_table_head">
|
||||||
|
<view class="private_table_head_left">
|
||||||
|
<view class="private_head_name">股票名称</view>
|
||||||
|
<view class="private_head_price">价格</view>
|
||||||
|
<view class="private_head_gains">涨幅</view>
|
||||||
|
</view>
|
||||||
|
<view class="private_table_head_right">
|
||||||
|
<view>{{ currentBelong }}</view>
|
||||||
|
<!-- <image
|
||||||
|
src="../../../static/jiangxu_false.png"
|
||||||
|
class="private_screening_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/> -->
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="private_table_list" v-for="(item, index) in list" :key="index">
|
||||||
|
<view class="private_table_list_left">
|
||||||
|
<view class="table_list_name">
|
||||||
|
<view>{{ item.name }}</view>
|
||||||
|
<view class="table_list_code">{{ item.code }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="table_list_price" :style="{ color: item.color }">
|
||||||
|
{{ item.price }}
|
||||||
|
</view>
|
||||||
|
<view class="table_list_gains" :style="{ color: item.color }">
|
||||||
|
{{ item.gains }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view>{{ item.suoshu }}</view>
|
||||||
|
</view>
|
||||||
|
<u-picker
|
||||||
|
:show="showPrivateBelong"
|
||||||
|
:columns="columnsBelong"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="cancelPrivateBelong"
|
||||||
|
@confirm="confirmPrivateBelong"
|
||||||
|
></u-picker>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 个股所属板块
|
||||||
|
showPrivateBelong: false,
|
||||||
|
currentBelong: '所属板块',
|
||||||
|
columnsBelong: [['所属板块', '成交量']],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
uni.setNavigationBarTitle({
|
||||||
|
title: options.name,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 打开个股所属模块弹出层
|
||||||
|
privateBelong() {
|
||||||
|
uni.hideTabBar({})
|
||||||
|
this.showPrivateBelong = true
|
||||||
|
},
|
||||||
|
// 取消个股所属模块弹出层
|
||||||
|
cancelPrivateBelong() {
|
||||||
|
this.showPrivateBelong = false
|
||||||
|
uni.showTabBar({})
|
||||||
|
},
|
||||||
|
// 确认个股所属模块
|
||||||
|
confirmPrivateBelong(e) {
|
||||||
|
this.currentBelong = e.value[0]
|
||||||
|
this.showPrivateBelong = false
|
||||||
|
uni.showTabBar({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.private_table_head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: #fff9f3;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
justify-content: space-between;
|
||||||
|
.private_table_head_right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.private_table_head_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.private_head_name {
|
||||||
|
width: 200rpx;
|
||||||
|
}
|
||||||
|
.private_head_price {
|
||||||
|
width: 150rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.private_head_gains {
|
||||||
|
width: 150rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_table_list {
|
||||||
|
padding: 24rpx 24rpx;
|
||||||
|
border-bottom: 2rpx solid #f6f6f6;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 28rpx;
|
||||||
|
.private_table_list_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.table_list_name {
|
||||||
|
width: 200rpx;
|
||||||
|
}
|
||||||
|
.table_list_code {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
.table_list_price {
|
||||||
|
width: 150rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.table_list_gains {
|
||||||
|
width: 150rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_screening_icon {
|
||||||
|
width: 18rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
margin-left: 24rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,185 @@
|
|||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<view class="center">
|
||||||
|
<view class="serch">
|
||||||
|
<u-input
|
||||||
|
placeholder="请输入个股代码、板块或名称"
|
||||||
|
v-model="serchValue"
|
||||||
|
prefixIcon="search"
|
||||||
|
prefixIconStyle="font-size: 22px;color: #909399"
|
||||||
|
shape="circle"
|
||||||
|
@input="change"
|
||||||
|
fontSize="24rpx"
|
||||||
|
style="margin-right: "
|
||||||
|
></u-input>
|
||||||
|
<view class="serch_text" @tap="submit">搜索</view>
|
||||||
|
</view>
|
||||||
|
<!-- <view
|
||||||
|
class="serch_list"
|
||||||
|
v-for="(item, index) in serchList"
|
||||||
|
:key="index"
|
||||||
|
@tap="detail(item)"
|
||||||
|
>
|
||||||
|
<image
|
||||||
|
src="../../../static/serch.png"
|
||||||
|
mode="scaleToFill"
|
||||||
|
style="width: 26rpx; height: 26rpx"
|
||||||
|
/>
|
||||||
|
<view style="margin-left: 10rpx">{{ item.name }}</view>
|
||||||
|
</view> -->
|
||||||
|
</view>
|
||||||
|
<view class="table_head" v-if="this.list.length > 0">
|
||||||
|
<view class="head_code">股票代码</view>
|
||||||
|
<view class="head_name">股票名称</view>
|
||||||
|
<view class="head_price">价格</view>
|
||||||
|
<view class="head_gains">涨幅</view>
|
||||||
|
</view>
|
||||||
|
<view class="table_list" v-for="(item, index) in list" :key="index">
|
||||||
|
<view>{{ item.securityCode }}</view>
|
||||||
|
<view>{{ item.securityName }}</view>
|
||||||
|
<view
|
||||||
|
class="list_price"
|
||||||
|
:class="{ list_price1: item.riseLossesCurrentDay < 0 }"
|
||||||
|
>{{ item.closingPrice }}</view
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
class="list_price"
|
||||||
|
:class="{ list_price1: item.riseLossesCurrentDay < 0 }"
|
||||||
|
>{{ item.riseLossesCurrentDay.toFixed(2) }}</view
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
serchValue: '',
|
||||||
|
serchList: [],
|
||||||
|
list: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
change() {
|
||||||
|
// this.list = []
|
||||||
|
// this.$api
|
||||||
|
// .post('/getHomeInfo', {
|
||||||
|
// keyword: this.serchValue,
|
||||||
|
// })
|
||||||
|
// .then(r => {
|
||||||
|
// if (r) {
|
||||||
|
this.serchList = []
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
if (this.serchValue.length == 0) {
|
||||||
|
this.serchList = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
detail(item) {
|
||||||
|
this.serchList = []
|
||||||
|
// this.$api.post('/getHomeInfo', {}).then(r => {
|
||||||
|
// if (r) {
|
||||||
|
// this.isMarket = false
|
||||||
|
|
||||||
|
// this.list = r
|
||||||
|
// this.list.forEach(item => {})
|
||||||
|
// this.list = [...this.list]
|
||||||
|
// this.showLoad = false
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
},
|
||||||
|
submit() {
|
||||||
|
console.log(111)
|
||||||
|
this.$api
|
||||||
|
.post('/stock/originalIssueStockDetail', {
|
||||||
|
businessDate: '',
|
||||||
|
keyWord: this.serchValue,
|
||||||
|
market: '',
|
||||||
|
pageModel: {
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 30,
|
||||||
|
sortField: '',
|
||||||
|
sortWay: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
// this.data = r
|
||||||
|
this.list = r.list
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.center {
|
||||||
|
padding: 0 24rpx;
|
||||||
|
}
|
||||||
|
.serch {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.serch_text {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 120rpx;
|
||||||
|
height: 75rpx;
|
||||||
|
background-color: #ec7c00;
|
||||||
|
color: white;
|
||||||
|
border-radius: 36rpx;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 75rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
/deep/.u-border {
|
||||||
|
border-color: #ec7c00 !important;
|
||||||
|
}
|
||||||
|
.serch_list {
|
||||||
|
padding: 32rpx 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
border-bottom: 1px solid #cfcfcf;
|
||||||
|
}
|
||||||
|
.table_head {
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
background-color: #fff9f3;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 34rpx;
|
||||||
|
}
|
||||||
|
.table_head view {
|
||||||
|
width: 140rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.table_list {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 32rpx 24rpx 0 24rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
.table_list view {
|
||||||
|
text-align: center;
|
||||||
|
width: 140rpx;
|
||||||
|
}
|
||||||
|
.list_price {
|
||||||
|
color: #d9001b;
|
||||||
|
}
|
||||||
|
.list_price1 {
|
||||||
|
color: #6fbb60;
|
||||||
|
}
|
||||||
|
/deep/ .u-input__content__field-wrapper {
|
||||||
|
margin-right: 120rpx !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,148 @@
|
|||||||
|
<template>
|
||||||
|
<view class="login">
|
||||||
|
<view class="center">
|
||||||
|
<view class="logo_box">
|
||||||
|
<image
|
||||||
|
src="../../static/logo.png"
|
||||||
|
style="width: 200rpx; height: 200rpx"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<!-- <button class="submit_weixin btn" @tap="getPhoneNumber">微信登录</button> -->
|
||||||
|
<!-- open-type="getPhoneNumber" -->
|
||||||
|
<!-- @getphonenumber="getPhoneNumber" -->
|
||||||
|
<button class="btn submit_phone" @tap="toPhone">手机号登录/注册</button>
|
||||||
|
<!-- <button class="btn submit_phone">游客登陆</button> -->
|
||||||
|
<view class="protocol">
|
||||||
|
<image
|
||||||
|
v-if="!isChoose"
|
||||||
|
src="../../static/login_xuanze.png"
|
||||||
|
mode="scaleToFill"
|
||||||
|
style="width: 40rpx; height: 40rpx"
|
||||||
|
@tap="isChoose = !isChoose"
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
v-if="isChoose"
|
||||||
|
src="../../static/login_xuanze_true.png"
|
||||||
|
mode="scaleToFill"
|
||||||
|
style="width: 40rpx; height: 40rpx"
|
||||||
|
@tap="isChoose = !isChoose"
|
||||||
|
/>
|
||||||
|
<view class="protocolText" @tap.stop="isChoose = !isChoose">
|
||||||
|
我已阅读并同意
|
||||||
|
<view @tap.stop="toWeb(1)" style="color: #62afed">《用户协议》</view>
|
||||||
|
<view @tap.stop="toWeb(2)" style="color: #62afed">《隐私政策》</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 是否同意协议
|
||||||
|
isChoose: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad() {},
|
||||||
|
methods: {
|
||||||
|
// 获取手机号
|
||||||
|
getPhoneNumber(e) {
|
||||||
|
console.log(e)
|
||||||
|
if (this.isChoose) {
|
||||||
|
// uni.navigateTo({
|
||||||
|
// url: `/pages/index/enroll`,
|
||||||
|
// })
|
||||||
|
// this.$api
|
||||||
|
// .post('/phoneLogin', {
|
||||||
|
// openid: this.$store.state.WXcode.openid,
|
||||||
|
// session_key: this.$store.state.WXcode.session_key,
|
||||||
|
// code: e.detail.code,
|
||||||
|
// })
|
||||||
|
// .then(r => {
|
||||||
|
// if (r) {
|
||||||
|
uni.switchTab({
|
||||||
|
url: `/pages/home/home`,
|
||||||
|
})
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
} else {
|
||||||
|
uni.$u.toast('请先同意协议')
|
||||||
|
}
|
||||||
|
// uni.switchTab({
|
||||||
|
// url: `/pages/home/home`,
|
||||||
|
// })
|
||||||
|
},
|
||||||
|
// 跳转协议详情
|
||||||
|
toWeb() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/index/webview?src=`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 微信登录
|
||||||
|
submitWX() {},
|
||||||
|
// 手机号登录
|
||||||
|
toPhone() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/index/phoneLogin`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.protocol {
|
||||||
|
padding: 0 30rpx;
|
||||||
|
position: fixed;
|
||||||
|
display: flex;
|
||||||
|
bottom: 60rpx;
|
||||||
|
margin-top: 80rpx;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.protocolText {
|
||||||
|
display: flex;
|
||||||
|
margin-left: 16rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
|
||||||
|
text {
|
||||||
|
color: #6678d7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.logo_box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.center {
|
||||||
|
margin-top: 320rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
height: 88rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 88rpx;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
.submit_weixin {
|
||||||
|
margin-top: 140rpx;
|
||||||
|
background-color: #3478ff;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.submit_phone {
|
||||||
|
margin-top: 48rpx;
|
||||||
|
background-color: white;
|
||||||
|
border: 2rpx solid #d2d2d2;
|
||||||
|
}
|
||||||
|
uni-button:after {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
button::after {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,219 @@
|
|||||||
|
<template>
|
||||||
|
<view class="phone_login">
|
||||||
|
<view class="center">
|
||||||
|
<view class="title">手机号登录/注册</view>
|
||||||
|
<view class="ipt_box">
|
||||||
|
<u-input
|
||||||
|
placeholder="请输入手机号"
|
||||||
|
v-model="phone"
|
||||||
|
border="bottom"
|
||||||
|
type="number"
|
||||||
|
fontSize="32rpx"
|
||||||
|
clearable
|
||||||
|
></u-input>
|
||||||
|
<view style="margin-top: 50rpx">
|
||||||
|
<u-input
|
||||||
|
placeholder="请输入密码"
|
||||||
|
v-model="password"
|
||||||
|
border="bottom"
|
||||||
|
type="password"
|
||||||
|
fontSize="32rpx"
|
||||||
|
clearable
|
||||||
|
></u-input>
|
||||||
|
<!-- <u-input
|
||||||
|
border="bottom"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入验证码"
|
||||||
|
v-model="value"
|
||||||
|
fontSize="32rpx"
|
||||||
|
>
|
||||||
|
<template slot="suffix">
|
||||||
|
<u-code
|
||||||
|
ref="uCode"
|
||||||
|
@change="codeChange"
|
||||||
|
seconds="60"
|
||||||
|
changeText="Xs"
|
||||||
|
></u-code>
|
||||||
|
<u-button
|
||||||
|
@tap="getCode"
|
||||||
|
:text="tips"
|
||||||
|
class="custom-style"
|
||||||
|
:plain="true"
|
||||||
|
size="mini"
|
||||||
|
></u-button>
|
||||||
|
</template>
|
||||||
|
</u-input> -->
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="btn_box" @tap="submit">
|
||||||
|
<button class="btn">登录</button>
|
||||||
|
</view>
|
||||||
|
<view class="login_tips">未注册的账号第一次登陆时自动注册账号</view>
|
||||||
|
<view class="protocol">
|
||||||
|
<image
|
||||||
|
v-if="!isChoose"
|
||||||
|
src="../../static/login_xuanze.png"
|
||||||
|
mode="scaleToFill"
|
||||||
|
style="width: 40rpx; height: 40rpx"
|
||||||
|
@tap="isChoose = !isChoose"
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
v-if="isChoose"
|
||||||
|
src="../../static/login_xuanze_true.png"
|
||||||
|
mode="scaleToFill"
|
||||||
|
style="width: 40rpx; height: 40rpx"
|
||||||
|
@tap="isChoose = !isChoose"
|
||||||
|
/>
|
||||||
|
<view class="protocolText" @tap.stop="isChoose = !isChoose">
|
||||||
|
我已阅读并同意
|
||||||
|
<view @tap.stop="toWeb(1)" style="color: #62afed">《用户协议》</view>
|
||||||
|
<view @tap.stop="toWeb(2)" style="color: #62afed">《隐私政策》</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tips: '',
|
||||||
|
// 验证码
|
||||||
|
value: '',
|
||||||
|
// 手机号
|
||||||
|
phone: '',
|
||||||
|
// 是否同意协议
|
||||||
|
isChoose: true,
|
||||||
|
// 邀请码
|
||||||
|
value1: '',
|
||||||
|
password: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 验证码
|
||||||
|
codeChange(text) {
|
||||||
|
this.tips = text
|
||||||
|
},
|
||||||
|
// 获取验证码
|
||||||
|
getCode() {
|
||||||
|
if (this.$refs.uCode.canGetCode) {
|
||||||
|
if (this.phone) {
|
||||||
|
// 模拟向后端请求验证码
|
||||||
|
uni.showLoading({
|
||||||
|
title: '正在获取验证码',
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
// 这里此提示会被this.start()方法中的提示覆盖
|
||||||
|
uni.$u.toast('验证码已发送')
|
||||||
|
// 通知验证码组件内部开始倒计时
|
||||||
|
this.$refs.uCode.start()
|
||||||
|
}, 2000)
|
||||||
|
this.$api.post('/login/sendSms', { phone: this.phone })
|
||||||
|
} else {
|
||||||
|
uni.$u.toast('请先输入手机号')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uni.$u.toast('倒计时结束后再发送')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
submit() {
|
||||||
|
if (this.isChoose) {
|
||||||
|
if (this.phone && this.password) {
|
||||||
|
this.$cache.set('gupiao_phone',this.phone )
|
||||||
|
this.$api
|
||||||
|
.post('/user/login/smsCodeLogin', {
|
||||||
|
phone: this.phone,
|
||||||
|
createWay: 1,
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
let re = r
|
||||||
|
this.$cache.set('token', re)
|
||||||
|
uni.switchTab({
|
||||||
|
url: `/pages/home/home`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// console.log(res.code)
|
||||||
|
//发起网络请求
|
||||||
|
} else {
|
||||||
|
uni.$u.toast('请先输入手机号和密码')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uni.$u.toast('请先同意协议')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.center {
|
||||||
|
padding: 0 24rpx;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
margin-top: 120rpx;
|
||||||
|
font-size: 44rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.ipt_box {
|
||||||
|
margin-top: 100rpx;
|
||||||
|
}
|
||||||
|
.custom-style {
|
||||||
|
color: #6195fe;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
/deep/.u-button--plain.u-button--info {
|
||||||
|
color: #6195fe !important;
|
||||||
|
border: 0 !important;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
/deep/.u-button--mini {
|
||||||
|
font-size: 32rpx !important;
|
||||||
|
}
|
||||||
|
/deep/.u-input--no-radius {
|
||||||
|
padding: 24rpx 18rpx !important;
|
||||||
|
}
|
||||||
|
.btn_box {
|
||||||
|
margin-top: 96rpx;
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
height: 88rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 88rpx;
|
||||||
|
border: 0;
|
||||||
|
background-color: #3478ff;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.protocol {
|
||||||
|
padding: 0 30rpx;
|
||||||
|
position: fixed;
|
||||||
|
display: flex;
|
||||||
|
bottom: 60rpx;
|
||||||
|
margin-top: 80rpx;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.protocolText {
|
||||||
|
display: flex;
|
||||||
|
margin-left: 16rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
|
||||||
|
text {
|
||||||
|
color: #6678d7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.login_tips {
|
||||||
|
color: #999999;
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin-top: 24rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
/deep/.u-number-box {
|
||||||
|
align-items: start !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,776 @@
|
|||||||
|
<template>
|
||||||
|
<view class="trade">
|
||||||
|
<view class="head">
|
||||||
|
<view class="head_left">
|
||||||
|
<view class="head_date">
|
||||||
|
<image
|
||||||
|
src="../../static/home_left.png"
|
||||||
|
class="date_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
@tap="beforeDay(date)"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="date"
|
||||||
|
disabled
|
||||||
|
class="ipt_date"
|
||||||
|
@tap="selectDate"
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
src="../../static/home_right.png"
|
||||||
|
class="date_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
@tap="afterDay(date)"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="select_days" @tap="RangeSelect">
|
||||||
|
<view>{{ timeRange }}</view>
|
||||||
|
<image
|
||||||
|
src="../../static/jiangxu_false.png"
|
||||||
|
class="select_days_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<image
|
||||||
|
@tap="selectSift"
|
||||||
|
src="../../static/home_sift.png"
|
||||||
|
class="head_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="table_head">
|
||||||
|
<view class="table_head_left">
|
||||||
|
<view class="table_head_name">名称/时间</view>
|
||||||
|
<view class="table_head_clinchMoney">成交价</view>
|
||||||
|
<view class="table_head_clinchNum">成交量</view>
|
||||||
|
</view>
|
||||||
|
<view>类别/金额</view>
|
||||||
|
</view>
|
||||||
|
<view class="table_center">
|
||||||
|
<view v-if="list.length > 0">
|
||||||
|
<view
|
||||||
|
class="table_main"
|
||||||
|
v-for="(item, index) in list"
|
||||||
|
:key="index"
|
||||||
|
@tap="toDetail(item.id)"
|
||||||
|
>
|
||||||
|
<view class="table_left">
|
||||||
|
<view class="table_name_date">
|
||||||
|
<view class="table_name">{{ item.securityName }}</view>
|
||||||
|
<view class="table_date">{{ item.transactionDate }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="table_clinchMoney">
|
||||||
|
{{ item.transactionPrice }}
|
||||||
|
</view>
|
||||||
|
<view class="table_clinchNum">
|
||||||
|
{{ item.volume }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="table_type_money">
|
||||||
|
<view>{{ item.securityType }}</view>
|
||||||
|
<view class="table_money">{{ item.transactionAmount }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-else>
|
||||||
|
<u-empty
|
||||||
|
mode="data"
|
||||||
|
icon="http://cdn.uviewui.com/uview/empty/data.png"
|
||||||
|
marginTop="50"
|
||||||
|
>
|
||||||
|
</u-empty>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="add_icon_box" @tap="addInfo">
|
||||||
|
<u-icon name="plus" color="#ffffff" size="28"></u-icon>
|
||||||
|
</view>
|
||||||
|
<!-- 筛选弹出层 -->
|
||||||
|
<u-popup :show="showSift" mode="top">
|
||||||
|
<view class="sift_show_type">
|
||||||
|
<view>交易类型</view>
|
||||||
|
<view class="show_trade_type">
|
||||||
|
<view
|
||||||
|
class="trade_type"
|
||||||
|
:class="{ trade_type1: isSecurities }"
|
||||||
|
@tap="isSecurities = true"
|
||||||
|
>
|
||||||
|
证券买入
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="trade_type"
|
||||||
|
:class="{ trade_type1: !isSecurities }"
|
||||||
|
@tap="isSecurities = false"
|
||||||
|
>
|
||||||
|
证券卖出
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="sift_show_date">
|
||||||
|
<view style="font-weight: bold">时间段</view>
|
||||||
|
<view class="show_date_range">
|
||||||
|
<u-input
|
||||||
|
placeholder="请选择开始日期"
|
||||||
|
disabled
|
||||||
|
border="surround"
|
||||||
|
v-model="startTime"
|
||||||
|
inputAlign="center"
|
||||||
|
fontSize="12"
|
||||||
|
disabledColor="#ffffff"
|
||||||
|
@tap="selectStartTime"
|
||||||
|
></u-input>
|
||||||
|
<u-icon name="minus" size="12" style="padding: 0 16rpx"></u-icon>
|
||||||
|
<u-input
|
||||||
|
disabled
|
||||||
|
placeholder="请选择截至日期"
|
||||||
|
disabledColor="#ffffff"
|
||||||
|
border="surround"
|
||||||
|
v-model="endTime"
|
||||||
|
inputAlign="center"
|
||||||
|
fontSize="12"
|
||||||
|
@tap="selectEndTime"
|
||||||
|
></u-input>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="show_securities_code">
|
||||||
|
<view style="font-weight: bold">证券代码</view>
|
||||||
|
<view class="securities_code">
|
||||||
|
<u-input
|
||||||
|
placeholder="请输入证券代码"
|
||||||
|
border="surround"
|
||||||
|
v-model="securitiesCode"
|
||||||
|
fontSize="12"
|
||||||
|
></u-input>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="show_btn_box">
|
||||||
|
<view class="btn show_cancel" @tap="cancelShowSift">取消</view>
|
||||||
|
<view class="btn show_confirm" @tap="confirmShowSift">确定</view>
|
||||||
|
</view>
|
||||||
|
</u-popup>
|
||||||
|
<!-- 筛选/开始时间选择 -->
|
||||||
|
<u-datetime-picker
|
||||||
|
:show="startTimeShow"
|
||||||
|
v-model="date"
|
||||||
|
mode="date"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="startTimeShow = false"
|
||||||
|
@confirm="confirmStartTime"
|
||||||
|
></u-datetime-picker>
|
||||||
|
<!-- 筛选/结束时间选择 -->
|
||||||
|
<u-datetime-picker
|
||||||
|
:show="endTimeShow"
|
||||||
|
v-model="date"
|
||||||
|
mode="date"
|
||||||
|
:minDate="minDate"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="endTimeShow = false"
|
||||||
|
@confirm="confirmEndTime"
|
||||||
|
></u-datetime-picker>
|
||||||
|
<!-- 录入信息弹出层 -->
|
||||||
|
<u-popup :show="showInput" mode="bottom">
|
||||||
|
<view class="show_input_list" @tap="toInfo"> 录入信息 </view>
|
||||||
|
<view class="show_input_list" @tap="daoru"> 导入信息 </view>
|
||||||
|
<view
|
||||||
|
class="show_input_list"
|
||||||
|
style="background-color: #f6f6f6"
|
||||||
|
@tap="cancelInput"
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</view>
|
||||||
|
</u-popup>
|
||||||
|
<!-- 时间段选择 -->
|
||||||
|
<u-picker
|
||||||
|
:show="timeRangeShow"
|
||||||
|
:columns="columns"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="cancelRange"
|
||||||
|
@confirm="confirmRange"
|
||||||
|
></u-picker>
|
||||||
|
<!-- 选择时间 -->
|
||||||
|
<u-datetime-picker
|
||||||
|
:show="dateShow"
|
||||||
|
v-model="date"
|
||||||
|
mode="date"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="cancelDate"
|
||||||
|
@confirm="confirmDate"
|
||||||
|
></u-datetime-picker>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import config from '@/config'
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 时间选择
|
||||||
|
dateShow: false,
|
||||||
|
date: '',
|
||||||
|
// 时间段选择
|
||||||
|
timeRangeShow: false,
|
||||||
|
timeRange: '近7天',
|
||||||
|
columns: [['近7天', '近30天']],
|
||||||
|
// 录入信息弹出层
|
||||||
|
showInput: false,
|
||||||
|
// 筛选弹出层
|
||||||
|
showSift: false,
|
||||||
|
// 证券买入or证券卖出
|
||||||
|
isSecurities: true,
|
||||||
|
// 筛选/开始时间
|
||||||
|
startTime: '',
|
||||||
|
// 选择开始时间
|
||||||
|
startTimeShow: false,
|
||||||
|
// 选择截至时间
|
||||||
|
endTimeShow: false,
|
||||||
|
// 选择截至时间最小值
|
||||||
|
minDate: null,
|
||||||
|
// 筛选/截至时间
|
||||||
|
endTime: '',
|
||||||
|
// 证券代码
|
||||||
|
securitiesCode: '',
|
||||||
|
list: [],
|
||||||
|
pagenum: 1,
|
||||||
|
last_page: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
this.date = this.getTime(Date.parse(new Date()))
|
||||||
|
},
|
||||||
|
onShow() {
|
||||||
|
this.rending()
|
||||||
|
},
|
||||||
|
onReachBottom() {
|
||||||
|
if (this.pagenum < this.last_page) {
|
||||||
|
this.getnewGoods()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
daoru() {
|
||||||
|
let that = this
|
||||||
|
wx.chooseMessageFile({
|
||||||
|
count: 1, //限制选择的文件数量
|
||||||
|
type: 'file', //非图片和视频的文件,不选默认为all
|
||||||
|
//type:'video',//视频
|
||||||
|
//type:'image',//图片
|
||||||
|
success(res) {
|
||||||
|
console.log(res)
|
||||||
|
res.tempFiles.forEach((item, index) => {
|
||||||
|
let str = item.path
|
||||||
|
let i = str.indexOf('.')
|
||||||
|
let result = str.substr(i + 1, str.length)
|
||||||
|
console.log(result)
|
||||||
|
if (result == 'xlsx' || result == 'xls') {
|
||||||
|
uni.uploadFile({
|
||||||
|
url: config.server + '/import/transactionRecord',
|
||||||
|
filePath: item.path,
|
||||||
|
name: 'file',
|
||||||
|
// header: {
|
||||||
|
// "X-Token": "Bearer " + store.state.app.token,
|
||||||
|
// },
|
||||||
|
// formData: {
|
||||||
|
// month: that.date,
|
||||||
|
// },
|
||||||
|
success: uploadFileRes => {
|
||||||
|
that.rending()
|
||||||
|
that.showInput = false
|
||||||
|
uni.showTabBar({})
|
||||||
|
uni.showToast({
|
||||||
|
title: '导入成功',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fail: err => {
|
||||||
|
console.log(err)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '暂只支持excel文件',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
rending() {
|
||||||
|
this.$api
|
||||||
|
.post('/transaction/findPage', {
|
||||||
|
endTime: this.endTime,
|
||||||
|
keyWord: this.securitiesCode,
|
||||||
|
num: '',
|
||||||
|
pageModel: {
|
||||||
|
pageNo: this.pagenum,
|
||||||
|
pageSize: 20,
|
||||||
|
sortField: '',
|
||||||
|
sortWay: '',
|
||||||
|
},
|
||||||
|
startTime: this.startTime,
|
||||||
|
transactionCategory: this.isSecurities ? '证券买入' : '证券卖出',
|
||||||
|
transactionDate: this.date,
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
this.list = r.list
|
||||||
|
this.last_page = r.totalPage
|
||||||
|
console.log(r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
rending1() {
|
||||||
|
this.$api
|
||||||
|
.post('/transaction/findPage', {
|
||||||
|
endTime: this.endTime,
|
||||||
|
keyWord: this.securitiesCode,
|
||||||
|
num: '',
|
||||||
|
pageModel: {
|
||||||
|
pageNo: this.pagenum,
|
||||||
|
pageSize: 20,
|
||||||
|
sortField: '',
|
||||||
|
sortWay: '',
|
||||||
|
},
|
||||||
|
startTime: this.startTime,
|
||||||
|
transactionCategory: this.isSecurities ? '证券买入' : '证券卖出',
|
||||||
|
transactionDate: this.endTime ? this.date : '',
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
this.list = [...this.list, ...r.list]
|
||||||
|
this.last_page = r.totalPage
|
||||||
|
console.log(r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 格式化时间戳
|
||||||
|
getTime(date) {
|
||||||
|
let time = new Date(date)
|
||||||
|
let y = time.getFullYear()
|
||||||
|
let m = time.getMonth() + 1
|
||||||
|
let d = time.getDate()
|
||||||
|
let h = time.getHours()
|
||||||
|
let mm = time.getMinutes()
|
||||||
|
let s = time.getSeconds()
|
||||||
|
if (s < 10) {
|
||||||
|
s = '0' + s
|
||||||
|
}
|
||||||
|
if (h < 10) {
|
||||||
|
h = '0' + h
|
||||||
|
}
|
||||||
|
if (mm < 10) {
|
||||||
|
mm = '0' + mm
|
||||||
|
}
|
||||||
|
if (m < 10) {
|
||||||
|
m = '0' + m
|
||||||
|
}
|
||||||
|
if (d < 10) {
|
||||||
|
d = '0' + d
|
||||||
|
}
|
||||||
|
return `${y}-${m}-${d}`
|
||||||
|
},
|
||||||
|
// 打开时间选择器
|
||||||
|
selectDate() {
|
||||||
|
this.dateShow = true
|
||||||
|
uni.hideTabBar({})
|
||||||
|
},
|
||||||
|
// 关闭时间选择器
|
||||||
|
cancelDate() {
|
||||||
|
this.dateShow = false
|
||||||
|
uni.showTabBar({})
|
||||||
|
},
|
||||||
|
// 确认当前选中时间
|
||||||
|
confirmDate(e) {
|
||||||
|
this.pagenum = 1
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.date = this.getTime(e.value)
|
||||||
|
|
||||||
|
this.rending()
|
||||||
|
})
|
||||||
|
this.dateShow = false
|
||||||
|
uni.showTabBar({})
|
||||||
|
},
|
||||||
|
// 打开筛选弹出层
|
||||||
|
selectSift() {
|
||||||
|
uni.hideTabBar({})
|
||||||
|
this.showSift = true
|
||||||
|
},
|
||||||
|
// 关闭筛选弹出层
|
||||||
|
cancelShowSift() {
|
||||||
|
this.startTimeShow = false
|
||||||
|
this.endTimeShow = false
|
||||||
|
uni.showTabBar({})
|
||||||
|
this.showSift = false
|
||||||
|
},
|
||||||
|
// 确定筛选
|
||||||
|
confirmShowSift() {
|
||||||
|
this.pagenum = 1
|
||||||
|
this.$api
|
||||||
|
.post('/transaction/findPage', {
|
||||||
|
endTime: this.endTime,
|
||||||
|
keyWord: this.securitiesCode,
|
||||||
|
num: '',
|
||||||
|
pageModel: {
|
||||||
|
pageNo: this.pagenum,
|
||||||
|
pageSize: 20,
|
||||||
|
sortField: '',
|
||||||
|
sortWay: '',
|
||||||
|
},
|
||||||
|
startTime: this.startTime,
|
||||||
|
transactionCategory: this.isSecurities ? '证券买入' : '证券卖出',
|
||||||
|
transactionDate: '',
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
this.list = r.list
|
||||||
|
uni.showTabBar({})
|
||||||
|
this.showSift = false
|
||||||
|
console.log(r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 确定开始时间
|
||||||
|
confirmStartTime(e) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.minDate = new Date(e.value).getTime()
|
||||||
|
console.log(this.minDate)
|
||||||
|
this.startTime = this.getTime(e.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.startTimeShow = false
|
||||||
|
},
|
||||||
|
// 确定截至时间
|
||||||
|
confirmEndTime(e) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.endTime = this.getTime(e.value)
|
||||||
|
})
|
||||||
|
this.endTimeShow = false
|
||||||
|
},
|
||||||
|
// 选择开始时间
|
||||||
|
selectStartTime() {
|
||||||
|
if (!this.endTimeShow) {
|
||||||
|
this.startTimeShow = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 选择截至时间
|
||||||
|
selectEndTime() {
|
||||||
|
if (this.startTime) {
|
||||||
|
if (!this.startTimeShow) {
|
||||||
|
this.endTimeShow = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请先选择开始时间',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 打开录入信息弹出层
|
||||||
|
addInfo() {
|
||||||
|
uni.hideTabBar({})
|
||||||
|
this.showInput = true
|
||||||
|
},
|
||||||
|
// 关闭录入信息弹出层
|
||||||
|
cancelInput() {
|
||||||
|
this.showInput = false
|
||||||
|
uni.showTabBar({})
|
||||||
|
},
|
||||||
|
// 打开时间段选择
|
||||||
|
RangeSelect() {
|
||||||
|
uni.hideTabBar({})
|
||||||
|
this.timeRangeShow = true
|
||||||
|
},
|
||||||
|
// 确定时间段选择
|
||||||
|
confirmRange(e) {
|
||||||
|
this.timeRange = e.value[0]
|
||||||
|
this.$api
|
||||||
|
.post('/transaction/findPage', {
|
||||||
|
endTime: '',
|
||||||
|
keyWord: '',
|
||||||
|
num: this.timeRange == '近7天' ? 7 : 30,
|
||||||
|
pageModel: {
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
sortField: '',
|
||||||
|
sortWay: '',
|
||||||
|
},
|
||||||
|
startTime: '',
|
||||||
|
transactionCategory: '',
|
||||||
|
transactionDate: '',
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
this.list = r.list
|
||||||
|
console.log(r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
this.timeRangeShow = false
|
||||||
|
uni.showTabBar({})
|
||||||
|
},
|
||||||
|
// 取消时间段选择
|
||||||
|
cancelRange() {
|
||||||
|
this.timeRangeShow = false
|
||||||
|
uni.showTabBar({})
|
||||||
|
},
|
||||||
|
// 前往交易详情
|
||||||
|
toDetail(id) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/trade/tradeDateil?id=${id}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 前往录入信息
|
||||||
|
toInfo() {
|
||||||
|
this.showInput = false
|
||||||
|
uni.showTabBar({})
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/trade/tradeInfo',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 前一日
|
||||||
|
beforeDay(date) {
|
||||||
|
this.pagenum = 1
|
||||||
|
this.endTime = ''
|
||||||
|
this.startTime = ''
|
||||||
|
this.date = this.getTime(new Date(date).getTime() - 24 * 60 * 60 * 1000)
|
||||||
|
this.rending()
|
||||||
|
},
|
||||||
|
// 后一日
|
||||||
|
afterDay(date) {
|
||||||
|
this.pagenum = 1
|
||||||
|
this.endTime = ''
|
||||||
|
this.startTime = ''
|
||||||
|
this.date = this.getTime(new Date(date).getTime() + 24 * 60 * 60 * 1000)
|
||||||
|
this.rending()
|
||||||
|
},
|
||||||
|
getnewGoods() {
|
||||||
|
this.pagenum = this.pagenum + 1
|
||||||
|
// 1.展示loading效果
|
||||||
|
uni.showLoading({
|
||||||
|
title: '数据加载中...',
|
||||||
|
})
|
||||||
|
// 2.开启节流阀
|
||||||
|
this.isLoading = true
|
||||||
|
// 3.发起网络请求
|
||||||
|
this.rending1()
|
||||||
|
// 无论成功与否都会调用该方法
|
||||||
|
// 4.隐藏loading效果
|
||||||
|
uni.hideLoading()
|
||||||
|
// 5.关闭节流阀
|
||||||
|
this.isLoading = false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.head {
|
||||||
|
padding: 24rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
.head_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.head_date {
|
||||||
|
padding: 16rpx 32rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
border: 2rpx solid #ec7c00;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.ipt_date {
|
||||||
|
color: #ec7c00;
|
||||||
|
width: 180rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.date_icon {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.select_days {
|
||||||
|
margin-left: 40rpx;
|
||||||
|
padding: 16rpx 32rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
border: 2rpx solid #999999;
|
||||||
|
font-size: 24rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
.select_days_icon {
|
||||||
|
margin-left: 40rpx;
|
||||||
|
width: 18rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.head_icon {
|
||||||
|
height: 40rpx;
|
||||||
|
width: 40rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sift_show_type {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 16rpx 24rpx;
|
||||||
|
}
|
||||||
|
.show_trade_type {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
.trade_type {
|
||||||
|
padding: 12rpx 40rpx;
|
||||||
|
border: 2rpx solid #999999;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
margin-right: 40rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-top: 16rpx;
|
||||||
|
}
|
||||||
|
.trade_type1 {
|
||||||
|
border-color: #ec7c00;
|
||||||
|
color: #ec7c00;
|
||||||
|
background-color: #fffcf9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sift_show_date {
|
||||||
|
padding: 24rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
.show_date_range {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/deep/.u-icon--right {
|
||||||
|
padding: 0 16rpx !important;
|
||||||
|
}
|
||||||
|
.show_securities_code {
|
||||||
|
padding: 0 24rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
.securities_code {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
margin-bottom: 80rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.show_btn_box {
|
||||||
|
height: 88rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.btn {
|
||||||
|
width: 374rpx;
|
||||||
|
height: 88rpx;
|
||||||
|
line-height: 88rpx;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
.show_cancel {
|
||||||
|
border-top: 2rpx solid #f6f6f6;
|
||||||
|
}
|
||||||
|
.show_confirm {
|
||||||
|
width: 378rpx;
|
||||||
|
background-color: #ec7c00;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.table_head {
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
background-color: #fff9f3;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
// justify-content: space-between;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
justify-content: space-between;
|
||||||
|
.table_head_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.table_head_name {
|
||||||
|
width: 200rpx;
|
||||||
|
}
|
||||||
|
.table_head_clinchMoney {
|
||||||
|
width: 170rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.table_head_clinchNum {
|
||||||
|
width: 170rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.table_center {
|
||||||
|
padding: 0 24rpx;
|
||||||
|
.table_main {
|
||||||
|
padding: 24rpx 0;
|
||||||
|
font-size: 28rpx;
|
||||||
|
padding-bottom: 2rpx solid #f6f6f6;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
.table_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.table_name_date {
|
||||||
|
.table_date {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.table_name {
|
||||||
|
width: 200rpx;
|
||||||
|
}
|
||||||
|
.table_type_money {
|
||||||
|
text-align: center;
|
||||||
|
.table_money {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.table_clinchMoney {
|
||||||
|
width: 170rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.table_clinchNum {
|
||||||
|
width: 170rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.add_icon_box {
|
||||||
|
width: 104rpx;
|
||||||
|
height: 104rpx;
|
||||||
|
background: linear-gradient(132deg, #ffb564 0%, #ec7c00 100%);
|
||||||
|
position: fixed;
|
||||||
|
border-radius: 104rpx;
|
||||||
|
bottom: 56rpx;
|
||||||
|
right: 24rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.show_input_list {
|
||||||
|
padding: 40rpx 0rpx;
|
||||||
|
border-bottom: 2rpx solid #f6f6f6;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,152 @@
|
|||||||
|
<template>
|
||||||
|
<view class="trade_dateil">
|
||||||
|
<view class="securities_info">
|
||||||
|
<view class="list">
|
||||||
|
<view class="list_left">证券名称</view>
|
||||||
|
<view>{{ data.securityName }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="list">
|
||||||
|
<view class="list_left">证券代码</view>
|
||||||
|
<view>{{ data.securityCode }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="clinch_info">
|
||||||
|
<view class="list">
|
||||||
|
<view class="list_left">交易类别</view>
|
||||||
|
<view>{{ data.securityType }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="list">
|
||||||
|
<view class="list_left">成交价格</view>
|
||||||
|
<view>{{ data.transactionPrice }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="list">
|
||||||
|
<view class="list_left">成交数量</view>
|
||||||
|
<view>{{ data.volume }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="list">
|
||||||
|
<view class="list_left">成交金额</view>
|
||||||
|
<view>{{ data.transactionAmount }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="list">
|
||||||
|
<view class="list_left" style="margin-right: 140rpx">手续费</view>
|
||||||
|
<view>{{ data.premium }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="list">
|
||||||
|
<view class="list_left">手续费率</view>
|
||||||
|
<view>{{ data.premiumRatio }}%</view>
|
||||||
|
</view>
|
||||||
|
<view class="list">
|
||||||
|
<view class="list_left">成交日期</view>
|
||||||
|
<view>{{ data.transactionDate }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="list">
|
||||||
|
<view class="list_left">成交时间</view>
|
||||||
|
<view>{{ data.transactionTime }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="footer">
|
||||||
|
<button class="btn" @tap="del">删除</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
data: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
this.rending(options.id)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
rending(id) {
|
||||||
|
this.$api
|
||||||
|
.get('/transaction/getDetailById/' + id)
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
this.data = r
|
||||||
|
console.log(r, '获取数据')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
del() {
|
||||||
|
this.$api
|
||||||
|
.post('/transaction/delByIds', {
|
||||||
|
ids: this.data.id,
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '删除成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 1000,
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack({
|
||||||
|
delta: 1,
|
||||||
|
})
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
// delByIds
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.securities_info {
|
||||||
|
padding: 32rpx 24rpx 0 24rpx;
|
||||||
|
background-color: white;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
.list {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 28rpx;
|
||||||
|
padding-bottom: 32rpx;
|
||||||
|
.list_left {
|
||||||
|
color: #666666;
|
||||||
|
margin-right: 112rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.clinch_info {
|
||||||
|
padding: 32rpx 24rpx 0 24rpx;
|
||||||
|
background-color: white;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 64rpx;
|
||||||
|
width: 702rpx;
|
||||||
|
height: 88rpx;
|
||||||
|
left: 24rpx;
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
border-radius: 44rpx;
|
||||||
|
border: 2rpx solid #ec7c00;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
height: 88rpx;
|
||||||
|
line-height: 88rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #ec7c00;
|
||||||
|
}
|
||||||
|
button::after {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="scss">
|
||||||
|
page {
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,373 @@
|
|||||||
|
<template>
|
||||||
|
<view class="trade_info">
|
||||||
|
<view class="center">
|
||||||
|
<view class="list">
|
||||||
|
<view>证券名称</view>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="data.name"
|
||||||
|
placeholder="请输入"
|
||||||
|
class="ipt"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="list">
|
||||||
|
<view>证券代码</view>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="data.code"
|
||||||
|
placeholder="请输入"
|
||||||
|
class="ipt"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="list" @tap="showType = true">
|
||||||
|
<view>交易类别</view>
|
||||||
|
<view class="list_right">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
v-model="data.type"
|
||||||
|
placeholder="请输入"
|
||||||
|
class="ipt"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
src="../../static/right_all.png"
|
||||||
|
class="right_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="list">
|
||||||
|
<view>成交价格</view>
|
||||||
|
<input
|
||||||
|
type="digit"
|
||||||
|
v-model="data.clinchPrice"
|
||||||
|
placeholder="请输入"
|
||||||
|
class="ipt"
|
||||||
|
@input="changePrice"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="list">
|
||||||
|
<view>成交数量</view>
|
||||||
|
<input
|
||||||
|
type="digit"
|
||||||
|
v-model="data.clinchNum"
|
||||||
|
placeholder="请输入"
|
||||||
|
class="ipt"
|
||||||
|
@input="changeNum"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="list">
|
||||||
|
<view>成交金额</view>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
disabled
|
||||||
|
v-model="data.clinchMoney"
|
||||||
|
placeholder="请输入"
|
||||||
|
class="ipt"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="list">
|
||||||
|
<view>手续费</view>
|
||||||
|
<input
|
||||||
|
type="digit"
|
||||||
|
disabled
|
||||||
|
v-model="data.formalitiesFee"
|
||||||
|
placeholder="请输入"
|
||||||
|
class="ipt"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="list">
|
||||||
|
<view>手续费率</view>
|
||||||
|
<view class="list_right">
|
||||||
|
<input
|
||||||
|
type="digit"
|
||||||
|
v-model="data.formalitiesRates"
|
||||||
|
placeholder="请输入"
|
||||||
|
class="ipt"
|
||||||
|
@input="changeRates"
|
||||||
|
/>
|
||||||
|
<view>%</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="list" @tap="showDate = true">
|
||||||
|
<view>成交日期</view>
|
||||||
|
<view class="list_right">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
v-model="data.date"
|
||||||
|
placeholder="请选择"
|
||||||
|
class="ipt"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
src="../../static/right_all.png"
|
||||||
|
class="right_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="list" @tap="showTime = true">
|
||||||
|
<view>成交时间</view>
|
||||||
|
<view class="list_right">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
v-model="data.time"
|
||||||
|
placeholder="请选择"
|
||||||
|
class="ipt"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
src="../../static/right_all.png"
|
||||||
|
class="right_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="footer">
|
||||||
|
<button class="btn" @tap="submit">提交信息</button>
|
||||||
|
</view>
|
||||||
|
<!-- 交易类别 -->
|
||||||
|
<u-picker
|
||||||
|
:show="showType"
|
||||||
|
:columns="columns"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="showType = false"
|
||||||
|
@confirm="confirmType"
|
||||||
|
></u-picker>
|
||||||
|
<!-- 成交日期 -->
|
||||||
|
<u-datetime-picker
|
||||||
|
:show="showDate"
|
||||||
|
v-model="data.date"
|
||||||
|
mode="date"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="showDate = false"
|
||||||
|
@confirm="confirmDate"
|
||||||
|
></u-datetime-picker>
|
||||||
|
<!-- 成交时间 -->
|
||||||
|
<u-datetime-picker
|
||||||
|
:show="showTime"
|
||||||
|
v-model="data.time"
|
||||||
|
mode="time"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="showTime = false"
|
||||||
|
@confirm="confirmTime"
|
||||||
|
></u-datetime-picker>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 日期选择
|
||||||
|
showDate: false,
|
||||||
|
// 时间选择
|
||||||
|
showTime: false,
|
||||||
|
// 交易类别选择
|
||||||
|
showType: false,
|
||||||
|
columns: [['证券买入', '证券卖出']],
|
||||||
|
data: {
|
||||||
|
name: '',
|
||||||
|
code: '',
|
||||||
|
type: '',
|
||||||
|
clinchPrice: '',
|
||||||
|
// 成交数量
|
||||||
|
clinchNum: '',
|
||||||
|
// 成交金额
|
||||||
|
clinchMoney: '',
|
||||||
|
// 手续费
|
||||||
|
formalitiesFee: '',
|
||||||
|
// 手续费率
|
||||||
|
formalitiesRates: '5',
|
||||||
|
// 日期
|
||||||
|
date: '',
|
||||||
|
// 时间
|
||||||
|
time: '',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 选择交易类别
|
||||||
|
confirmType(e) {
|
||||||
|
this.data.type = e.value[0]
|
||||||
|
this.showType = false
|
||||||
|
},
|
||||||
|
// 选择日期
|
||||||
|
confirmDate(e) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.data.date = this.getTime(e.value)
|
||||||
|
})
|
||||||
|
this.showDate = false
|
||||||
|
},
|
||||||
|
// 选择时间
|
||||||
|
confirmTime(e) {
|
||||||
|
this.showTime = false
|
||||||
|
},
|
||||||
|
// 格式化时间戳
|
||||||
|
getTime(date) {
|
||||||
|
let time = new Date(date)
|
||||||
|
let y = time.getFullYear()
|
||||||
|
let m = time.getMonth() + 1
|
||||||
|
let d = time.getDate()
|
||||||
|
let h = time.getHours()
|
||||||
|
let mm = time.getMinutes()
|
||||||
|
let s = time.getSeconds()
|
||||||
|
if (s < 10) {
|
||||||
|
s = '0' + s
|
||||||
|
}
|
||||||
|
if (h < 10) {
|
||||||
|
h = '0' + h
|
||||||
|
}
|
||||||
|
if (mm < 10) {
|
||||||
|
mm = '0' + mm
|
||||||
|
}
|
||||||
|
if (m < 10) {
|
||||||
|
m = '0' + m
|
||||||
|
}
|
||||||
|
if (d < 10) {
|
||||||
|
d = '0' + d
|
||||||
|
}
|
||||||
|
return `${y}-${m}-${d}`
|
||||||
|
},
|
||||||
|
getTime1(date) {
|
||||||
|
let time = new Date(date)
|
||||||
|
let y = time.getFullYear()
|
||||||
|
let m = time.getMonth() + 1
|
||||||
|
let d = time.getDate()
|
||||||
|
let h = time.getHours()
|
||||||
|
let mm = time.getMinutes()
|
||||||
|
let s = time.getSeconds()
|
||||||
|
if (s < 10) {
|
||||||
|
s = '0' + s
|
||||||
|
}
|
||||||
|
if (h < 10) {
|
||||||
|
h = '0' + h
|
||||||
|
}
|
||||||
|
if (mm < 10) {
|
||||||
|
mm = '0' + mm
|
||||||
|
}
|
||||||
|
if (m < 10) {
|
||||||
|
m = '0' + m
|
||||||
|
}
|
||||||
|
if (d < 10) {
|
||||||
|
d = '0' + d
|
||||||
|
}
|
||||||
|
return `${h}:${mm}:${s}`
|
||||||
|
},
|
||||||
|
// 输入成交价格
|
||||||
|
changePrice(e) {
|
||||||
|
if (this.data.clinchNum) {
|
||||||
|
this.data.clinchMoney = this.data.clinchPrice * this.data.clinchNum
|
||||||
|
this.data.clinchMoney = this.data.clinchMoney.toFixed(2)
|
||||||
|
if (this.data.formalitiesRates) {
|
||||||
|
this.data.formalitiesFee =
|
||||||
|
this.data.clinchMoney * this.data.formalitiesRates
|
||||||
|
this.data.formalitiesFee = this.data.formalitiesFee.toFixed(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 输入成交数量
|
||||||
|
changeNum(e) {
|
||||||
|
if (this.data.clinchPrice) {
|
||||||
|
this.data.clinchMoney = this.data.clinchPrice * this.data.clinchNum
|
||||||
|
this.data.clinchMoney = this.data.clinchMoney.toFixed(2)
|
||||||
|
if (this.data.formalitiesRates) {
|
||||||
|
this.data.formalitiesFee =
|
||||||
|
this.data.clinchMoney * this.data.formalitiesRates
|
||||||
|
this.data.formalitiesFee = this.data.formalitiesFee.toFixed(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 手续费
|
||||||
|
changeRates() {
|
||||||
|
if (this.data.clinchMoney) {
|
||||||
|
this.data.formalitiesFee =
|
||||||
|
this.data.clinchMoney * this.data.formalitiesRates
|
||||||
|
this.data.formalitiesFee = this.data.formalitiesFee / 100
|
||||||
|
this.data.formalitiesFee = this.data.formalitiesFee.toFixed(2)
|
||||||
|
console.log(this.formalitiesFee)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
submit() {
|
||||||
|
this.$api
|
||||||
|
.post('/transaction/saveOrUpdate', {
|
||||||
|
createTime: '',
|
||||||
|
id: 0,
|
||||||
|
premium: this.data.formalitiesFee,
|
||||||
|
premiumRatio: this.data.formalitiesRates,
|
||||||
|
securityCode: this.data.code,
|
||||||
|
securityName: this.data.name,
|
||||||
|
securityType: '',
|
||||||
|
transactionAmount: this.data.clinchMoney,
|
||||||
|
transactionCategory: this.data.type,
|
||||||
|
transactionDate: this.data.date,
|
||||||
|
transactionPrice: this.data.clinchPrice,
|
||||||
|
transactionTime: this.data.date + ' ' + this.data.time + ':00',
|
||||||
|
userId: 0,
|
||||||
|
volume: this.data.clinchNum,
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '添加成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 1000,
|
||||||
|
})
|
||||||
|
uni.navigateBack({
|
||||||
|
delta: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.center {
|
||||||
|
padding: 40rpx 24rpx;
|
||||||
|
}
|
||||||
|
.list {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 28rpx;
|
||||||
|
padding-bottom: 32rpx;
|
||||||
|
.ipt {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.list_right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.right_icon {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
margin-left: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 64rpx;
|
||||||
|
width: 702rpx;
|
||||||
|
height: 88rpx;
|
||||||
|
left: 24rpx;
|
||||||
|
.btn {
|
||||||
|
border-radius: 44rpx;
|
||||||
|
background-color: #ec7c00;
|
||||||
|
color: white;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 88rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,348 @@
|
|||||||
|
<template>
|
||||||
|
<view class="all_breakeven">
|
||||||
|
<view class="center">
|
||||||
|
<view class="private_serch" @tap="toSerch">
|
||||||
|
<view class="private_serch_left">
|
||||||
|
<u-icon name="search" size="20"></u-icon>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="请输入请输入个股代码、板块或名称"
|
||||||
|
class="private_serch_ipt"
|
||||||
|
style="margin-left: 8rpx"
|
||||||
|
v-model="serchValue"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view @tap="submitSerch">搜索</view>
|
||||||
|
</view>
|
||||||
|
<view class="private_screening">
|
||||||
|
<view class="private_screening_list" @tap="showPrivateMarket = true">
|
||||||
|
<view>{{ currentMarket }}</view>
|
||||||
|
<image
|
||||||
|
src="../../static/jiangxu_false.png"
|
||||||
|
class="private_screening_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<!-- <view class="private_screening_list" @tap="showPrivatePlate = true">
|
||||||
|
<view>{{ currentPlate }}</view>
|
||||||
|
<image
|
||||||
|
src="../../static/jiangxu_false.png"
|
||||||
|
class="private_screening_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view> -->
|
||||||
|
<view class="private_screening_list" @tap="showPrivatePrice = true">
|
||||||
|
<view>{{ currentPrice }}</view>
|
||||||
|
<image
|
||||||
|
src="../../static/jiangxu_false.png"
|
||||||
|
class="private_screening_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="private_table_head">
|
||||||
|
<view class="private_table_head_left">
|
||||||
|
<view class="private_head_name">股票代码</view>
|
||||||
|
<view class="private_head_price">买入时间</view>
|
||||||
|
<view class="private_head_gains">卖出时间</view>
|
||||||
|
</view>
|
||||||
|
<view class="private_table_head_right" @tap="privateBelong">
|
||||||
|
<view>盈亏金额</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="private_table_list"
|
||||||
|
v-for="(item, index) in list"
|
||||||
|
:key="index"
|
||||||
|
@tap="toBreakevenDetail(item.securityCode, item.securityName)"
|
||||||
|
>
|
||||||
|
<view class="private_table_list_left">
|
||||||
|
<view class="table_list_name">
|
||||||
|
<view>{{ item.securityName }}</view>
|
||||||
|
<view class="table_list_code">{{ item.securityCode }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="table_list_price">
|
||||||
|
{{ item.buyDate }}
|
||||||
|
</view>
|
||||||
|
<view class="table_list_gains">
|
||||||
|
{{ item.sellDate }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="table_list_totalMoney"
|
||||||
|
:class="{ table_list_gains1: item.totalMoney < 0 }"
|
||||||
|
>{{ item.totalMoney }}</view
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
<!-- 个股筛选大盘 -->
|
||||||
|
<u-picker
|
||||||
|
:show="showPrivateMarket"
|
||||||
|
:columns="columnsMarket"
|
||||||
|
keyName="name"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="showPrivateMarket = false"
|
||||||
|
@confirm="confirmPrivateMarket"
|
||||||
|
></u-picker>
|
||||||
|
<!-- 个股筛选板块 -->
|
||||||
|
<u-picker
|
||||||
|
:show="showPrivatePlate"
|
||||||
|
:columns="columnsPlate"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="showPrivatePlate = false"
|
||||||
|
@confirm="confirmPrivatePlate"
|
||||||
|
></u-picker>
|
||||||
|
<!-- 个股筛选价格 -->
|
||||||
|
<u-picker
|
||||||
|
:show="showPrivatePrice"
|
||||||
|
:columns="columnsPrice"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="showPrivatePrice = false"
|
||||||
|
@confirm="confirmPrivatePrice"
|
||||||
|
></u-picker>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 个股筛选价格
|
||||||
|
showPrivatePrice: false,
|
||||||
|
currentPrice: '全部盈亏',
|
||||||
|
currentPriceIndex: '',
|
||||||
|
columnsPrice: [['全部盈亏', '盈利个股', '亏损个股']],
|
||||||
|
// 个股筛选板块
|
||||||
|
showPrivatePlate: false,
|
||||||
|
currentPlate: '全部板块',
|
||||||
|
columnsPlate: [
|
||||||
|
['全部板块', '上证指数', '深证指数', '创业板', '科创板'],
|
||||||
|
],
|
||||||
|
// 个股筛选大盘
|
||||||
|
showPrivateMarket: false,
|
||||||
|
currentMarket: '全部大盘',
|
||||||
|
columnsMarket: [
|
||||||
|
[
|
||||||
|
{ name: '全部大盘', current: '' },
|
||||||
|
{ name: '上证指数', current: 'SZ' },
|
||||||
|
{ name: '深证指数', current: 'SH' },
|
||||||
|
{ name: '创业板', current: '30' },
|
||||||
|
{ name: '科创版', current: '688' },
|
||||||
|
],
|
||||||
|
],
|
||||||
|
currentMarketId: '',
|
||||||
|
list: [],
|
||||||
|
pagenum: 1,
|
||||||
|
last_page: null,
|
||||||
|
serchValue: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
this.rending()
|
||||||
|
},
|
||||||
|
onReachBottom() {
|
||||||
|
if (this.pagenum < this.last_page) {
|
||||||
|
this.getnewGoods()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
rending() {
|
||||||
|
this.$api
|
||||||
|
.post('/transaction/profitOrLoss', {
|
||||||
|
endDate: '',
|
||||||
|
keyWord: this.serchValue,
|
||||||
|
pageModel: {
|
||||||
|
pageNo: this.pagenum,
|
||||||
|
pageSize: 20,
|
||||||
|
sortField: '',
|
||||||
|
sortWay: '',
|
||||||
|
},
|
||||||
|
securityType: this.currentMarketId,
|
||||||
|
sortAmount: 1,
|
||||||
|
startDate: '',
|
||||||
|
type: this.currentPriceIndex,
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
// this.data = r
|
||||||
|
this.list = r.list
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 确认个股筛选大盘
|
||||||
|
confirmPrivateMarket(e) {
|
||||||
|
this.pagenum = 1
|
||||||
|
this.currentMarket = e.value[0].name
|
||||||
|
this.currentMarketId = e.value[0].current
|
||||||
|
this.rending()
|
||||||
|
this.showPrivateMarket = false
|
||||||
|
},
|
||||||
|
// 确认个股筛选板块
|
||||||
|
confirmPrivatePlate(e) {
|
||||||
|
this.currentPlate = e.value[0]
|
||||||
|
this.showPrivatePlate = false
|
||||||
|
},
|
||||||
|
// 确认个股筛选价格
|
||||||
|
confirmPrivatePrice(e) {
|
||||||
|
this.pagenum = 1
|
||||||
|
this.currentPrice = e.value[0]
|
||||||
|
if (this.currentPrice == '全部盈亏') {
|
||||||
|
this.currentPriceIndex = ''
|
||||||
|
} else if (this.currentPrice == '盈利个股') {
|
||||||
|
this.currentPriceIndex = 1
|
||||||
|
} else {
|
||||||
|
this.currentPriceIndex = 2
|
||||||
|
}
|
||||||
|
this.rending()
|
||||||
|
this.showPrivatePrice = false
|
||||||
|
},
|
||||||
|
// 上拉加载
|
||||||
|
getnewGoods() {
|
||||||
|
this.pagenum = this.pagenum + 1
|
||||||
|
this.rending1()
|
||||||
|
// 1.展示loading效果
|
||||||
|
uni.showLoading({
|
||||||
|
title: '数据加载中...',
|
||||||
|
})
|
||||||
|
// 2.开启节流阀
|
||||||
|
this.isLoading = true
|
||||||
|
// 3.发起网络请求
|
||||||
|
|
||||||
|
// 无论成功与否都会调用该方法
|
||||||
|
// 4.隐藏loading效果
|
||||||
|
uni.hideLoading()
|
||||||
|
// 5.关闭节流阀
|
||||||
|
this.isLoading = false
|
||||||
|
},
|
||||||
|
// 前往个股详情
|
||||||
|
toBreakevenDetail(id, name) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/user/breakevenDetail?id=${id}&name=${name}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitSerch() {
|
||||||
|
this.rending()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.center {
|
||||||
|
padding: 24rpx 24rpx;
|
||||||
|
}
|
||||||
|
.private_serch {
|
||||||
|
padding: 24rpx 32rpx;
|
||||||
|
border: 2rpx solid #dedede;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
color: #ec7c00;
|
||||||
|
.private_serch_ipt {
|
||||||
|
width: 400rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
.private_serch_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_screening {
|
||||||
|
padding: 32rpx 24rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 24rpx;
|
||||||
|
.private_screening_list {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_screening_icon {
|
||||||
|
width: 18rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
margin-left: 24rpx;
|
||||||
|
}
|
||||||
|
.private_table_head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: #fff9f3;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
justify-content: space-between;
|
||||||
|
.private_table_head_right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.private_table_head_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.private_head_name {
|
||||||
|
width: 150rpx;
|
||||||
|
}
|
||||||
|
.private_head_price {
|
||||||
|
width: 180rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.private_head_gains {
|
||||||
|
width: 180rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_table_list {
|
||||||
|
padding: 24rpx 24rpx;
|
||||||
|
border-bottom: 2rpx solid #f6f6f6;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 28rpx;
|
||||||
|
.private_table_list_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.table_list_name {
|
||||||
|
width: 150rpx;
|
||||||
|
}
|
||||||
|
.table_list_code {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
.table_list_price {
|
||||||
|
width: 180rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.table_list_totalMoney {
|
||||||
|
color: #d9001b;
|
||||||
|
}
|
||||||
|
.table_list_gains {
|
||||||
|
width: 180rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.table_list_gains1 {
|
||||||
|
color: #6fbb60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_screening_icon {
|
||||||
|
width: 18rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
margin-left: 24rpx;
|
||||||
|
}
|
||||||
|
.table_list_totalMoney {
|
||||||
|
color: #d9001b;
|
||||||
|
}
|
||||||
|
.table_list_gains1 {
|
||||||
|
color: #6fbb60;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,525 @@
|
|||||||
|
<template>
|
||||||
|
<u-loading-page v-if="load" :loading="load"></u-loading-page>
|
||||||
|
<view class="trade_stats" v-else>
|
||||||
|
<view class="head" :class="{ head1: totalEarnings < 0 }">
|
||||||
|
<view class="head_top">
|
||||||
|
<view class="all_breakeven">{{ name }}</view>
|
||||||
|
<view class="all_money">{{ totalProfit }}</view>
|
||||||
|
<view>收益率 {{ totalEarnings.toFixed(2) }}%</view>
|
||||||
|
</view>
|
||||||
|
<view class="head_bottom">
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in nav"
|
||||||
|
:key="index"
|
||||||
|
class="head_bottom_list"
|
||||||
|
@tap="selectNav(item.name)"
|
||||||
|
>
|
||||||
|
<view :class="{ bottom_list: item.is }">{{ item.name }}</view>
|
||||||
|
<image
|
||||||
|
v-if="item.is"
|
||||||
|
src="../../static/collection_select.png"
|
||||||
|
class="select_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="main">
|
||||||
|
<view class="main_date" v-if="nav[3].is == true">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="main_date_ipt"
|
||||||
|
disabled
|
||||||
|
placeholder="请选择开始日期"
|
||||||
|
v-model="startDate"
|
||||||
|
@tap="selectStartDate"
|
||||||
|
/>
|
||||||
|
<u-icon name="minus" size="20" style="padding: 0 20rpx"></u-icon>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
disabled
|
||||||
|
class="main_date_ipt"
|
||||||
|
placeholder="请选择截至日期"
|
||||||
|
v-model="endDate"
|
||||||
|
@tap="selectEndDate"
|
||||||
|
/>
|
||||||
|
<view class="main_date_serch">搜索</view>
|
||||||
|
</view>
|
||||||
|
<view class="main_nav">
|
||||||
|
<view
|
||||||
|
:class="{ main_nav_color: isEarnings }"
|
||||||
|
@tap="selectMainNav(true)"
|
||||||
|
>
|
||||||
|
收益率
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
style="margin-left: 72rpx"
|
||||||
|
:class="{ main_nav_color: !isEarnings }"
|
||||||
|
@tap="selectMainNav(false)"
|
||||||
|
>
|
||||||
|
盈亏金额
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view style="height: 600rpx; width: 100%; padding-top: 60rpx">
|
||||||
|
<l-echart ref="chart"></l-echart>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 筛选/开始时间选择 -->
|
||||||
|
<u-datetime-picker
|
||||||
|
:show="startDateShow"
|
||||||
|
v-model="date"
|
||||||
|
mode="date"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="startDateShow = false"
|
||||||
|
@confirm="confirmStartDate"
|
||||||
|
></u-datetime-picker>
|
||||||
|
<!-- 筛选/结束时间选择 -->
|
||||||
|
<u-datetime-picker
|
||||||
|
:show="endDateShow"
|
||||||
|
v-model="date"
|
||||||
|
mode="date"
|
||||||
|
:minDate="minDate"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="endDateShow = false"
|
||||||
|
@confirm="confirmEndDate"
|
||||||
|
></u-datetime-picker>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from '@/uni_modules/lime-echart/static/echarts.min'
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
totalProfit: '',
|
||||||
|
totalEarnings: '',
|
||||||
|
bgcColor: {
|
||||||
|
background: '',
|
||||||
|
},
|
||||||
|
nav: [
|
||||||
|
{
|
||||||
|
name: '本月',
|
||||||
|
is: true,
|
||||||
|
current: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '近一月',
|
||||||
|
is: false,
|
||||||
|
current: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '近三月',
|
||||||
|
is: false,
|
||||||
|
current: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '自定义',
|
||||||
|
is: false,
|
||||||
|
current: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
navIndex: 0,
|
||||||
|
// 收益率or盈亏金额
|
||||||
|
isEarnings: true,
|
||||||
|
// 自定义开始日期
|
||||||
|
startDate: '',
|
||||||
|
// 选择开始时间
|
||||||
|
startDateShow: false,
|
||||||
|
// 自定义截至日期
|
||||||
|
endDate: '',
|
||||||
|
// 选择截至时间
|
||||||
|
endDateShow: false,
|
||||||
|
minDate: null,
|
||||||
|
name: '',
|
||||||
|
load: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
this.name = options.name
|
||||||
|
this.code = options.id
|
||||||
|
this.rending()
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
// 执行echarts方法
|
||||||
|
this.initEchartsZ()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 选择导航
|
||||||
|
selectNav(name) {
|
||||||
|
this.nav.forEach(item => {
|
||||||
|
if (item.name === name) {
|
||||||
|
item.is = true
|
||||||
|
this.navIndex = item.current
|
||||||
|
this.initEchartsZ()
|
||||||
|
} else {
|
||||||
|
item.is = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 选择收益率or盈亏金额
|
||||||
|
selectMainNav(is) {
|
||||||
|
this.isEarnings = is
|
||||||
|
},
|
||||||
|
// 确定开始时间
|
||||||
|
confirmStartDate(e) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.minDate = new Date(e.value).getTime()
|
||||||
|
console.log(this.minDate)
|
||||||
|
this.startDate = this.getTime(e.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.startDateShow = false
|
||||||
|
},
|
||||||
|
// 确定截至时间
|
||||||
|
confirmEndDate(e) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.endDate = this.getTime(e.value)
|
||||||
|
})
|
||||||
|
this.endDateShow = false
|
||||||
|
},
|
||||||
|
// 选择开始时间
|
||||||
|
selectStartDate() {
|
||||||
|
if (!this.endDateShow) {
|
||||||
|
this.startDateShow = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 选择截至时间
|
||||||
|
selectEndDate() {
|
||||||
|
if (this.startDate) {
|
||||||
|
if (!this.startDateShow) {
|
||||||
|
this.endDateShow = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请先选择开始时间',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 格式化时间戳
|
||||||
|
getTime(date) {
|
||||||
|
let time = new Date(date)
|
||||||
|
let y = time.getFullYear()
|
||||||
|
let m = time.getMonth() + 1
|
||||||
|
let d = time.getDate()
|
||||||
|
let h = time.getHours()
|
||||||
|
let mm = time.getMinutes()
|
||||||
|
let s = time.getSeconds()
|
||||||
|
if (s < 10) {
|
||||||
|
s = '0' + s
|
||||||
|
}
|
||||||
|
if (h < 10) {
|
||||||
|
h = '0' + h
|
||||||
|
}
|
||||||
|
if (mm < 10) {
|
||||||
|
mm = '0' + mm
|
||||||
|
}
|
||||||
|
if (m < 10) {
|
||||||
|
m = '0' + m
|
||||||
|
}
|
||||||
|
if (d < 10) {
|
||||||
|
d = '0' + d
|
||||||
|
}
|
||||||
|
return `${y}-${m}-${d}`
|
||||||
|
},
|
||||||
|
rending() {
|
||||||
|
this.$api
|
||||||
|
.post('/transaction/totalEarnings', {
|
||||||
|
securityCode: this.code,
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
// this.data = r
|
||||||
|
this.totalProfit = r.totalEarnings
|
||||||
|
this.totalEarnings = r.earningsRate
|
||||||
|
if (r.earningsRate > 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.setNavigationBarColor({
|
||||||
|
frontColor: '#ffffff',
|
||||||
|
backgroundColor: '#F93938',
|
||||||
|
})
|
||||||
|
}, 100)
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.setNavigationBarColor({
|
||||||
|
frontColor: '#ffffff',
|
||||||
|
backgroundColor: '#568EFF',
|
||||||
|
})
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
this.load = false
|
||||||
|
console.log(r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
initEchartsZ() {
|
||||||
|
this.$api
|
||||||
|
.post('/transaction/earningsLine', {
|
||||||
|
endDate: this.endDate,
|
||||||
|
securityCode: '',
|
||||||
|
startDate: this.startDate,
|
||||||
|
timeType: this.navIndex,
|
||||||
|
type: this.isEarnings ? 1 : 2,
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
console.log(r)
|
||||||
|
// this.data = r
|
||||||
|
this.$refs.chart.init(echarts, chart => {
|
||||||
|
let option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
position: function (pt) {
|
||||||
|
return [pt[0], '10%']
|
||||||
|
},
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
left: 'center',
|
||||||
|
text: 'Large Ara Chart',
|
||||||
|
},
|
||||||
|
toolbox: {
|
||||||
|
feature: {
|
||||||
|
dataZoom: {
|
||||||
|
yAxisIndex: 'none',
|
||||||
|
},
|
||||||
|
restore: {},
|
||||||
|
saveAsImage: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'time',
|
||||||
|
boundaryGap: false,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
boundaryGap: [0, '100%'],
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
show: false,
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '数据',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'none',
|
||||||
|
// 背景色
|
||||||
|
// areaStyle: {},
|
||||||
|
data: r,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
chart.setOption(option)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.head {
|
||||||
|
height: 300rpx;
|
||||||
|
color: white;
|
||||||
|
background: linear-gradient(180deg, #f93938 0%, #fa4c0d 100%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.head_top {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 28rpx;
|
||||||
|
.all_breakeven {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
.all_money {
|
||||||
|
margin: 20rpx 0;
|
||||||
|
font-size: 60rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.head_bottom {
|
||||||
|
padding: 0 24rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
.head_bottom_list {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
margin-right: 64rpx;
|
||||||
|
padding-bottom: 24rpx;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.bottom_list {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.select_icon {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 24rpx;
|
||||||
|
height: 14rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.head1 {
|
||||||
|
background: linear-gradient(180deg, #568eff 0%, #4479eb 100%);
|
||||||
|
}
|
||||||
|
.main {
|
||||||
|
margin-top: 32rpx;
|
||||||
|
.main_date {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
.main_date_ipt {
|
||||||
|
width: 170rpx;
|
||||||
|
padding: 10rpx 46rpx;
|
||||||
|
border: 2rpx solid #999999;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
height: 36rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.main_date_serch {
|
||||||
|
color: #ec7c00;
|
||||||
|
margin-left: 32rpx;
|
||||||
|
}
|
||||||
|
/deep/.u-icon--right {
|
||||||
|
padding: 0 16rpx !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.main_nav {
|
||||||
|
margin-top: 32rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
.main_nav_color {
|
||||||
|
color: #ec7c00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.line {
|
||||||
|
height: 20rpx;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
.footer_nav {
|
||||||
|
padding: 32rpx 24rpx;
|
||||||
|
display: flex;
|
||||||
|
font-weight: bold;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
.footer_nav_right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.right_icon {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
margin-left: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_table_head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: #fff9f3;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
justify-content: space-between;
|
||||||
|
.private_table_head_right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.private_table_head_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.private_head_name {
|
||||||
|
width: 150rpx;
|
||||||
|
}
|
||||||
|
.private_head_price {
|
||||||
|
width: 180rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.private_head_gains {
|
||||||
|
width: 180rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_table_list {
|
||||||
|
padding: 24rpx 24rpx;
|
||||||
|
border-bottom: 2rpx solid #f6f6f6;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 28rpx;
|
||||||
|
.private_table_list_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.table_list_name {
|
||||||
|
width: 150rpx;
|
||||||
|
}
|
||||||
|
.table_list_code {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
.table_list_price {
|
||||||
|
width: 180rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.table_list_gains {
|
||||||
|
width: 180rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_screening_icon {
|
||||||
|
width: 18rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
margin-left: 24rpx;
|
||||||
|
}
|
||||||
|
.see_more {
|
||||||
|
padding: 32rpx 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #ec7c00;
|
||||||
|
}
|
||||||
|
.more_icon {
|
||||||
|
width: 22rpx;
|
||||||
|
height: 22rpx;
|
||||||
|
margin-left: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,363 @@
|
|||||||
|
<template>
|
||||||
|
<view class="collection">
|
||||||
|
<view class="nav center">
|
||||||
|
<view>
|
||||||
|
<text :class="{ nav_select: !isPrivate }" @tap="selectNav(false)">
|
||||||
|
指数/板块
|
||||||
|
</text>
|
||||||
|
<text :class="{ nav_select: isPrivate }" @tap="selectNav(true)">
|
||||||
|
个股
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="center" style="padding-top: 0">
|
||||||
|
<view class="private_serch">
|
||||||
|
<view class="private_serch_left">
|
||||||
|
<u-icon name="search" size="20"></u-icon>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="serchValue"
|
||||||
|
placeholder="请输入请输入个股代码、板块或名称"
|
||||||
|
class="private_serch_ipt"
|
||||||
|
style="margin-left: 8rpx"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view @tap="submit">搜索</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-if="!showLoad">
|
||||||
|
<view class="private_table_head">
|
||||||
|
<view class="private_table_head_left">
|
||||||
|
<view class="private_head_name">股票名称</view>
|
||||||
|
<view class="private_head_price">价格</view>
|
||||||
|
<view class="private_head_gains">涨幅</view>
|
||||||
|
</view>
|
||||||
|
<view class="private_table_head_right" @tap="privateBelong">
|
||||||
|
<view>关注日期</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="private_table_list"
|
||||||
|
v-for="(item, index) in list"
|
||||||
|
@tap="to(item.securityName, item.securityCode)"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<view class="private_table_list_left">
|
||||||
|
<view class="table_list_name">
|
||||||
|
<view>{{ item.securityName }}</view>
|
||||||
|
<view class="table_list_code">{{ item.securityCode }}</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="table_list_price"
|
||||||
|
:class="{ table_list_price1: item.riseLossesCurrentDay < 0 }"
|
||||||
|
>
|
||||||
|
{{ item.closingPrice }}
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="table_list_gains"
|
||||||
|
:class="{ table_list_gains1: item.riseLossesCurrentDay < 0 }"
|
||||||
|
>
|
||||||
|
<span v-if="item.riseLossesCurrentDay"
|
||||||
|
>{{ item.riseLossesCurrentDay.toFixed(2) }}%</span
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view>{{ item.collectDate }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-else class="show_load">
|
||||||
|
<u-loading-icon></u-loading-icon>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showLoad: false,
|
||||||
|
// 指数/板块or个股
|
||||||
|
isPrivate: false,
|
||||||
|
list: [],
|
||||||
|
pagenum: 1,
|
||||||
|
last_page: null,
|
||||||
|
serchValue: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
this.rending()
|
||||||
|
},
|
||||||
|
onReachBottom() {
|
||||||
|
if (this.pagenum < this.last_page) {
|
||||||
|
this.getnewGoods()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
// 前往搜索页
|
||||||
|
// toSerch() {
|
||||||
|
// uni.navigateTo({
|
||||||
|
// url: `/pages/user/collectionSerch`,
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
submit() {
|
||||||
|
this.isPrivate ? this.privateRending() : this.rending()
|
||||||
|
},
|
||||||
|
selectNav(is) {
|
||||||
|
this.pagenum = 1
|
||||||
|
this.showLoad = true
|
||||||
|
this.isPrivate = is
|
||||||
|
this.serchValue = ''
|
||||||
|
if (is) {
|
||||||
|
this.privateRending()
|
||||||
|
} else {
|
||||||
|
this.rending()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 请求个股数据
|
||||||
|
privateRending() {
|
||||||
|
// /collect/query
|
||||||
|
this.$api
|
||||||
|
.post('/collect/query', {
|
||||||
|
businessDate: '',
|
||||||
|
keyWord: this.serchValue,
|
||||||
|
market: '',
|
||||||
|
pageModel: {
|
||||||
|
pageNo: this.pagenum,
|
||||||
|
pageSize: 20,
|
||||||
|
sortField: '',
|
||||||
|
sortWay: '',
|
||||||
|
},
|
||||||
|
plateOrA: 2,
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
this.list = r.list
|
||||||
|
console.log(this.list)
|
||||||
|
this.last_page = r.totalPage
|
||||||
|
this.showLoad = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
privateRending1() {
|
||||||
|
this.showLoad = false
|
||||||
|
// /collect/query
|
||||||
|
this.$api
|
||||||
|
.post('/collect/query', {
|
||||||
|
businessDate: '',
|
||||||
|
keyWord: this.serchValue,
|
||||||
|
market: '',
|
||||||
|
pageModel: {
|
||||||
|
pageNo: this.pagenum,
|
||||||
|
pageSize: 20,
|
||||||
|
sortField: '',
|
||||||
|
sortWay: '',
|
||||||
|
},
|
||||||
|
plateOrA: 2,
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
this.list = [...this.list, ...r.list]
|
||||||
|
this.last_page = r.totalPage
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
rending() {
|
||||||
|
this.$api
|
||||||
|
.post('/collect/query', {
|
||||||
|
businessDate: '',
|
||||||
|
keyWord: this.serchValue,
|
||||||
|
market: '',
|
||||||
|
pageModel: {
|
||||||
|
pageNo: this.pagenum,
|
||||||
|
pageSize: 20,
|
||||||
|
sortField: '',
|
||||||
|
sortWay: '',
|
||||||
|
},
|
||||||
|
plateOrA: 1,
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
this.list = r.list
|
||||||
|
this.last_page = r.totalPage
|
||||||
|
this.showLoad = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
rending1() {
|
||||||
|
this.$api
|
||||||
|
.post('/collect/query', {
|
||||||
|
businessDate: '',
|
||||||
|
keyWord: this.serchValue,
|
||||||
|
market: '',
|
||||||
|
pageModel: {
|
||||||
|
pageNo: this.pagenum,
|
||||||
|
pageSize: 20,
|
||||||
|
sortField: '',
|
||||||
|
sortWay: '',
|
||||||
|
},
|
||||||
|
plateOrA: 1,
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
this.list = [...this.list, ...r.list]
|
||||||
|
this.last_page = r.totalPage
|
||||||
|
this.showLoad = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 上拉加载
|
||||||
|
getnewGoods() {
|
||||||
|
this.pagenum = this.pagenum + 1
|
||||||
|
if (this.isPrivate) {
|
||||||
|
this.privateRending1()
|
||||||
|
} else {
|
||||||
|
this.rending1()
|
||||||
|
}
|
||||||
|
// 1.展示loading效果
|
||||||
|
uni.showLoading({
|
||||||
|
title: '数据加载中...',
|
||||||
|
})
|
||||||
|
// 2.开启节流阀
|
||||||
|
this.isLoading = true
|
||||||
|
// 3.发起网络请求
|
||||||
|
|
||||||
|
// 无论成功与否都会调用该方法
|
||||||
|
// 4.隐藏loading效果
|
||||||
|
uni.hideLoading()
|
||||||
|
// 5.关闭节流阀
|
||||||
|
this.isLoading = false
|
||||||
|
},
|
||||||
|
to(name, id) {
|
||||||
|
if (this.isPrivate) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/home/private/privateDetail?name=${name}&id=${id}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.center {
|
||||||
|
padding: 32rpx 24rpx;
|
||||||
|
}
|
||||||
|
.nav {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.nav text {
|
||||||
|
margin-right: 32rpx;
|
||||||
|
}
|
||||||
|
.nav_select {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #ec7c00;
|
||||||
|
}
|
||||||
|
.private_serch {
|
||||||
|
padding: 24rpx 32rpx;
|
||||||
|
border: 2rpx solid #dedede;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 24rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
color: #ec7c00;
|
||||||
|
.private_serch_ipt {
|
||||||
|
width: 400rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
.private_serch_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_table_head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: #fff9f3;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
justify-content: space-between;
|
||||||
|
.private_table_head_right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.private_table_head_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.private_head_name {
|
||||||
|
width: 200rpx;
|
||||||
|
}
|
||||||
|
.private_head_price {
|
||||||
|
width: 150rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.private_head_gains {
|
||||||
|
width: 150rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_table_list {
|
||||||
|
padding: 24rpx 24rpx;
|
||||||
|
border-bottom: 2rpx solid #f6f6f6;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 28rpx;
|
||||||
|
.private_table_list_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.table_list_name {
|
||||||
|
width: 200rpx;
|
||||||
|
}
|
||||||
|
.table_list_code {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
.table_list_price {
|
||||||
|
width: 150rpx;
|
||||||
|
text-align: center;
|
||||||
|
color: #d9001b;
|
||||||
|
}
|
||||||
|
.table_list_price1 {
|
||||||
|
color: #8fc320;
|
||||||
|
}
|
||||||
|
.table_list_gains {
|
||||||
|
width: 150rpx;
|
||||||
|
text-align: center;
|
||||||
|
color: #d9001b;
|
||||||
|
}
|
||||||
|
.table_list_gains1 {
|
||||||
|
color: #8fc320;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_screening_icon {
|
||||||
|
width: 18rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
margin-left: 24rpx;
|
||||||
|
}
|
||||||
|
.show_load {
|
||||||
|
margin-top: 100rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,242 @@
|
|||||||
|
<template>
|
||||||
|
<view class="collection_serch">
|
||||||
|
<view class="center">
|
||||||
|
<view class="serch">
|
||||||
|
<u-input
|
||||||
|
placeholder="请输入个股代码或名称"
|
||||||
|
v-model="serchValue"
|
||||||
|
prefixIcon="search"
|
||||||
|
prefixIconStyle="font-size: 22px;color: #909399"
|
||||||
|
shape="circle"
|
||||||
|
@input="change"
|
||||||
|
fontSize="24rpx"
|
||||||
|
></u-input>
|
||||||
|
<view class="serch_text">搜索</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="serch_list"
|
||||||
|
v-for="(item, index) in serchList"
|
||||||
|
:key="index"
|
||||||
|
@tap="detail(item)"
|
||||||
|
>
|
||||||
|
<image
|
||||||
|
src="../../static/serch.png"
|
||||||
|
mode="scaleToFill"
|
||||||
|
style="width: 26rpx; height: 26rpx"
|
||||||
|
/>
|
||||||
|
<view style="margin-left: 10rpx">{{ item.name }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="private_table_head" v-if="list.length > 0">
|
||||||
|
<view class="private_table_head_left">
|
||||||
|
<view class="private_head_name">股票名称</view>
|
||||||
|
<view class="private_head_price">价格</view>
|
||||||
|
<view class="private_head_gains">涨幅</view>
|
||||||
|
</view>
|
||||||
|
<view class="private_table_head_right" @tap="privateBelong">
|
||||||
|
<view>关注日期</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="private_table_list" v-for="(item, index) in list" :key="index">
|
||||||
|
<view class="private_table_list_left">
|
||||||
|
<view class="table_list_name">
|
||||||
|
<view>{{ item.name }}</view>
|
||||||
|
<view class="table_list_code">{{ item.code }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="table_list_price" :style="{ color: item.color }">
|
||||||
|
{{ item.price }}
|
||||||
|
</view>
|
||||||
|
<view class="table_list_gains" :style="{ color: item.color }">
|
||||||
|
{{ item.gains }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view>{{ item.suoshu }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
serchValue: '',
|
||||||
|
serchList: [],
|
||||||
|
list: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
change() {
|
||||||
|
this.list = []
|
||||||
|
// this.$api
|
||||||
|
// .post('/getHomeInfo', {
|
||||||
|
// keyword: this.serchValue,
|
||||||
|
// })
|
||||||
|
// .then(r => {
|
||||||
|
// if (r) {
|
||||||
|
this.serchList = [
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
if (this.serchValue.length == 0) {
|
||||||
|
this.serchList = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
detail(item) {
|
||||||
|
this.serchList = []
|
||||||
|
// this.$api.post('/getHomeInfo', {}).then(r => {
|
||||||
|
// if (r) {
|
||||||
|
this.list = [
|
||||||
|
{
|
||||||
|
code: '601633',
|
||||||
|
name: '长城汽车',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10.12%',
|
||||||
|
date: '2023-04-10',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: '601633',
|
||||||
|
name: '泰永长征',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10.12%',
|
||||||
|
date: '2023-04-10',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// this.list = r
|
||||||
|
// this.list.forEach(item => {})
|
||||||
|
// this.list = [...this.list]
|
||||||
|
// this.showLoad = false
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.center {
|
||||||
|
padding: 0 24rpx;
|
||||||
|
}
|
||||||
|
.serch {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.serch_text {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 120rpx;
|
||||||
|
height: 75rpx;
|
||||||
|
background-color: #ec7c00;
|
||||||
|
color: white;
|
||||||
|
border-radius: 36rpx;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 75rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
/deep/.u-border {
|
||||||
|
border-color: #ec7c00 !important;
|
||||||
|
}
|
||||||
|
.serch_list {
|
||||||
|
padding: 32rpx 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
border-bottom: 1px solid #cfcfcf;
|
||||||
|
}
|
||||||
|
.table_head {
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
background-color: #fff9f3;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 34rpx;
|
||||||
|
}
|
||||||
|
.table_head view {
|
||||||
|
width: 140rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.table_list {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 32rpx 24rpx 0 24rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
.table_list view {
|
||||||
|
text-align: center;
|
||||||
|
width: 140rpx;
|
||||||
|
}
|
||||||
|
.private_table_head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: #fff9f3;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
justify-content: space-between;
|
||||||
|
.private_table_head_right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.private_table_head_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.private_head_name {
|
||||||
|
width: 200rpx;
|
||||||
|
}
|
||||||
|
.private_head_price {
|
||||||
|
width: 150rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.private_head_gains {
|
||||||
|
width: 150rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_table_list {
|
||||||
|
padding: 24rpx 24rpx;
|
||||||
|
border-bottom: 2rpx solid #f6f6f6;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 28rpx;
|
||||||
|
.private_table_list_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.table_list_name {
|
||||||
|
width: 200rpx;
|
||||||
|
}
|
||||||
|
.table_list_code {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
.table_list_price {
|
||||||
|
width: 150rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.table_list_gains {
|
||||||
|
width: 150rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_screening_icon {
|
||||||
|
width: 18rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
margin-left: 24rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<view class="user_info">
|
||||||
|
<view class="center">
|
||||||
|
<u-input
|
||||||
|
placeholder="请输入名称"
|
||||||
|
border="bottom"
|
||||||
|
clearable
|
||||||
|
v-model="value"
|
||||||
|
></u-input>
|
||||||
|
<button class="btn" @tap="submit">保存</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
this.value = options.value
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
if (this.value) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '修改成功',
|
||||||
|
icon: 'success',
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack()
|
||||||
|
}, 1500)
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请输入名称',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.center {
|
||||||
|
padding: 40rpx 24rpx;
|
||||||
|
.btn {
|
||||||
|
height: 88rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 88rpx;
|
||||||
|
border: 0;
|
||||||
|
margin-top: 140rpx;
|
||||||
|
background-color: #ec7c00;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
<template>
|
||||||
|
<view class="edit_phone">
|
||||||
|
<view class="center">
|
||||||
|
<view class="ipt_box">
|
||||||
|
<u-input
|
||||||
|
placeholder="请输入新手机号"
|
||||||
|
v-model="phone"
|
||||||
|
border="bottom"
|
||||||
|
type="number"
|
||||||
|
fontSize="32rpx"
|
||||||
|
clearable
|
||||||
|
></u-input>
|
||||||
|
<view style="margin-top: 50rpx">
|
||||||
|
<u-input
|
||||||
|
border="bottom"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入验证码"
|
||||||
|
v-model="value"
|
||||||
|
fontSize="32rpx"
|
||||||
|
>
|
||||||
|
<template slot="suffix">
|
||||||
|
<u-code
|
||||||
|
ref="uCode"
|
||||||
|
@change="codeChange"
|
||||||
|
seconds="60"
|
||||||
|
changeText="Xs"
|
||||||
|
></u-code>
|
||||||
|
<u-button
|
||||||
|
@tap="getCode"
|
||||||
|
:text="tips"
|
||||||
|
class="custom-style"
|
||||||
|
:plain="true"
|
||||||
|
size="mini"
|
||||||
|
></u-button>
|
||||||
|
</template>
|
||||||
|
</u-input>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<button class="btn" @tap="submit">保存</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tips: '',
|
||||||
|
// 验证码
|
||||||
|
value: '',
|
||||||
|
// 手机号
|
||||||
|
phone: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 验证码
|
||||||
|
codeChange(text) {
|
||||||
|
this.tips = text
|
||||||
|
},
|
||||||
|
getCode() {
|
||||||
|
if (this.$refs.uCode.canGetCode) {
|
||||||
|
if (this.phone) {
|
||||||
|
// 模拟向后端请求验证码
|
||||||
|
uni.showLoading({
|
||||||
|
title: '正在获取验证码',
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
// 这里此提示会被this.start()方法中的提示覆盖
|
||||||
|
uni.$u.toast('验证码已发送')
|
||||||
|
// 通知验证码组件内部开始倒计时
|
||||||
|
this.$refs.uCode.start()
|
||||||
|
}, 2000)
|
||||||
|
this.$api.post('/login/sendSms', { phone: this.phone })
|
||||||
|
} else {
|
||||||
|
uni.$u.toast('请先输入手机号')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uni.$u.toast('倒计时结束后再发送')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
submit() {
|
||||||
|
if (this.phone && this.value) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '修改成功',
|
||||||
|
icon: 'success',
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack()
|
||||||
|
}, 1500)
|
||||||
|
} else {
|
||||||
|
uni.$u.toast('请先输入手机号和验证码')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.center {
|
||||||
|
padding: 60rpx 24rpx;
|
||||||
|
}
|
||||||
|
// .ipt_box {
|
||||||
|
// margin-top: 100rpx;
|
||||||
|
// }
|
||||||
|
.custom-style {
|
||||||
|
color: #6195fe;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
/deep/.u-button--plain.u-button--info {
|
||||||
|
color: #ec7c00 !important;
|
||||||
|
border: 0 !important;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
/deep/.u-input--no-radius {
|
||||||
|
padding: 24rpx 18rpx !important;
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
height: 88rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 88rpx;
|
||||||
|
border: 0;
|
||||||
|
margin-top: 140rpx;
|
||||||
|
background-color: #ec7c00;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
/deep/.u-button__text {
|
||||||
|
font-size: 28rpx !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,144 @@
|
|||||||
|
<template>
|
||||||
|
<view class="edit_user">
|
||||||
|
<view class="center">
|
||||||
|
<view class="avatar_box" @tap="editAvatar">
|
||||||
|
<view>头像</view>
|
||||||
|
<view class="avatar_right">
|
||||||
|
<image
|
||||||
|
src="https://upload-bbs.mihoyo.com/upload/2021/07/10/278678644/00185141732cb0f85adcf0ee68754554_8318448679343513426.jpg"
|
||||||
|
mode="scaleToFill"
|
||||||
|
class="avatar"
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
src="../../../static/right_all.png"
|
||||||
|
mode="scaleToFill"
|
||||||
|
style="width: 24rpx; height: 24rpx"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="name_phone">
|
||||||
|
<view class="list" @tap="to('editName', data.name)">
|
||||||
|
<view>用户昵称</view>
|
||||||
|
<view class="list_right">
|
||||||
|
<view style="color: #333333">微信用户</view>
|
||||||
|
<image
|
||||||
|
src="../../../static/right_all.png"
|
||||||
|
mode="scaleToFill"
|
||||||
|
style="width: 24rpx; height: 24rpx"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="list" @tap="to('editPhone')">
|
||||||
|
<view>手机号</view>
|
||||||
|
<view class="list_right">
|
||||||
|
<view style="color: #333333">130****8745</view>
|
||||||
|
<image
|
||||||
|
src="../../../static/right_all.png"
|
||||||
|
mode="scaleToFill"
|
||||||
|
style="width: 24rpx; height: 24rpx"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
avatar: '',
|
||||||
|
name: '微信用户微信用户微信用户',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
to(url, value) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/user/edit/${url}?value=${value}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 修改头像
|
||||||
|
editAvatar() {
|
||||||
|
let that = this
|
||||||
|
uni.chooseImage({
|
||||||
|
count: 1,
|
||||||
|
sourceType: ['album'],
|
||||||
|
sizeType: ['compressed'],
|
||||||
|
success(res) {
|
||||||
|
console.log(res)
|
||||||
|
res.tempFilePaths.forEach((item, index) => {
|
||||||
|
uni.uploadFile({
|
||||||
|
url: config.server + '/uploadGoods',
|
||||||
|
filePath: item,
|
||||||
|
name: 'file',
|
||||||
|
header: {
|
||||||
|
'ssdmn-token': cache.get('token'),
|
||||||
|
},
|
||||||
|
success: uploadFileRes => {
|
||||||
|
//接口返回的数据
|
||||||
|
let r = JSON.parse(uploadFileRes.data)
|
||||||
|
that.$set(that.data, 'avatar', r.data.file)
|
||||||
|
},
|
||||||
|
fail: err => {
|
||||||
|
console.log(err)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.center {
|
||||||
|
padding: 24rpx;
|
||||||
|
}
|
||||||
|
.avatar_box {
|
||||||
|
padding: 24rpx;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666666;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
.avatar_right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.avatar {
|
||||||
|
width: 70rpx;
|
||||||
|
height: 70rpx;
|
||||||
|
border-radius: 70rpx;
|
||||||
|
margin-right: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.name_phone {
|
||||||
|
padding: 24rpx 24rpx 0 24rpx;
|
||||||
|
background-color: white;
|
||||||
|
margin-top: 24rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
}
|
||||||
|
.list {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666666;
|
||||||
|
padding-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
.list_right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="scss">
|
||||||
|
page {
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,725 @@
|
|||||||
|
<template>
|
||||||
|
<u-loading-page v-if="load" :loading="load"></u-loading-page>
|
||||||
|
<view class="trade_stats" v-else>
|
||||||
|
<view class="head" :class="{ head1: totalEarnings < 0 }">
|
||||||
|
<view class="head_top">
|
||||||
|
<view class="all_breakeven">总盈亏</view>
|
||||||
|
<view class="all_money">{{ totalProfit }}</view>
|
||||||
|
<view>收益率 {{ totalEarnings.toFixed(2) }}%</view>
|
||||||
|
</view>
|
||||||
|
<view class="head_bottom">
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in nav"
|
||||||
|
:key="index"
|
||||||
|
class="head_bottom_list"
|
||||||
|
@tap="selectNav(item.name)"
|
||||||
|
>
|
||||||
|
<view :class="{ bottom_list: item.is }">{{ item.name }}</view>
|
||||||
|
<image
|
||||||
|
v-if="item.is"
|
||||||
|
src="../../static/collection_select.png"
|
||||||
|
class="select_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="main">
|
||||||
|
<view class="main_date" v-if="nav[3].is == true">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="main_date_ipt"
|
||||||
|
disabled
|
||||||
|
placeholder="请选择开始日期"
|
||||||
|
v-model="startDate"
|
||||||
|
@tap="selectStartDate"
|
||||||
|
/>
|
||||||
|
<u-icon name="minus" size="20" style="padding: 0 20rpx"></u-icon>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
disabled
|
||||||
|
class="main_date_ipt"
|
||||||
|
placeholder="请选择截至日期"
|
||||||
|
v-model="endDate"
|
||||||
|
@tap="selectEndDate"
|
||||||
|
/>
|
||||||
|
<view class="main_date_serch">搜索</view>
|
||||||
|
</view>
|
||||||
|
<view class="main_nav">
|
||||||
|
<view
|
||||||
|
:class="{ main_nav_color: isEarnings }"
|
||||||
|
@tap="selectMainNav(true)"
|
||||||
|
>
|
||||||
|
收益率
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
style="margin-left: 72rpx"
|
||||||
|
:class="{ main_nav_color: !isEarnings }"
|
||||||
|
@tap="selectMainNav(false)"
|
||||||
|
>
|
||||||
|
盈亏金额
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view style="height: 600rpx; width: 100%">
|
||||||
|
<l-echart ref="chart"></l-echart>
|
||||||
|
</view>
|
||||||
|
<view class="footer">
|
||||||
|
<view class="footer_nav" @tap="toAllBreakeven">
|
||||||
|
<view>个股盈亏统计</view>
|
||||||
|
<view class="footer_nav_right">
|
||||||
|
<view>查看所有</view>
|
||||||
|
<image
|
||||||
|
src="../../static/right_all.png"
|
||||||
|
class="right_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="private_table_head">
|
||||||
|
<view class="private_table_head_left">
|
||||||
|
<view class="private_head_name">股票代码</view>
|
||||||
|
<view class="private_head_price">买入时间</view>
|
||||||
|
<view class="private_head_gains">卖出时间</view>
|
||||||
|
</view>
|
||||||
|
<view class="private_table_head_right" @tap="privateBelong">
|
||||||
|
<view>盈亏金额</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="private_table_list"
|
||||||
|
v-for="(item, index) in list"
|
||||||
|
:key="index"
|
||||||
|
@tap="toBreakevenDetail(item.securityCode, item.securityName)"
|
||||||
|
>
|
||||||
|
<view class="private_table_list_left">
|
||||||
|
<view class="table_list_name">
|
||||||
|
<view>{{ item.securityName }}</view>
|
||||||
|
<view class="table_list_code">{{ item.securityCode }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="table_list_price">
|
||||||
|
{{ item.buyDate }}
|
||||||
|
</view>
|
||||||
|
<view class="table_list_gains">
|
||||||
|
{{ item.sellDate }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="table_list_totalMoney"
|
||||||
|
:class="{ table_list_gains1: item.totalMoney < 0 }"
|
||||||
|
>{{ item.totalMoney }}</view
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
<view class="see_more" v-if="list.length > 5" @tap="toAllBreakeven">
|
||||||
|
<view>查看更多</view>
|
||||||
|
<image
|
||||||
|
src="../../static/chakangengduo.png"
|
||||||
|
class="more_icon"
|
||||||
|
mode="scaleToFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 筛选/开始时间选择 -->
|
||||||
|
<u-datetime-picker
|
||||||
|
:show="startDateShow"
|
||||||
|
v-model="date"
|
||||||
|
mode="date"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="startDateShow = false"
|
||||||
|
@confirm="confirmStartDate"
|
||||||
|
></u-datetime-picker>
|
||||||
|
<!-- 筛选/结束时间选择 -->
|
||||||
|
<u-datetime-picker
|
||||||
|
:show="endDateShow"
|
||||||
|
v-model="date"
|
||||||
|
mode="date"
|
||||||
|
:minDate="minDate"
|
||||||
|
cancelColor="#EC7C00"
|
||||||
|
confirmColor="#EC7C00"
|
||||||
|
@cancel="endDateShow = false"
|
||||||
|
@confirm="confirmEndDate"
|
||||||
|
></u-datetime-picker>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from '@/uni_modules/lime-echart/static/echarts.min'
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 总盈亏
|
||||||
|
totalProfit: '',
|
||||||
|
// 总收益率
|
||||||
|
totalEarnings: '',
|
||||||
|
bgcColor: {
|
||||||
|
background: '',
|
||||||
|
},
|
||||||
|
nav: [
|
||||||
|
{
|
||||||
|
name: '本月',
|
||||||
|
is: true,
|
||||||
|
current: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '近三月',
|
||||||
|
is: false,
|
||||||
|
current: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '近一年',
|
||||||
|
is: false,
|
||||||
|
current: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '自定义',
|
||||||
|
is: false,
|
||||||
|
current: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
navIndex: 0,
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '2023-04-10',
|
||||||
|
gains: '2023-04-10',
|
||||||
|
suoshu: '5422.12万',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '长城汽车',
|
||||||
|
code: '601633',
|
||||||
|
price: '33.21',
|
||||||
|
gains: '10%',
|
||||||
|
suoshu: '电子设备制造',
|
||||||
|
color: '#D9001B',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 收益率or盈亏金额
|
||||||
|
isEarnings: true,
|
||||||
|
// 自定义开始日期
|
||||||
|
startDate: '',
|
||||||
|
// 选择开始时间
|
||||||
|
startDateShow: false,
|
||||||
|
// 自定义截至日期
|
||||||
|
endDate: '',
|
||||||
|
// 选择截至时间
|
||||||
|
endDateShow: false,
|
||||||
|
minDate: null,
|
||||||
|
load: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
this.rending()
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
// 执行echarts方法
|
||||||
|
this.initEchartsZ()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 选择导航
|
||||||
|
selectNav(name) {
|
||||||
|
this.nav.forEach(item => {
|
||||||
|
if (item.name == name) {
|
||||||
|
item.is = true
|
||||||
|
this.navIndex = item.current
|
||||||
|
this.$nextTick(() => {
|
||||||
|
// 执行echarts方法
|
||||||
|
this.initEchartsZ()
|
||||||
|
console.log('执行方法')
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
item.is = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 选择收益率or盈亏金额
|
||||||
|
selectMainNav(is) {
|
||||||
|
this.isEarnings = is
|
||||||
|
this.$nextTick(() => {
|
||||||
|
// 执行echarts方法
|
||||||
|
this.initEchartsZ()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 确定开始时间
|
||||||
|
confirmStartDate(e) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.minDate = new Date(e.value).getTime()
|
||||||
|
console.log(this.minDate)
|
||||||
|
this.startDate = this.getTime(e.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.startDateShow = false
|
||||||
|
},
|
||||||
|
// 确定截至时间
|
||||||
|
confirmEndDate(e) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.endDate = this.getTime(e.value)
|
||||||
|
})
|
||||||
|
this.endDateShow = false
|
||||||
|
},
|
||||||
|
// 选择开始时间
|
||||||
|
selectStartDate() {
|
||||||
|
if (!this.endDateShow) {
|
||||||
|
this.startDateShow = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 选择截至时间
|
||||||
|
selectEndDate() {
|
||||||
|
if (this.startDate) {
|
||||||
|
if (!this.startDateShow) {
|
||||||
|
this.endDateShow = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请先选择开始时间',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 跳转到所有个股盈亏
|
||||||
|
toAllBreakeven() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/user/allBreakeven',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 前往个股详情
|
||||||
|
toBreakevenDetail(id, name) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/user/breakevenDetail?id=${id}&name=${name}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 格式化时间戳
|
||||||
|
getTime(date) {
|
||||||
|
let time = new Date(date)
|
||||||
|
let y = time.getFullYear()
|
||||||
|
let m = time.getMonth() + 1
|
||||||
|
let d = time.getDate()
|
||||||
|
let h = time.getHours()
|
||||||
|
let mm = time.getMinutes()
|
||||||
|
let s = time.getSeconds()
|
||||||
|
if (s < 10) {
|
||||||
|
s = '0' + s
|
||||||
|
}
|
||||||
|
if (h < 10) {
|
||||||
|
h = '0' + h
|
||||||
|
}
|
||||||
|
if (mm < 10) {
|
||||||
|
mm = '0' + mm
|
||||||
|
}
|
||||||
|
if (m < 10) {
|
||||||
|
m = '0' + m
|
||||||
|
}
|
||||||
|
if (d < 10) {
|
||||||
|
d = '0' + d
|
||||||
|
}
|
||||||
|
return `${y}-${m}-${d}`
|
||||||
|
},
|
||||||
|
rending() {
|
||||||
|
this.$api
|
||||||
|
.post('/transaction/totalEarnings', {
|
||||||
|
securityCode: '',
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
// this.data = r
|
||||||
|
this.totalProfit = r.totalEarnings
|
||||||
|
this.totalEarnings = r.earningsRate
|
||||||
|
if (r.earningsRate > 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.setNavigationBarColor({
|
||||||
|
frontColor: '#ffffff',
|
||||||
|
backgroundColor: '#F93938',
|
||||||
|
})
|
||||||
|
}, 100)
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.setNavigationBarColor({
|
||||||
|
frontColor: '#ffffff',
|
||||||
|
backgroundColor: '#568EFF',
|
||||||
|
})
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
this.load = false
|
||||||
|
console.log(r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
this.$api
|
||||||
|
.post('/transaction/profitOrLoss', {
|
||||||
|
endDate: '',
|
||||||
|
keyWord: '',
|
||||||
|
pageModel: {
|
||||||
|
pageNo: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
sortField: '',
|
||||||
|
sortWay: '',
|
||||||
|
},
|
||||||
|
securityType: '',
|
||||||
|
sortAmount: 1,
|
||||||
|
startDate: '',
|
||||||
|
type: '',
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
// this.data = r
|
||||||
|
this.list = r.list
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
initEchartsZ() {
|
||||||
|
console.log('执行函数')
|
||||||
|
this.$api
|
||||||
|
.post('/transaction/earningsLine', {
|
||||||
|
endDate: this.endDate,
|
||||||
|
securityCode: '',
|
||||||
|
startDate: this.startDate,
|
||||||
|
timeType: this.navIndex,
|
||||||
|
type: this.isEarnings ? '1' : '2',
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r) {
|
||||||
|
console.log(r)
|
||||||
|
// this.data = r
|
||||||
|
this.$refs.chart.init(
|
||||||
|
echarts,
|
||||||
|
chart => {
|
||||||
|
let option = {
|
||||||
|
// tooltip: {
|
||||||
|
// trigger: 'axis',
|
||||||
|
// position: function (pt) {
|
||||||
|
// return [pt[0], '10%']
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// title: {
|
||||||
|
// left: 'center',
|
||||||
|
// text: 'Large Ara Chart',
|
||||||
|
// },
|
||||||
|
toolbox: {
|
||||||
|
feature: {
|
||||||
|
dataZoom: {
|
||||||
|
yAxisIndex: 'none',
|
||||||
|
},
|
||||||
|
restore: {},
|
||||||
|
saveAsImage: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'time',
|
||||||
|
boundaryGap: false,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
boundaryGap: [0, '100%'],
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
show: false,
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '数据',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'none',
|
||||||
|
// 背景色
|
||||||
|
// areaStyle: {},
|
||||||
|
data: r,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
chart.setOption(option)
|
||||||
|
},
|
||||||
|
{ locale: 'ZH' }
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log(r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(fall => {
|
||||||
|
console.log(fall)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.head {
|
||||||
|
height: 300rpx;
|
||||||
|
color: white;
|
||||||
|
background: linear-gradient(180deg, #f93938 0%, #fa4c0d 100%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.head_top {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 28rpx;
|
||||||
|
.all_breakeven {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
.all_money {
|
||||||
|
margin: 20rpx 0;
|
||||||
|
font-size: 60rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.head_bottom {
|
||||||
|
padding: 0 24rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
.head_bottom_list {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
margin-right: 64rpx;
|
||||||
|
padding-bottom: 24rpx;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.bottom_list {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.select_icon {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 24rpx;
|
||||||
|
height: 14rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.head1 {
|
||||||
|
background: linear-gradient(180deg, #568eff 0%, #4479eb 100%);
|
||||||
|
}
|
||||||
|
.main {
|
||||||
|
margin-top: 32rpx;
|
||||||
|
.main_date {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
.main_date_ipt {
|
||||||
|
width: 170rpx;
|
||||||
|
padding: 10rpx 46rpx;
|
||||||
|
border: 2rpx solid #999999;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
height: 36rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.main_date_serch {
|
||||||
|
color: #ec7c00;
|
||||||
|
margin-left: 32rpx;
|
||||||
|
}
|
||||||
|
/deep/.u-icon--right {
|
||||||
|
padding: 0 16rpx !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.main_nav {
|
||||||
|
margin-top: 32rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
.main_nav_color {
|
||||||
|
color: #ec7c00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.line {
|
||||||
|
height: 20rpx;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
.footer_nav {
|
||||||
|
padding: 32rpx 24rpx;
|
||||||
|
display: flex;
|
||||||
|
font-weight: bold;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
.footer_nav_right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.right_icon {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
margin-left: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_table_head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: #fff9f3;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
justify-content: space-between;
|
||||||
|
.private_table_head_right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.private_table_head_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.private_head_name {
|
||||||
|
width: 150rpx;
|
||||||
|
}
|
||||||
|
.private_head_price {
|
||||||
|
width: 180rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.private_head_gains {
|
||||||
|
width: 180rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_table_list {
|
||||||
|
padding: 24rpx 24rpx;
|
||||||
|
border-bottom: 2rpx solid #f6f6f6;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 28rpx;
|
||||||
|
.private_table_list_left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.table_list_name {
|
||||||
|
width: 150rpx;
|
||||||
|
}
|
||||||
|
.table_list_code {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
.table_list_price {
|
||||||
|
width: 180rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table_list_gains {
|
||||||
|
width: 180rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.private_screening_icon {
|
||||||
|
width: 18rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
margin-left: 24rpx;
|
||||||
|
}
|
||||||
|
.see_more {
|
||||||
|
padding: 32rpx 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #ec7c00;
|
||||||
|
}
|
||||||
|
.more_icon {
|
||||||
|
width: 22rpx;
|
||||||
|
height: 22rpx;
|
||||||
|
margin-left: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.table_list_totalMoney {
|
||||||
|
color: #d9001b;
|
||||||
|
}
|
||||||
|
.table_list_gains1 {
|
||||||
|
color: #6fbb60;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue