forked from fanruan/demo-tabledata-redis
39 changed files with 10581 additions and 5 deletions
@ -0,0 +1,5 @@
|
||||
types |
||||
node_modules/ |
||||
dist/ |
||||
assets/ |
||||
webpack/ |
@ -0,0 +1,236 @@
|
||||
{ |
||||
"env": { |
||||
"browser": true, |
||||
"node": true, |
||||
"es6": true, |
||||
"jest/globals": true |
||||
}, |
||||
"globals": { |
||||
"Fix": true, |
||||
"BI": true, |
||||
"Report": true, |
||||
"Dec": true, |
||||
"DecCst": true |
||||
}, |
||||
"parser": "@typescript-eslint/parser", |
||||
"parserOptions": { |
||||
"project": "./tsconfig.json", |
||||
"ecmaVersion": 6, |
||||
"sourceType": "module", |
||||
"ecmaFeatures": { |
||||
"modules": true |
||||
} |
||||
}, |
||||
"plugins": ["@typescript-eslint", "jest"], |
||||
"rules": { |
||||
"no-underscore-dangle": 0, |
||||
"prefer-rest-params": 0, |
||||
"no-plusplus": 0, |
||||
"max-len": 0, |
||||
"no-use-before-define": [ |
||||
"error", |
||||
{ |
||||
"functions": false |
||||
} |
||||
], |
||||
"new-cap": [ |
||||
"error", |
||||
{ |
||||
"properties": false |
||||
} |
||||
], |
||||
"no-confusing-arrow": ["off"], |
||||
"no-unused-vars": "off", |
||||
"@typescript-eslint/no-unused-vars": [ |
||||
"error", |
||||
{ |
||||
"vars": "all", |
||||
"args": "none", |
||||
"ignoreRestSiblings": true |
||||
} |
||||
], |
||||
"no-irregular-whitespace": [ |
||||
"error", |
||||
{ |
||||
"skipTemplates": false, |
||||
"skipStrings": false |
||||
} |
||||
], |
||||
// 声明 |
||||
"prefer-const": "error", //如果一个变量不会被重新赋值,最好使用const进行声明 |
||||
"no-const-assign": "error", //不允许改变用const声明的变量 |
||||
"no-var": "error", //用let/const代替var |
||||
// 对象 |
||||
"no-dupe-keys": "error", // 禁止在对象字面量中出现重复的键 |
||||
"no-prototype-builtins": "error", // 禁止直接使用 Object.prototypes 的内置属性 |
||||
"no-extend-native": "error", // 禁止扩展原生对象 |
||||
"no-new-object": "error", // 禁止使用 Object 构造函数 |
||||
"object-shorthand": ["error", "always"], //要求对象字面量简写语法 |
||||
"quote-props": ["error", "as-needed"], // 对象属性只在需要的时候加引号 |
||||
// 数组 |
||||
"no-sparse-arrays": "error", // 禁用稀疏数组 |
||||
"no-array-constructor": "error", //禁止使用 Array 构造函数 |
||||
"array-callback-return": "error", // 数组回调函数内必须返回一个状态 |
||||
// 字符串 |
||||
"quotes": [ |
||||
"error", |
||||
"single", |
||||
{ |
||||
"allowTemplateLiterals": true |
||||
} |
||||
], // 字符串开头和结束使用单引号 |
||||
"prefer-template": "error", // 使用模板而非字符串连接 |
||||
"template-curly-spacing": ["error", "never"], // 强制模板字符串中花括号内不能出现空格 |
||||
"no-path-concat": "error", // 当使用 _dirname 和 _filename 时不允许字符串拼接 |
||||
"no-useless-concat": "error", // 禁止没有必要的字符拼接 |
||||
"no-useless-escape": "off", // 禁用不必要的转义 |
||||
// 函数 |
||||
"no-dupe-args": "error", // 禁止在 function 定义中出现重复的参数 |
||||
// "no-new-func": "error", // 禁用Function构造函数 |
||||
"no-return-assign": "error", // 禁止在返回语句中赋值 |
||||
"func-style": [ |
||||
"error", |
||||
"declaration", |
||||
{ |
||||
"allowArrowFunctions": true |
||||
} |
||||
], // 统一函数风格为函数表达式或函数声明,并且允许使用箭头函数 |
||||
"newline-before-return": "error", // 要求 return 语句之前有一空行 |
||||
"wrap-iife": ["error", "outside"], // 立即执行函数外部必须包裹括号 |
||||
"no-loop-func": "error", // 禁止在非function内声明function |
||||
// "space-before-function-paren": "error", // 函数括号前必须要有空格 |
||||
"no-param-reassign": "error", // 禁止修改函数参数 |
||||
"prefer-spread": "error", // 使用解构形式代替.apply() |
||||
// 箭头函数 |
||||
"prefer-arrow-callback": "error", // 回调使用箭头函数 |
||||
"arrow-spacing": "error", // 箭头前后要有空格 |
||||
"arrow-parens": ["error", "as-needed"], // 参数使用括号包裹 |
||||
"arrow-body-style": [ |
||||
"error", |
||||
"as-needed", |
||||
{ |
||||
"requireReturnForObjectLiteral": true |
||||
} |
||||
], // 函数体在必要的时候使用大括号 |
||||
// 类 & 构造器 |
||||
"no-useless-constructor": "error", // 禁止没必要的构造器 |
||||
"no-dupe-class-members": "error", // 禁止重复创建类成员 |
||||
// 模块 |
||||
"no-duplicate-imports": "error", // 禁止从一个模块多次import |
||||
// 迭代器 & 生成器 |
||||
"no-iterator": "error", // 禁止使用Iterator属性 |
||||
"no-restricted-syntax": "error", // 使用对应的数组/对象方法去迭代操作成员 |
||||
"generator-star-spacing": "error", // *前后不要都有空格 |
||||
// 属性 |
||||
"dot-notation": "error", //强制在任何允许的时候使用点号访问属性 |
||||
// 变量 |
||||
"no-undef": "error", // 禁止使用未声明的变量 |
||||
// "one-var": ["error", "never"], // 变量统一声明 |
||||
// 比较运算符 & 相等运算符 |
||||
"eqeqeq": "error", // 使用 === 和 !== 代替 == 和 != |
||||
"no-case-declarations": "error", // 禁止在 case 或 default 子句中出现变量声明 |
||||
// "no-nested-ternary": "error", // 禁止混合的三目运算符 |
||||
"no-unneeded-ternary": "error", //禁止可以在有更简单的可替代的表达式时使用三元操作符 |
||||
// 条件 |
||||
"no-cond-assign": "error", // 禁止在条件语句中出现赋值操作符 |
||||
"no-constant-condition": "error", //禁止在条件中使用常量表达式 |
||||
"no-duplicate-case": "error", // 禁止在 switch 语句中的 case 子句中出现重复的测试表达式 |
||||
"default-case": "error", // 要求 Switch 语句中有 Default 分支 |
||||
"no-else-return": "error", // 如果 if 块中包含了一个 return 语句,else 块就成了多余的了。可以将其内容移至块外 |
||||
"no-fallthrough": "error", // 禁止 case 语句落空 |
||||
// 代码块 |
||||
"brace-style": "error", // 代码块左括号紧跟上一行结束 |
||||
"curly": ["error", "multi-line"], // if、else if、else、for、while强制使用大括号,但允许在单行中省略大括号 |
||||
"no-empty": [ |
||||
"error", |
||||
{ |
||||
"allowEmptyCatch": true |
||||
} |
||||
], // 禁止空块语句,但允许出现空的 catch 子句 |
||||
// 注释 |
||||
"spaced-comment": "error", // 注释前有空格 |
||||
"lines-around-comment": [ |
||||
"error", |
||||
{ |
||||
"beforeBlockComment": false |
||||
} |
||||
], // 块级注释前要有空行 |
||||
// 空白 |
||||
"indent": [ |
||||
"error", |
||||
4, |
||||
{ |
||||
"SwitchCase": 1 |
||||
} |
||||
], // 缩进控制4空格 |
||||
"no-mixed-spaces-and-tabs": "error", // 禁止使用 空格 和 tab 混合缩进 |
||||
"space-before-blocks": ["error", "always"], // 语句块之前的需要有空格 |
||||
"keyword-spacing": "error", // 关键字后面必须要有空格 |
||||
"space-infix-ops": [ |
||||
"error", |
||||
{ |
||||
"int32Hint": false |
||||
} |
||||
], // 要求中缀操作符周围有空格,设置 int32Hint 选项为 true (默认 false) 允许 a|0 不带空格 |
||||
"eol-last": "error", // 要求文件末尾保留一行空行 |
||||
"newline-per-chained-call": "error", // 要求方法链中每个调用都有一个换行符 |
||||
"padded-blocks": ["error", "never"], // 代码块开始和结束位置不可以有多余的空行 |
||||
"space-in-parens": ["error", "never"], // 禁止圆括号内的空格 |
||||
"array-bracket-spacing": ["error", "never"], // 数组紧贴括号部分不允许包含空格 |
||||
"object-curly-spacing": ["error", "always"], // 对象紧贴花括号部分不允许包含空格 |
||||
"no-regex-spaces": "error", // 禁止正则表达式字面量中出现多个空格 |
||||
"no-multi-spaces": "error", // 禁止出现多个空格而且不是用来作缩进的 |
||||
"block-spacing": ["error", "never"], // 单行代码块中紧贴括号部分不允许包含空格 |
||||
"computed-property-spacing": ["error", "never"], // 禁止括号和其内部值之间的空格 |
||||
"no-trailing-spaces": [ |
||||
"error", |
||||
{ |
||||
"skipBlankLines": true |
||||
} |
||||
], // 禁用行尾空格 |
||||
"no-spaced-func": "error", // 禁止函数调用时,与圆括号之间有空格 |
||||
"space-unary-ops": "error", // 要求或禁止在一元操作符之前或之后存在空格,new、delete、typeof、void、yield要求有空格,-、+、--、++、!、!!要求无空格 |
||||
"yield-star-spacing": [ |
||||
"error", |
||||
{ |
||||
"before": true, |
||||
"after": false |
||||
} |
||||
], // 强制 yield* 表达式中 * 号前有空格,后无空格 |
||||
// 逗号 |
||||
"comma-style": "error", // 逗号必须放在行末 |
||||
"comma-dangle": ["error", "always-multiline"], // 多行对象字面量中要求拖尾逗号 |
||||
"comma-spacing": [ |
||||
"error", |
||||
{ |
||||
"before": false, |
||||
"after": true |
||||
} |
||||
], //在变量声明、数组字面量、对象字面量、函数参数 和 序列中禁止在逗号前使用空格,要求在逗号后使用一个或多个空格 |
||||
// 分号 |
||||
"semi": "error", //不得省略语句结束的分号 |
||||
"semi-spacing": [ |
||||
"error", |
||||
{ |
||||
"before": false, |
||||
"after": true |
||||
} |
||||
], //禁止分号周围的空格 |
||||
"no-extra-semi": "error", // 禁用不必要的分号 |
||||
// 类型转换 |
||||
"radix": "error", // 在parseInt()中始终使用基数以消除意想不到的后果 |
||||
"no-extra-boolean-cast": "error", // 禁止不必要的布尔类型转换 |
||||
// 其他最佳实践或规范 |
||||
"strict": "error", // 使用强制模式开关use strict; |
||||
// 禁止冗余的括号 |
||||
"@typescript-eslint/no-extra-parens": ["error"], |
||||
"no-eval": "error", // 禁用 eval() |
||||
"no-with": "error", // 禁用 with 语句 |
||||
"no-unexpected-multiline": "error", // 禁止使用令人困惑的多行表达式 |
||||
"no-unreachable": "error", // 禁止在 return、throw、continue 和 break 语句后出现不可达代码 |
||||
"no-unsafe-finally": "error", // 禁止在 finally 语句块中出现控制流语句 |
||||
"valid-typeof": "error", // 强制 typeof 表达式与有效的字符串进行比较 |
||||
"no-new-wrappers": "error", // 禁止通过 new 操作符使用 String、Number 和 Boolean |
||||
"handle-callback-err": "error" // 强制回调错误处理 |
||||
} |
||||
} |
@ -0,0 +1,10 @@
|
||||
/node_modules |
||||
/bower_components |
||||
.idea/ |
||||
.DS_Store |
||||
/constants/classes/ |
||||
dist |
||||
package-lock.json |
||||
.vscode |
||||
yarn-error.log |
||||
.history/ |
@ -0,0 +1,29 @@
|
||||
module.exports = function(api) { |
||||
api.cache(true); |
||||
const presets = [ |
||||
[ |
||||
'@babel/preset-env', |
||||
{ |
||||
targets: { |
||||
ie: 9, |
||||
chrome: 47, |
||||
}, |
||||
}, |
||||
], |
||||
'@babel/preset-typescript', |
||||
]; |
||||
const plugins = [ |
||||
[ |
||||
'@babel/plugin-proposal-decorators', |
||||
{ |
||||
legacy: true, |
||||
}, |
||||
], |
||||
'@babel/plugin-proposal-class-properties', |
||||
]; |
||||
|
||||
return { |
||||
presets, |
||||
plugins, |
||||
}; |
||||
}; |
@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8" /> |
||||
<title>Fine Report</title> |
||||
<!--核心css文件--> |
||||
<link |
||||
rel="preload" |
||||
href="./node_modules/fineui/dist/font/iconfont.woff" |
||||
as="font" |
||||
type="font/woff" |
||||
crossorigin="" |
||||
/> |
||||
<link |
||||
rel="stylesheet" |
||||
type="text/css" |
||||
href="./node_modules/fineui/dist/fineui.css" |
||||
/> |
||||
</head> |
||||
<script> |
||||
var DecCst = null; |
||||
var Dec = null; |
||||
</script> |
||||
|
||||
<body id="body"> |
||||
<div id="wrapper"></div> |
||||
<script src="./node_modules/fineui/dist/fineui.js"></script> |
||||
<script src="./node_modules/fineui/i18n/i18n.cn.js"></script> |
||||
</body> |
||||
</html> |
@ -0,0 +1,26 @@
|
||||
const { pathsToModuleNameMapper } = require('ts-jest/utils'); |
||||
// In the following statement, replace `./tsconfig` with the path to your `tsconfig` file
|
||||
// which contains the path mapping (ie the `compilerOptions.paths` option):
|
||||
const { jsonc } = require('jsonc'); |
||||
const { readFileSync } = require('fs'); |
||||
|
||||
const { compilerOptions } = jsonc.parse(readFileSync('./tsconfig.json', { encoding: 'utf8' })); |
||||
|
||||
module.exports = { |
||||
transform: { |
||||
'^.+\\.tsx?$': 'ts-jest', |
||||
}, |
||||
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$', |
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], |
||||
testEnvironment: './config/jest.environment.js', |
||||
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { |
||||
prefix: '<rootDir>', |
||||
}), |
||||
globals: { |
||||
'ts-jest': { |
||||
diagnostics: { |
||||
ignoreCodes: ['TS151001'], |
||||
}, |
||||
}, |
||||
}, |
||||
}; |
@ -0,0 +1,21 @@
|
||||
const { resolve } = require('path'); |
||||
const { writeFileSync } = require('fs'); |
||||
const propertiesReader = require('properties-reader'); |
||||
|
||||
compileZhCN2TS(); |
||||
|
||||
/** |
||||
* 转换中文的i18n,写到private里 |
||||
*/ |
||||
function compileZhCN2TS() { |
||||
let content = ''; |
||||
|
||||
const properties = propertiesReader(resolve(__dirname, '../../../main/resources/com/fr/plugin/db/redis/locale/redis_zh_CN.properties')); |
||||
properties.each((key, value) => { |
||||
content += ` '${key}': '${unescape(value.replace(/\\u/g, '%u'))}',\n`; |
||||
}); |
||||
|
||||
writeFileSync(resolve(__dirname, '../../private/i18n.ts'), `export const i18n = {
|
||||
${content}}; |
||||
`);
|
||||
} |
@ -0,0 +1,29 @@
|
||||
const spawn = require('child_process').spawn; |
||||
|
||||
const repositories = ['fineui', 'fineui-materials']; |
||||
|
||||
update(repositories); |
||||
|
||||
function update(repositories, index = 0) { |
||||
if (index === repositories.length) { |
||||
return; |
||||
} |
||||
|
||||
const repository = repositories[index]; |
||||
|
||||
const ls = spawn(`yarn`, ['upgrade', `${repository}`]); |
||||
|
||||
ls.stdout.on('data', data => { |
||||
console.log(data.toString()); |
||||
}); |
||||
|
||||
ls.stderr.on('data', data => { |
||||
console.log(data.toString()); |
||||
}); |
||||
|
||||
ls.on('exit', code => { |
||||
console.log(`${repository} child process exited with code `, code.toString()); |
||||
|
||||
update(repositories, index + 1); |
||||
}); |
||||
} |
@ -0,0 +1,75 @@
|
||||
{ |
||||
"name": "demo-tabledate-redis-web", |
||||
"version": "1.0.0", |
||||
"description": "", |
||||
"main": "src/index.ts", |
||||
"repository": "", |
||||
"author": "alan", |
||||
"license": "MIT", |
||||
"dependencies": { |
||||
"@types/jss": "9.5.8", |
||||
"autoprefixer": "^9.6.1", |
||||
"es6-promise": "4.2.6", |
||||
"fork-ts-checker-webpack-plugin": "^1.5.1", |
||||
"jss": "9.8.7", |
||||
"jss-plugin-global": "10.0.0-alpha.7", |
||||
"jss-plugin-nested": "10.0.0-alpha.7", |
||||
"nprogress": "0.2.0", |
||||
"optimize-css-assets-webpack-plugin": "^5.0.3" |
||||
}, |
||||
"devDependencies": { |
||||
"@babel/core": "7.4.5", |
||||
"@babel/plugin-proposal-class-properties": "^7.5.0", |
||||
"@babel/plugin-proposal-decorators": "7.4.4", |
||||
"@babel/polyfill": "7.4.4", |
||||
"@babel/preset-env": "7.4.5", |
||||
"@babel/preset-typescript": "7.3.3", |
||||
"@types/jest": "24.0.11", |
||||
"@typescript-eslint/eslint-plugin": "1.7.0", |
||||
"@typescript-eslint/parser": "1.7.0", |
||||
"axios": "0.18.0", |
||||
"babel-loader": "8.0.6", |
||||
"body-parser": "1.18.3", |
||||
"chokidar": "2.1.5", |
||||
"cross-env": "5.2.0", |
||||
"css-loader": "3.0.0", |
||||
"eslint": "5.16.0", |
||||
"eslint-plugin-jest": "22.4.1", |
||||
"express": "4.16.4", |
||||
"html-webpack-plugin": "3.2.0", |
||||
"http-proxy": "1.17.0", |
||||
"husky": "1.3.1", |
||||
"jest": "24.7.1", |
||||
"jest-environment-jsdom": "24.7.1", |
||||
"jsdom": "15.0.0", |
||||
"jsonc": "1.1.0", |
||||
"less": "3.9.0", |
||||
"less-loader": "5.0.0", |
||||
"mini-css-extract-plugin": "0.7.0", |
||||
"postcss-loader": "3.0.0", |
||||
"postcss-simple-vars": "5.0.2", |
||||
"properties-reader": "0.0.16", |
||||
"source-map-loader": "0.2.4", |
||||
"style-loader": "0.23.1", |
||||
"ts-jest": "24.0.2", |
||||
"typescript": "3.5.1", |
||||
"webpack": "4.35.2", |
||||
"webpack-cli": "3.3.5", |
||||
"webpack-dev-server": "3.7.2", |
||||
"webpack-merge": "4.2.1" |
||||
}, |
||||
"optionalDependencies": { |
||||
"fineui": "git+ssh://git@cloud.finedevelop.com:7999/visual/fineui.git", |
||||
"fineui-materials": "git+ssh://git@cloud.finedevelop.com:7999/fui/fineui-materials.git#release/10.0" |
||||
}, |
||||
"scripts": { |
||||
"dev": "cross-env NODE_ENV=mock webpack-dev-server -p --progress --config=webpack/webpack.dev.js --mode development --open", |
||||
"build": "webpack -p --progress --config=webpack/webpack.prod.js --mode production", |
||||
"eslint": "eslint './*.js' './**/*.js' './**/*.ts'", |
||||
"eslint-fix": "eslint './*.js' './**/*.js' './**/*.ts' --fix", |
||||
"const": "javac -encoding UTF-8 -d constants/classes constants/*.java && java -cp constants/classes FRConstantsWriter", |
||||
"i18n": "node ./lib/i18n/i18n.js", |
||||
"test": "jest --passWithNoTests", |
||||
"upgrade": "node lib/upgrade" |
||||
} |
||||
} |
@ -0,0 +1,44 @@
|
||||
export const i18n = { |
||||
'Plugin-Redis_Host': '数据库地址', |
||||
'Plugin-Redis_Port': '端口', |
||||
'Plugin-Redis_Password': '密码', |
||||
'Plugin-Redis_Table_Data': 'Redis数据集', |
||||
'Plugin-Redis_Script_Table_Data': 'Redis程序数据集', |
||||
'Plugin-Redis_Query_Condition': '查询条件', |
||||
'Plugin-Redis_DB': 'RedisDB数据集', |
||||
'Plugin-Redis_Help': '帮助文档', |
||||
'Plugin-Redis_Keys_Pattern': '键值的正则表达式', |
||||
'Plugin-Redis_Keys_Pattern_Search': '搜索', |
||||
'Plugin-Redis_DB_Index': '数据库编号', |
||||
'Plugin-Redis_Preview': '预览', |
||||
'Plugin-Redis_Refresh': '刷新', |
||||
'Plugin-Redis_Formula': '公式', |
||||
'Plugin-Redis_Query': '查询', |
||||
'Plugin-Redis_Connection_Successfully': '连接成功', |
||||
'Plugin-Redis_Connection_Failed': '连接失败', |
||||
'Plugin-Redis_Script_Text_Description': '可以使用JavaScript脚本来转换查询结果。\n注意1:内置参数$content和$column,分别表示原始的数据集内容(二维数组)和数据集的列名(一维数组)。如果返回一个二维数组,表示仅修改数据集内容,不修改数据集的列名;如果返回一个如下格式的对象:\n{content:[][],column:[]},表示同时修改了数据集的内容和列名。\n注意2:如果希望引用外部JS,可以在脚本框中写入file:///$ENV_HOME/transform.js,其中$ENV_HOME表示当前工作目录。', |
||||
'Plugin-Redis_Pool_Max_Total': '最大连接数', |
||||
'Plugin-Redis_Script_Query_Text': '查询脚本', |
||||
'Plugin-Redis_Pool_Max_Wait': '最大等待时间', |
||||
'Plugin-Redis_Pool_Config': '连接池配置', |
||||
'Plugin-Redis_Proxy_Config': '跳板服务器设置', |
||||
'Plugin-Redis_Proxy_Open': '启用跳板服务器', |
||||
'Plugin-Redis_Proxy_Host': '主机地址', |
||||
'Plugin-Redis_Proxy_Port': '端口', |
||||
'Plugin-Redis_Proxy_Username': '用户名', |
||||
'Plugin-Redis_Proxy_Password': '密码', |
||||
'Plugin-Redis_Proxy_Private_Key_Path': '秘钥文件', |
||||
'Plugin-Redis_Proxy_Private_Key_Tip': 'PEM格式的秘钥文件路径', |
||||
'Plugin-Redis_Pool_Block_When_Exhausted': '连接耗尽时阻塞', |
||||
'Plugin-Redis_Pool_Lifo': '后进先出', |
||||
'Plugin-Redis_Pool_Max_Idle': '最大空闲连接数', |
||||
'Plugin-Redis_Pool_Timeout': '超时时间', |
||||
'Plugin-Redis_Proxy_Description': '跳板服务器连接仅支持ssh协议(注意:启用跳板服务器将无法使用连接池)', |
||||
'Plugin-Redis_Connect_Cluster_Description': '连接集群配置说明', |
||||
'Plugin-Redis_Cluster_Config_Description': '1、使用逗号分割集群中的多个数据库地址和端口,必须要求地址和端口数量一致;\n2、所有redis节点的密码必须相同;\n3、连接集群的时候无法使用跳板机。', |
||||
'Plugin-Redis_Script_Engine_Type': '脚本引擎', |
||||
'Plugin-Redis_Script_Engine_Type_Default': '默认', |
||||
'Plugin-Redis_Script_Engine_Type_V8': 'V8高速引擎', |
||||
'Plugin-Redis_Proxy_Private_Key_Path_Mark': '请输入服务器文件路径', |
||||
'Plugin-Redis_Connection_Form_OriginalCharsetName': '编码', |
||||
}; |
@ -1 +1,11 @@
|
||||
# 前端代码 |
||||
### 数据连接 |
||||
安装完插件后,在新建数据连接的地方可以看到Redis类型的数据连接: |
||||
![1](screenshots/1.png) |
||||
### 新建数据集 |
||||
点击新建数据连接即可看到新建Redis的表单: |
||||
![1](screenshots/2.png) |
||||
按照要求输入即可。 |
||||
### 查看数据连接 |
||||
数据连接新建完成后,点击数据连接的名称查看该数据连接: |
||||
![1](screenshots/3.png) |
After Width: | Height: | Size: 734 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 86 KiB |
@ -0,0 +1,3 @@
|
||||
import { i18n } from '../private/i18n'; |
||||
|
||||
BI.addI18n(i18n); |
@ -0,0 +1,6 @@
|
||||
import { RedisEdit } from './modules/app.edit'; |
||||
|
||||
BI.createWidget({ |
||||
type: RedisEdit.xtype, |
||||
element: '#wrapper', |
||||
}); |
@ -0,0 +1,6 @@
|
||||
import { RedisShow } from './modules/app.show'; |
||||
|
||||
BI.createWidget({ |
||||
type: RedisShow.xtype, |
||||
element: '#wrapper', |
||||
}); |
@ -0,0 +1,16 @@
|
||||
import { RedisShow } from './modules/app.show'; |
||||
import { RedisEdit } from './modules/app.edit'; |
||||
|
||||
const ConstantRedisType = 'dec.constant.database.conf.connect.types'; |
||||
const ConstantRedisShow = 'dec.constant.database.conf.connect.form.Redis.show'; |
||||
const ConstantRedisEdit = 'dec.constant.database.conf.connect.form.Redis.edit'; |
||||
|
||||
BI.DOM.ready(() => { |
||||
BI.config(ConstantRedisType, datas => [...datas, { |
||||
text: 'Redis', |
||||
databaseType: 'Redis', |
||||
iconUrl: 'com/fr/plugin/db/redis/images/redis.png', |
||||
}]); |
||||
BI.constant(ConstantRedisShow, RedisShow.xtype); |
||||
BI.constant(ConstantRedisEdit, RedisEdit.xtype); |
||||
}); |
@ -0,0 +1,183 @@
|
||||
import { shortcut } from '@core/core'; |
||||
import { POOL_CONFIG, PROXY_CONFIG, BASIC_CONFIG, CONNECT_CHARSET } from '@constants/constant'; |
||||
import { Vertical, TextEditor, TextValueCombo, Left, TextButton, BarPopOver, Editor } from 'ui'; |
||||
import { FormItem } from './components/form_item/form_item'; |
||||
import { PoolEdit } from './components/pool/pool_edit'; |
||||
import { ProxyEdit } from './components/proxy/proxy_edit'; |
||||
@shortcut() |
||||
export class RedisEdit extends BI.Widget { |
||||
static xtype = 'dec.dcm.connection.plugin.redis.edit'; |
||||
props = { |
||||
formData: { |
||||
basicConfig : { |
||||
...BASIC_CONFIG, |
||||
}, |
||||
poolConfig: { |
||||
...POOL_CONFIG, |
||||
}, |
||||
proxyConfig: { |
||||
...PROXY_CONFIG, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
host: any; |
||||
port: any; |
||||
password: any; |
||||
originalCharsetName: any; |
||||
poolConfig: PoolEdit; |
||||
proxyConfig: ProxyEdit; |
||||
|
||||
poolConfigData = POOL_CONFIG; |
||||
proxyConfigData = PROXY_CONFIG; |
||||
oldPassword = ''; |
||||
|
||||
render() { |
||||
const formData = BI.get(this.options, 'formData'); |
||||
const basicConfig = BI.get(formData, 'basicConfig', BASIC_CONFIG); |
||||
const poolConfig = BI.get(formData, 'poolConfig', POOL_CONFIG); |
||||
const proxyConfig = BI.get(formData, 'proxyConfig', PROXY_CONFIG); |
||||
const { host, port, password, originalCharsetName } = basicConfig; |
||||
this.poolConfigData = poolConfig; |
||||
this.proxyConfigData = proxyConfig; |
||||
this.oldPassword = password; |
||||
|
||||
return { |
||||
type: Vertical, |
||||
hgap: 15, |
||||
vgap: 10, |
||||
items: [ |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Host'), |
||||
forms: [{ |
||||
type: TextEditor, |
||||
width: 300, |
||||
allowBlank: true, |
||||
ref: _ref => { |
||||
this.host = _ref; |
||||
}, |
||||
value: host, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Port'), |
||||
forms: [{ |
||||
type: TextEditor, |
||||
width: 300, |
||||
allowBlank: true, |
||||
ref: _ref => { |
||||
this.port = _ref; |
||||
}, |
||||
value: port, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Password'), |
||||
forms: [{ |
||||
type: Editor, |
||||
cls: 'bi-border', |
||||
height: 20, |
||||
width: 300, |
||||
allowBlank: true, |
||||
ref: _ref => { |
||||
this.password = _ref; |
||||
}, |
||||
value: password, |
||||
inputType: 'password', |
||||
}], |
||||
}, |
||||
{ |
||||
type: Left, |
||||
hgap: 20, |
||||
items: [ |
||||
{ |
||||
type: TextButton, |
||||
cls: 'bi-high-light', |
||||
text: BI.i18nText('Plugin-Redis_Pool_Config'), |
||||
handler: () => { |
||||
const id = BI.UUID(); |
||||
BI.Popovers.create(id, { |
||||
type: BarPopOver, |
||||
width: 500, |
||||
height: 320, |
||||
header: BI.i18nText('Plugin-Redis_Pool_Config'), |
||||
body: { |
||||
type: PoolEdit.xtype, |
||||
poolConfig: this.poolConfigData, |
||||
ref: (_ref: any) => { |
||||
this.poolConfig = _ref; |
||||
}, |
||||
}, |
||||
listeners: [{ |
||||
eventName: BI.Popover.EVENT_CONFIRM, |
||||
action: () => { |
||||
this.poolConfigData = this.poolConfig.getSubmitValue(); |
||||
}, |
||||
}], |
||||
}).open(id); |
||||
}, |
||||
}, |
||||
{ |
||||
type: TextButton, |
||||
cls: 'bi-high-light', |
||||
text: BI.i18nText('Plugin-Redis_Proxy_Config'), |
||||
handler: () => { |
||||
const id = BI.UUID(); |
||||
BI.Popovers.create(id, { |
||||
type: BarPopOver, |
||||
width: 650, |
||||
height: 320, |
||||
header: BI.i18nText('Plugin-Redis_Pool_Config'), |
||||
body: { |
||||
type: ProxyEdit.xtype, |
||||
proxyConfig: this.proxyConfigData, |
||||
ref: (_ref: any) => { |
||||
this.proxyConfig = _ref; |
||||
}, |
||||
}, |
||||
listeners: [{ |
||||
eventName: BI.Popover.EVENT_CONFIRM, |
||||
action: () => { |
||||
this.proxyConfigData = this.proxyConfig.getSubmitValue(); |
||||
}, |
||||
}], |
||||
}).open(id); |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Connection_Form_OriginalCharsetName'), |
||||
forms: [{ |
||||
type: TextValueCombo, |
||||
width: 300, |
||||
value: originalCharsetName ? originalCharsetName : '', |
||||
items: CONNECT_CHARSET, |
||||
ref: (_ref: any) => { |
||||
this.originalCharsetName = _ref; |
||||
}, |
||||
}], |
||||
}, |
||||
], |
||||
}; |
||||
} |
||||
|
||||
public getSubmitValue() { |
||||
const originalCharsetName = this.originalCharsetName.getValue()[0] || '' |
||||
return { |
||||
basicConfig: { |
||||
host: this.host.getValue(), |
||||
port: this.port.getValue(), |
||||
password: this.oldPassword === this.password.getValue() ? this.oldPassword : BI.encode(this.password.getValue()), |
||||
newCharsetName: originalCharsetName ? 'gbk' : '', |
||||
originalCharsetName, |
||||
}, |
||||
poolConfig: this.poolConfigData, |
||||
proxyConfig: this.proxyConfigData, |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,115 @@
|
||||
import { shortcut } from '@core/core'; |
||||
import { Label, Vertical, Left, TextButton, BarPopOver } from 'ui'; |
||||
import { FormItem } from './components/form_item/form_item'; |
||||
import { PoolConfig } from './components/pool/pool'; |
||||
import { ProxyConfig } from './components/proxy/proxy'; |
||||
import { POOL_CONFIG, PROXY_CONFIG, BASIC_CONFIG } from '@constants/constant'; |
||||
|
||||
@shortcut() |
||||
export class RedisShow extends BI.Widget { |
||||
static xtype = 'dec.dcm.connection.plugin.redis.show' |
||||
props = { |
||||
formData: { |
||||
basicConfig : { |
||||
...BASIC_CONFIG, |
||||
}, |
||||
poolConfig: { |
||||
...POOL_CONFIG, |
||||
}, |
||||
proxyConfig: { |
||||
...PROXY_CONFIG, |
||||
}, |
||||
}, |
||||
} |
||||
render() { |
||||
const formData = BI.get(this.options, 'formData'); |
||||
const basicConfig = BI.get(formData, 'basicConfig', BASIC_CONFIG); |
||||
const poolConfig = BI.get(formData, 'poolConfig', POOL_CONFIG); |
||||
const proxyConfig = BI.get(formData, 'proxyConfig', PROXY_CONFIG); |
||||
const { host, port, newCharsetName } = basicConfig; |
||||
|
||||
return { |
||||
type: Vertical, |
||||
hgap: 15, |
||||
vgap: 10, |
||||
items: [ |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Host'), |
||||
forms: [{ |
||||
type: Label, |
||||
text: host, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Port'), |
||||
forms: [{ |
||||
type: Label, |
||||
text: port, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Password'), |
||||
forms: [{ |
||||
type: Label, |
||||
text: '*****', |
||||
}], |
||||
}, |
||||
{ |
||||
type: Left, |
||||
hgap: 20, |
||||
items: [ |
||||
{ |
||||
type: TextButton, |
||||
cls: 'bi-high-light', |
||||
text: BI.i18nText('Plugin-Redis_Pool_Config'), |
||||
handler: () => { |
||||
const id = BI.UUID(); |
||||
BI.Popovers.create(id, { |
||||
type: BarPopOver, |
||||
width: 400, |
||||
height: 300, |
||||
header: BI.i18nText('Plugin-Redis_Pool_Config'), |
||||
body: { |
||||
type: PoolConfig.xtype, |
||||
poolConfig, |
||||
}, |
||||
footer: {}, |
||||
}).open(id); |
||||
}, |
||||
}, |
||||
{ |
||||
type: TextButton, |
||||
cls: 'bi-high-light', |
||||
text: BI.i18nText('Plugin-Redis_Proxy_Config'), |
||||
handler: () => { |
||||
const id = BI.UUID(); |
||||
BI.Popovers.create(id, { |
||||
type: BarPopOver, |
||||
width: 650, |
||||
height: 280, |
||||
header: BI.i18nText('Plugin-Redis_Pool_Config'), |
||||
body: { |
||||
type: ProxyConfig.xtype, |
||||
proxyConfig, |
||||
}, |
||||
footer: {}, |
||||
}).open(id); |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Connection_Form_OriginalCharsetName'), |
||||
forms: [{ |
||||
type: Label, |
||||
text: newCharsetName ? newCharsetName : BI.i18nText('BI-Basic_Auto'), |
||||
}], |
||||
}, |
||||
], |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,29 @@
|
||||
import { shortcut } from '@core/core'; |
||||
import { Label, Left } from 'ui'; |
||||
|
||||
@shortcut() |
||||
export class FormItem extends BI.Widget { |
||||
static xtype = 'dec.dcm.connection.plugin.redis.components.form_item' |
||||
|
||||
props = { |
||||
name: '', |
||||
forms: '', |
||||
nameWidth: 100, |
||||
} |
||||
|
||||
render () { |
||||
return { |
||||
type: Left, |
||||
items: [ |
||||
{ |
||||
type: Label, |
||||
cls: 'bi-font-bold', |
||||
width: this.options.nameWidth, |
||||
textAlign: 'left', |
||||
text: `${this.options.name}:`, |
||||
}, |
||||
...this.options.forms, |
||||
], |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,74 @@
|
||||
import { shortcut } from '@core/core'; |
||||
import { Label, Vertical, MultiSelectItem } from 'ui'; |
||||
import { POOL_CONFIG } from '@constants/constant'; |
||||
import { FormItem } from '../form_item/form_item'; |
||||
@shortcut() |
||||
export class PoolConfig extends BI.Widget { |
||||
static xtype = 'dec.dcm.connection.plugin.redis.components.pool' |
||||
|
||||
props = { |
||||
poolConfig : { |
||||
...POOL_CONFIG, |
||||
}, |
||||
} |
||||
|
||||
render() { |
||||
const { maxTotal, maxWait, maxIdle, blockWhenExhausted, lifo, timeout } = this.options.poolConfig; |
||||
|
||||
return { |
||||
type: Vertical, |
||||
hgap: 15, |
||||
vgap: 10, |
||||
items: [ |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Pool_Max_Total'), |
||||
forms: [{ |
||||
type: Label, |
||||
text: maxTotal, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Pool_Max_Wait'), |
||||
forms: [{ |
||||
type: Label, |
||||
text: maxWait, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Pool_Max_Idle'), |
||||
forms: [{ |
||||
type: Label, |
||||
text: maxIdle, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Pool_Block_When_Exhausted'), |
||||
forms: [{ |
||||
type: MultiSelectItem, |
||||
selected: blockWhenExhausted, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Pool_Lifo'), |
||||
forms: [{ |
||||
type: MultiSelectItem, |
||||
selected: lifo, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Pool_Timeout'), |
||||
forms: [{ |
||||
type: Label, |
||||
text: timeout, |
||||
}], |
||||
}, |
||||
], |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,20 @@
|
||||
import { model, Model } from '@core/core'; |
||||
@model() |
||||
export class PoolEditModel extends Model { |
||||
static xtype = 'dec.dcm.model.connection.plugin.redis.components.pool_edit' |
||||
state() { |
||||
return { |
||||
blockWhenExhausted: true, |
||||
lifo: true, |
||||
}; |
||||
} |
||||
|
||||
actions = { |
||||
setBlockWhenExhausted:(blockWhenExhausted: boolean) => { |
||||
this.model.blockWhenExhausted = blockWhenExhausted; |
||||
}, |
||||
setLifo: (lifo: boolean) => { |
||||
this.model.lifo = lifo; |
||||
}, |
||||
} |
||||
} |
@ -0,0 +1,134 @@
|
||||
import { shortcut, store } from '@core/core'; |
||||
import { Vertical, MultiSelectItem, TextEditor } from 'ui'; |
||||
import { POOL_CONFIG } from '@constants/constant'; |
||||
import { FormItem } from '../form_item/form_item'; |
||||
import { PoolEditModel } from './pool_edit.model'; |
||||
|
||||
@shortcut() |
||||
@store(PoolEditModel) |
||||
export class PoolEdit extends BI.Widget { |
||||
static xtype = 'dec.dcm.connection.plugin.redis.components.pool_edit' |
||||
|
||||
props = { |
||||
poolConfig : { |
||||
...POOL_CONFIG, |
||||
}, |
||||
} |
||||
|
||||
store: PoolEditModel['store'] |
||||
model: PoolEditModel['model'] |
||||
|
||||
form = { |
||||
maxTotal: null, |
||||
maxWait: null, |
||||
maxIdle: null, |
||||
blockWhenExhausted: null, |
||||
lifo: null, |
||||
timeout: null, |
||||
} |
||||
|
||||
render() { |
||||
const { maxTotal, maxWait, maxIdle, blockWhenExhausted, lifo, timeout } = this.options.poolConfig; |
||||
this.store.setBlockWhenExhausted(blockWhenExhausted); |
||||
this.store.setLifo(lifo); |
||||
|
||||
return { |
||||
type: Vertical, |
||||
hgap: 15, |
||||
vgap: 10, |
||||
items: [ |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Pool_Max_Total'), |
||||
forms: [{ |
||||
type: TextEditor, |
||||
width: 300, |
||||
allowBlank: true, |
||||
value: maxTotal, |
||||
ref: (_ref: any) => { |
||||
this.form.maxTotal = _ref; |
||||
}, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Pool_Max_Wait'), |
||||
forms: [{ |
||||
type: TextEditor, |
||||
width: 300, |
||||
allowBlank: true, |
||||
value: maxWait, |
||||
ref: (_ref: any) => { |
||||
this.form.maxWait = _ref; |
||||
}, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Pool_Max_Idle'), |
||||
forms: [{ |
||||
type: TextEditor, |
||||
width: 300, |
||||
allowBlank: true, |
||||
value: maxIdle, |
||||
ref: (_ref: any) => { |
||||
this.form.maxIdle = _ref; |
||||
}, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Pool_Block_When_Exhausted'), |
||||
forms: [{ |
||||
type: MultiSelectItem, |
||||
selected: blockWhenExhausted, |
||||
ref: (_ref: any) => { |
||||
this.form.blockWhenExhausted = _ref; |
||||
}, |
||||
handler: () => { |
||||
this.store.setBlockWhenExhausted(!this.model.blockWhenExhausted); |
||||
}, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Pool_Lifo'), |
||||
forms: [{ |
||||
type: MultiSelectItem, |
||||
selected: lifo, |
||||
ref: (_ref: any) => { |
||||
this.form.lifo = _ref; |
||||
}, |
||||
handler: () => { |
||||
this.store.setLifo(!this.model.lifo); |
||||
}, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Pool_Timeout'), |
||||
forms: [{ |
||||
type: TextEditor, |
||||
width: 300, |
||||
allowBlank: true, |
||||
value: timeout, |
||||
ref: (_ref: any) => { |
||||
this.form.timeout = _ref; |
||||
}, |
||||
}], |
||||
}, |
||||
], |
||||
}; |
||||
} |
||||
|
||||
public getSubmitValue() { |
||||
return { |
||||
maxTotal: this.form.maxTotal.getValue(), |
||||
maxWait: this.form.maxWait.getValue(), |
||||
maxIdle: this.form.maxIdle.getValue(), |
||||
blockWhenExhausted: this.model.blockWhenExhausted, |
||||
lifo: this.model.lifo, |
||||
timeout: this.form.timeout.getValue(), |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,83 @@
|
||||
import { shortcut } from '@core/core'; |
||||
import { Label, Vertical, MultiSelectItem, CenterAdapt } from 'ui'; |
||||
import { PROXY_CONFIG } from '@constants/constant'; |
||||
import { FormItem } from '../form_item/form_item'; |
||||
@shortcut() |
||||
export class ProxyConfig extends BI.Widget { |
||||
static xtype = 'dec.dcm.connection.plugin.redis.components.proxy' |
||||
|
||||
props = { |
||||
proxyConfig : { |
||||
...PROXY_CONFIG, |
||||
}, |
||||
} |
||||
|
||||
render() { |
||||
const { open, host, port, username, password, privateKeyPath } = this.options.proxyConfig; |
||||
|
||||
return { |
||||
type: Vertical, |
||||
hgap: 15, |
||||
vgap: 10, |
||||
items: [ |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Proxy_Open'), |
||||
forms: [{ |
||||
type: CenterAdapt, |
||||
items: [ |
||||
{ |
||||
type: MultiSelectItem, |
||||
width: 30, |
||||
selected: open, |
||||
}, { |
||||
type: Label, |
||||
text: BI.i18nText('Plugin-Redis_Proxy_Description'), |
||||
}, |
||||
], |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Proxy_Host'), |
||||
forms: [{ |
||||
type: Label, |
||||
text: host, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Proxy_Port'), |
||||
forms: [{ |
||||
type: Label, |
||||
text: port, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Proxy_Username'), |
||||
forms: [{ |
||||
type: Label, |
||||
text: username, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Proxy_Password'), |
||||
forms: [{ |
||||
type: Label, |
||||
text: password, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Proxy_Private_Key_Path'), |
||||
forms: [{ |
||||
type: Label, |
||||
text: privateKeyPath, |
||||
}], |
||||
}, |
||||
], |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,142 @@
|
||||
import { shortcut } from '@core/core'; |
||||
import { Label, Vertical, MultiSelectItem, CenterAdapt, TextEditor, Editor } from 'ui'; |
||||
import { PROXY_CONFIG } from '@constants/constant'; |
||||
import { FormItem } from '../form_item/form_item'; |
||||
@shortcut() |
||||
export class ProxyEdit extends BI.Widget { |
||||
static xtype = 'dec.dcm.connection.plugin.redis.components.proxy_edit' |
||||
|
||||
props = { |
||||
proxyConfig : { |
||||
...PROXY_CONFIG, |
||||
}, |
||||
} |
||||
|
||||
isOpen = true; |
||||
|
||||
form = { |
||||
host: null, |
||||
port: null, |
||||
username: null, |
||||
password: null, |
||||
privateKeyPath: null, |
||||
} |
||||
|
||||
oldPassword = ''; |
||||
|
||||
render() { |
||||
const { open, host, port, username, password, privateKeyPath } = this.options.proxyConfig; |
||||
this.isOpen = open; |
||||
this.oldPassword = password; |
||||
|
||||
return { |
||||
type: Vertical, |
||||
hgap: 15, |
||||
vgap: 10, |
||||
items: [ |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Proxy_Open'), |
||||
forms: [{ |
||||
type: CenterAdapt, |
||||
items: [ |
||||
{ |
||||
type: MultiSelectItem, |
||||
width: 30, |
||||
selected: this.isOpen, |
||||
handler: () => { |
||||
this.isOpen = !this.isOpen; |
||||
}, |
||||
}, { |
||||
type: Label, |
||||
text: BI.i18nText('Plugin-Redis_Proxy_Description'), |
||||
}, |
||||
], |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Proxy_Host'), |
||||
forms: [{ |
||||
type: TextEditor, |
||||
width: 300, |
||||
allowBlank: true, |
||||
value: host, |
||||
ref: (_ref: any) => { |
||||
this.form.host = _ref; |
||||
}, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Proxy_Port'), |
||||
forms: [{ |
||||
type: TextEditor, |
||||
width: 300, |
||||
allowBlank: true, |
||||
value: port, |
||||
ref: (_ref: any) => { |
||||
this.form.port = _ref; |
||||
}, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Proxy_Username'), |
||||
forms: [{ |
||||
type: TextEditor, |
||||
width: 300, |
||||
allowBlank: true, |
||||
value: username, |
||||
ref: (_ref: any) => { |
||||
this.form.username = _ref; |
||||
}, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Proxy_Password'), |
||||
forms: [{ |
||||
type: Editor, |
||||
cls: 'bi-border', |
||||
width: 300, |
||||
height: 20, |
||||
allowBlank: true, |
||||
value: password, |
||||
inputType: 'password', |
||||
ref: (_ref: any) => { |
||||
this.form.password = _ref; |
||||
}, |
||||
}], |
||||
}, |
||||
{ |
||||
type: FormItem.xtype, |
||||
name: BI.i18nText('Plugin-Redis_Proxy_Private_Key_Path'), |
||||
forms: [ |
||||
{ |
||||
type: TextEditor, |
||||
width: 300, |
||||
allowBlank: true, |
||||
value: privateKeyPath, |
||||
watermark: BI.i18nText('Plugin-Redis_Proxy_Private_Key_Path_Mark'), |
||||
ref: (_ref: any) => { |
||||
this.form.privateKeyPath = _ref; |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
], |
||||
}; |
||||
} |
||||
|
||||
public getSubmitValue() { |
||||
return { |
||||
open: this.isOpen, |
||||
host: this.form.host.getValue(), |
||||
port: this.form.port.getValue(), |
||||
username: this.form.username.getValue(), |
||||
password: this.oldPassword === this.form.password.getValue() ? this.oldPassword : BI.encode(this.form.password.getValue()), |
||||
privateKeyPath: this.form.privateKeyPath.getValue(), |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,63 @@
|
||||
export const POOL_CONFIG = { |
||||
maxTotal: 10, |
||||
maxWait : -1, |
||||
maxIdle : 10, |
||||
blockWhenExhausted: true, |
||||
lifo: true, |
||||
timeout: 100000, |
||||
}; |
||||
|
||||
export const PROXY_CONFIG = { |
||||
open: false, |
||||
host: '', |
||||
port: 22, |
||||
username: '', |
||||
password: '', |
||||
privateKeyPath: '', |
||||
}; |
||||
|
||||
export const BASIC_CONFIG = { |
||||
host: '', |
||||
port: '6379', |
||||
password: '', |
||||
originalCharsetName: '', |
||||
}; |
||||
|
||||
export const CONNECT_CHARSET = [ |
||||
{ |
||||
text: BI.i18nText('BI-Basic_Auto'), |
||||
value: '', |
||||
}, |
||||
{ |
||||
text: 'GBK', |
||||
value: 'GBK', |
||||
}, |
||||
{ |
||||
text: 'BIG5', |
||||
value: 'BIG5', |
||||
}, |
||||
{ |
||||
text: 'ISO-8859-1', |
||||
value: 'ISO-8859-1', |
||||
}, |
||||
{ |
||||
text: 'UTF-8', |
||||
value: 'UTF-8', |
||||
}, |
||||
{ |
||||
text: 'UTF-16', |
||||
value: 'UTF-16', |
||||
}, |
||||
{ |
||||
text: 'EUC_JP', |
||||
value: 'EUC_JP', |
||||
}, |
||||
{ |
||||
text: 'EUC_KR', |
||||
value: 'EUC_KR', |
||||
}, |
||||
{ |
||||
text: 'CP850', |
||||
value: 'CP850', |
||||
}, |
||||
]; |
@ -0,0 +1,85 @@
|
||||
|
||||
export type Constructor<T> = new(...args: any[]) => T; |
||||
|
||||
/** |
||||
* 注册widget |
||||
*/ |
||||
export function shortcut() { |
||||
return function decorator<U>(Target: Constructor<U> & {xtype: string}): void { |
||||
BI.shortcut(Target.xtype, Target); |
||||
}; |
||||
} |
||||
|
||||
/** |
||||
* 注册model |
||||
*/ |
||||
export function model() { |
||||
return function decorator<U extends {new(...args:any[]):{}} & {xtype: string, context?: ReadonlyArray<string>}>(Target: U): void { |
||||
BI.model(Target.xtype, Target); |
||||
}; |
||||
} |
||||
|
||||
/** |
||||
* 类注册_store属性 |
||||
* @param Model model类 |
||||
* @param opts 额外条件 |
||||
*/ |
||||
export function store<T>(Model?: Constructor<T> & {xtype: string}, opts: { props?(this: unknown): { [key: string]: unknown } } = {}) { |
||||
return function classDecorator<U extends {new(...args:any[]):{}}>(constructor:U) { |
||||
return class extends constructor { |
||||
_store() { |
||||
const props = opts.props ? opts.props.apply(this) : undefined; |
||||
|
||||
return BI.Models.getModel(Model.xtype, props); |
||||
} |
||||
}; |
||||
}; |
||||
} |
||||
|
||||
/** |
||||
* Model基类 |
||||
*/ |
||||
export class Model<U extends {types?: {[key: string]: unknown} | {}, context?: ReadonlyArray<string>} = {}> extends Fix.Model { |
||||
// @ts-ignore this['computed'][key]为空
|
||||
model: Pick<{[key in keyof U['types']]: U['types'][key]}, U['context'][number]> & {[key in keyof ReturnType<this['state']>]: ReturnType<this['state']>[key]} & {[key in keyof this['computed']]: ReturnType<this['computed'][key]>}; |
||||
|
||||
store: this['actions']; |
||||
|
||||
state(): {[key: string]: unknown} | {} { |
||||
return {}; |
||||
} |
||||
|
||||
context: U['context']; |
||||
|
||||
actions:{[key: string]: (...args: any[]) => any}; |
||||
|
||||
childContext: ReadonlyArray<keyof (this['computed'] & ReturnType<this['state']>)>; |
||||
|
||||
// @ts-ignore this['computed'][key]为空
|
||||
TYPE: Pick<{[key in keyof this['computed']]: ReturnType<this['computed'][key]>} & {[key in keyof ReturnType<this['state']>]: ReturnType<this['state']>[key]}, this['childContext'][number]>; |
||||
|
||||
computed: {[key: string]: () => unknown} | {}; |
||||
} |
||||
|
||||
// union to intersection of functions
|
||||
type UnionToIoF<U> = |
||||
(U extends any ? (k: (x: U) => void) => void : never) extends |
||||
((k: infer I) => void) ? I : never |
||||
|
||||
// return last element from Union
|
||||
type UnionPop<U> = UnionToIoF<U> extends { (a: infer A): void; } ? A : never; |
||||
|
||||
// prepend an element to a tuple.
|
||||
type Prepend<U, T extends ReadonlyArray<any>> = |
||||
((a: U, ...r: T) => void) extends (...r: infer R) => void ? R : never; |
||||
|
||||
type UnionToTupleRecursively<Union, Result extends ReadonlyArray<any>> = { |
||||
1: Result; |
||||
0: UnionToTupleRecursively_<Union, UnionPop<Union>, Result>; |
||||
// 0: UnionToTupleRecursively<Exclude<Union, UnionPop<Union>>, Prepend<UnionPop<Union>, Result>>
|
||||
}[[Union] extends [never] ? 1 : 0]; |
||||
|
||||
type UnionToTupleRecursively_<Union, Element, Result extends ReadonlyArray<any>> = |
||||
UnionToTupleRecursively<Exclude<Union, Element>, Prepend<Element, Result>>; |
||||
|
||||
type UnionToTuple<U> = UnionToTupleRecursively<U, []>; |
@ -0,0 +1,75 @@
|
||||
export const Icon = 'bi.icon'; |
||||
export const IconTextItem = 'bi.icon_text_item'; |
||||
export const IconTextIconItem = 'bi.icon_text_icon_item'; |
||||
export const IconButton = 'bi.icon_button'; |
||||
export const IconChangeButton = 'bi.icon_change_button'; |
||||
export const TextButton = 'bi.text_button'; |
||||
export const DownListCombo = 'bi.down_list_combo'; |
||||
export const Label = 'bi.label'; |
||||
export const SmallTextEditor = 'bi.small_text_editor'; |
||||
export const MultiFileEditor = 'bi.multifile_editor'; |
||||
export const SignEditor = 'bi.sign_editor'; |
||||
export const Button = 'bi.button'; |
||||
export const TextEditor = 'bi.text_editor'; |
||||
export const MultiSelectInsertCombo = 'bi.multi_select_insert_combo'; |
||||
export const MultiSelectCombo = 'bi.multi_select_combo'; |
||||
export const ButtonGroup = 'bi.button_group'; |
||||
export const AllValueChooserCombo = 'bi.all_value_chooser_combo'; |
||||
export const TextAreaEditor = 'bi.textarea_editor'; |
||||
export const MultiSelectItem = 'bi.multi_select_item'; |
||||
export const BarPopOver = 'bi.bar_popover'; |
||||
export const DynamicDateCombo = 'bi.dynamic_date_combo'; |
||||
export const DynamicDateTimeCombo = 'bi.dynamic_date_time_combo'; |
||||
export const MultiTreeCombo = 'bi.multi_tree_combo'; |
||||
export const RichEditor = 'bi.rich_editor'; |
||||
export const NicEditor = 'bi.nic_editor'; |
||||
export const Editor = 'bi.editor'; |
||||
export const MultiTreePopupView = 'bi.multi_tree_popup_view'; |
||||
export const SingleSelectRadioItem = 'bi.single_select_radio_item'; |
||||
export const SingleSelectInsertCombo = 'bi.single_select_insert_combo'; |
||||
export const SingleSelectCombo = 'bi.single_select_combo'; |
||||
export const Tab = 'bi.tab'; |
||||
export const DynamicYearMonthCombo = 'bi.dynamic_year_month_combo'; |
||||
export const Text = 'bi.text'; |
||||
export const Combo = 'bi.combo'; |
||||
export const TimeCombo = 'bi.time_combo'; |
||||
export const IFrame = 'bi.iframe'; |
||||
export const MultiTreeInsertCombo = 'bi.multi_tree_insert_combo'; |
||||
export const MultiTreeListCombo = 'bi.multi_tree_list_combo'; |
||||
export const MultilayerSingleTreeCombo = 'bi.multilayer_single_tree_combo'; |
||||
export const MultilayerSelectTreeCombo = 'bi.multilayer_select_tree_combo'; |
||||
export const AsyncTree = 'bi.async_tree'; |
||||
export const ListAsyncTree = 'bi.list_async_tree'; |
||||
export const MultilayerSingleTreePopup = 'bi.multilayer_single_tree_popup'; |
||||
export const MultilayerSelectTreePopup = 'bi.multilayer_select_tree_popup'; |
||||
export const IconLabel = 'bi.icon_label'; |
||||
export const Radio = 'bi.radio'; |
||||
export const LinearSegment = 'bi.linear_segment'; |
||||
export const SearchEditor = 'bi.search_editor'; |
||||
export const Img = 'bi.img'; |
||||
export const BubbleCombo = 'bi.bubble_combo'; |
||||
export const TextBubblePopupBarView = 'bi.text_bubble_bar_popup_view'; |
||||
export const TextValueCombo = 'bi.text_value_combo'; |
||||
export const File = 'bi.file'; |
||||
|
||||
|
||||
// 布局
|
||||
export const VerticalAdapt = 'bi.vertical_adapt'; |
||||
export const Vtape = 'bi.vtape'; |
||||
export const CenterAdapt = 'bi.center_adapt'; |
||||
export const Htape = 'bi.htape'; |
||||
export const Layout = 'bi.layout'; |
||||
export const Absolute = 'bi.absolute'; |
||||
export const Vertical = 'bi.vertical'; |
||||
export const Left = 'bi.left'; |
||||
export const Right = 'bi.right'; |
||||
export const HorizontalAdapt = 'bi.horizontal_adapt'; |
||||
export const AbsoluteCenterAdapt = 'bi.absolute_center_adapt'; |
||||
export const TableAdapt = 'bi.table_adapt'; |
||||
export const RightVerticalAdapt = 'bi.right_vertical_adapt'; |
||||
export const LeftRightVerticalAdapt = 'bi.left_right_vertical_adapt'; |
||||
export const ListView = 'bi.list_view'; |
||||
export const VirtualGroup = 'bi.virtual_group'; |
||||
export const HorizotalAuto = 'bi.horizontal_auto'; |
||||
export const Horizotal = 'bi.horizontal'; |
||||
export const FloatCenter = 'bi.float_center'; |
@ -0,0 +1,37 @@
|
||||
{ |
||||
"compilerOptions": { |
||||
"sourceMap": true, |
||||
"target": "es2017", |
||||
"module": "es2015", |
||||
"moduleResolution": "node", |
||||
"lib": [ |
||||
"es2017", |
||||
"dom" |
||||
], |
||||
"declaration": true, |
||||
"experimentalDecorators": true, |
||||
"outDir": "./bin", |
||||
"baseUrl": ".", |
||||
// "strict": true, |
||||
// "strictNullChecks": true, |
||||
// "noImplicitAny": true, |
||||
// "noUnusedLocals": true, |
||||
// "noUnusedParameters": true, |
||||
// "noImplicitReturns": true, |
||||
"noFallthroughCasesInSwitch": true, |
||||
"paths": { |
||||
"ui": ["./src/ui"], |
||||
"ReportCst": ["./private/constants"], |
||||
"types": ["./types/index.d.ts"], |
||||
"@core/*": ["./src/modules/core/*"], |
||||
"@constants/*": ["./src/modules/constants/*"] |
||||
} |
||||
}, |
||||
"include": [ |
||||
"src/*.ts", |
||||
"src/**/*.ts", |
||||
"private/*.ts", |
||||
"private/**/*.ts", |
||||
"types/globals.d.ts" |
||||
] |
||||
} |
@ -0,0 +1,8 @@
|
||||
interface Obj { |
||||
[key: string]: any; |
||||
} |
||||
|
||||
declare let BI: Obj & import('fineui')._BI; |
||||
declare const Fix: Obj; |
||||
declare const DecCst: Obj; |
||||
declare const Dec: Obj; |
@ -0,0 +1,15 @@
|
||||
const path = require('path'); |
||||
module.exports = { |
||||
DEST: path.resolve(__dirname, '../dist'), |
||||
NODE_MODULES: path.resolve(__dirname, '../node_modules'), |
||||
UI: path.resolve(__dirname, '../src/ui'), |
||||
SRC: path.resolve(__dirname, '../src'), |
||||
CORE: path.resolve(__dirname, '../src/modules/core'), |
||||
CONST: path.resolve(__dirname, '../src/modules/constants'), |
||||
CONSTAINS: path.resolve(__dirname, '../private/constants.ts'), |
||||
FINESHEET: path.resolve(__dirname, '../node_modules/finesheet'), |
||||
AXIOS: path.resolve(__dirname, '../node_modules/axios'), |
||||
BABEL_CONFIG: path.resolve(__dirname, '../babel.config.js'), |
||||
RESIZER: path.resolve(__dirname, '../node_modules/js-resizable'), |
||||
LOCAL_FINESHEET: path.resolve(__dirname, '../finesheet'), |
||||
}; |
@ -0,0 +1,69 @@
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); |
||||
const vars = require('postcss-simple-vars'); |
||||
const autoprefixer = require('autoprefixer'); |
||||
|
||||
const dirs = require('./dirs'); |
||||
|
||||
module.exports = { |
||||
resolve: { |
||||
mainFields: ['module', 'main'], |
||||
extensions: ['.js', '.ts'], |
||||
alias: { |
||||
ui: dirs.UI, |
||||
src: dirs.SRC, |
||||
ReportCst: dirs.CONSTAINS, |
||||
'@core': dirs.CORE, |
||||
'@constants': dirs.CONST, |
||||
}, |
||||
}, |
||||
|
||||
module: { |
||||
rules: [ |
||||
{ |
||||
test: /\.(js|ts)$/, |
||||
include: [dirs.SRC, dirs.FINESHEET, dirs.AXIOS, dirs.RESIZER, dirs.LOCAL_FINESHEET, dirs.NODE_MODULES], |
||||
use: [{ |
||||
loader: 'babel-loader', |
||||
options: { |
||||
configFile: dirs.BABEL_CONFIG |
||||
}, |
||||
}, { |
||||
loader: 'source-map-loader', |
||||
options: { |
||||
enforce: 'pre' |
||||
} |
||||
}] |
||||
}, |
||||
{ |
||||
test: /\.(css|less)$/, |
||||
use: [ |
||||
MiniCssExtractPlugin.loader, |
||||
{ |
||||
loader: 'css-loader', |
||||
options: { |
||||
url: false |
||||
} |
||||
}, |
||||
{ |
||||
loader: 'postcss-loader', |
||||
options: { |
||||
plugins: [vars({ |
||||
variables: { |
||||
fontUrl: '../node_modules/fineui/dist/font/', |
||||
imageUrl: '/webroot/decision/resources?path=/com/fr/web/resources/dist/images/1x', |
||||
image2xUrl: '/webroot/decision/resources?path=/com/fr/web/resources/dist/images/2x', |
||||
} |
||||
}), autoprefixer] |
||||
}, |
||||
}, |
||||
{ |
||||
loader: 'less-loader', |
||||
options: { |
||||
relativeUrls: false |
||||
} |
||||
} |
||||
], |
||||
}, |
||||
], |
||||
}, |
||||
}; |
@ -0,0 +1,87 @@
|
||||
const merge = require('webpack-merge'); |
||||
const path = require('path'); |
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); |
||||
const HtmlWebpackPlugin = require('html-webpack-plugin'); |
||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); |
||||
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); |
||||
|
||||
const chokidar = require('chokidar'); |
||||
const { execSync } = require('child_process'); |
||||
const { resolve } = require('path'); |
||||
|
||||
const dirs = require('./dirs'); |
||||
|
||||
const common = require('./webpack.common.js'); |
||||
|
||||
// 监听国际化文件并编译
|
||||
chokidar |
||||
.watch(resolve(__dirname, '../../main/resources/com/fr/plugin/db/json/locale/json_zh_CN.properties'), { ignored: /(^|[/\\])\../ }) |
||||
.on('all', (event, path) => { |
||||
try { |
||||
execSync('npm run i18n'); |
||||
} catch (e) { |
||||
console.error(e); |
||||
} |
||||
}); |
||||
|
||||
module.exports = merge(common, { |
||||
devtool: 'eval-source-map', |
||||
entry: { |
||||
i18n: [ |
||||
'./src/i18n.ts', |
||||
], |
||||
show: [ |
||||
'./src/index.show.ts', |
||||
], |
||||
edit: [ |
||||
'./src/index.edit.ts', |
||||
], |
||||
}, |
||||
externals: { |
||||
CodeMirror: 'CodeMirror', |
||||
}, |
||||
output: { |
||||
path: dirs.DEST, |
||||
filename: '[name].[contenthash].js', |
||||
}, |
||||
devServer: { |
||||
contentBase: path.join(__dirname, '..'), |
||||
port: 10002, |
||||
liveReload: true, |
||||
}, |
||||
plugins: [ |
||||
new MiniCssExtractPlugin({ |
||||
path: dirs.DEST, |
||||
filename: 'show.dev.[contenthash].css', |
||||
}), |
||||
new HtmlWebpackPlugin({ |
||||
template: path.resolve(__dirname, '../index.html'), |
||||
chunks: ['polyfill', 'i18n', 'edit'], |
||||
chunksSortMode: 'manual', |
||||
title: 'Redis插件编辑界面', |
||||
}), |
||||
new HtmlWebpackPlugin({ |
||||
filename: 'show/index.html', |
||||
template: path.resolve(__dirname, '../index.html'), |
||||
chunks: ['polyfill', 'i18n', 'show'], |
||||
chunksSortMode: 'manual', |
||||
title: 'Redis插件预览界面', |
||||
}), |
||||
new ForkTsCheckerWebpackPlugin({ |
||||
watch: ['./src'], |
||||
}), |
||||
new OptimizeCssAssetsPlugin({ |
||||
assetNameRegExp: /\.css$/g, |
||||
cssProcessor: require('cssnano'), |
||||
cssProcessorPluginOptions: { |
||||
preset: ['default', { |
||||
discardComments: { |
||||
removeAll: true, |
||||
}, |
||||
normalizeUnicode: false, |
||||
}], |
||||
}, |
||||
canPrint: true, |
||||
}), |
||||
], |
||||
}); |
@ -0,0 +1,52 @@
|
||||
const webpack = require('webpack'); |
||||
const merge = require('webpack-merge'); |
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); |
||||
const vars = require('postcss-simple-vars'); |
||||
|
||||
const dirs = require('./dirs'); |
||||
|
||||
const common = require('./webpack.common.js'); |
||||
|
||||
module.exports = merge.smart(common, { |
||||
mode: 'production', |
||||
|
||||
devtool: 'hidden-source-map', |
||||
entry: { |
||||
show: ['./src/index.ts'], |
||||
}, |
||||
output: { |
||||
path: dirs.DEST, |
||||
filename: 'redis.js' |
||||
}, |
||||
plugins: [ |
||||
new MiniCssExtractPlugin({ |
||||
path: dirs.DEST, |
||||
filename: "redis.css" |
||||
}), |
||||
new webpack.BannerPlugin({ |
||||
banner: `time: ${new Date().toLocaleString()}` |
||||
}) |
||||
], |
||||
|
||||
module: { |
||||
rules: [ |
||||
{ |
||||
test: /\.(css|less)$/, |
||||
use: [ |
||||
{ |
||||
loader: 'postcss-loader', |
||||
options: { |
||||
plugins: [vars({ |
||||
variables: { |
||||
fontUrl: '/webroot/decision/resources?path=/com/fr/web/ui/font', |
||||
imageUrl: '/webroot/decision/resources?path=/com/fr/web/resources/dist/images/1x', |
||||
image2xUrl: '/webroot/decision/resources?path=/com/fr/web/resources/dist/images/2x', |
||||
} |
||||
})] |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
], |
||||
}, |
||||
}); |
Loading…
Reference in new issue