Browse Source

feat: KERNEL-1500 redis数据集代码迁移

paid
alan 5 years ago
parent
commit
c5f72ac49c
  1. 5
      src/main/resources/com/fr/plugin/db/redis/locale/redis_zh_CN.properties
  2. 5
      src/web/.eslintignore
  3. 236
      src/web/.eslintrc
  4. 10
      src/web/.gitignore
  5. 29
      src/web/babel.config.js
  6. 30
      src/web/index.html
  7. 26
      src/web/jest.config.js
  8. 21
      src/web/lib/i18n/i18n.js
  9. 29
      src/web/lib/upgrade/index.js
  10. 75
      src/web/package.json
  11. 44
      src/web/private/i18n.ts
  12. 10
      src/web/readme.md
  13. BIN
      src/web/screenshots/1.png
  14. BIN
      src/web/screenshots/2.png
  15. BIN
      src/web/screenshots/3.png
  16. 3
      src/web/src/i18n.ts
  17. 6
      src/web/src/index.edit.ts
  18. 6
      src/web/src/index.show.ts
  19. 16
      src/web/src/index.ts
  20. 183
      src/web/src/modules/app.edit.ts
  21. 115
      src/web/src/modules/app.show.ts
  22. 29
      src/web/src/modules/components/form_item/form_item.ts
  23. 74
      src/web/src/modules/components/pool/pool.ts
  24. 20
      src/web/src/modules/components/pool/pool_edit.model.ts
  25. 134
      src/web/src/modules/components/pool/pool_edit.ts
  26. 83
      src/web/src/modules/components/proxy/proxy.ts
  27. 142
      src/web/src/modules/components/proxy/proxy_edit.ts
  28. 63
      src/web/src/modules/constants/constant.ts
  29. 85
      src/web/src/modules/core/core.ts
  30. 75
      src/web/src/ui/fineui.ts
  31. 1
      src/web/src/ui/index.ts
  32. 37
      src/web/tsconfig.json
  33. 8
      src/web/types/globals.d.ts
  34. 15
      src/web/webpack/dirs.js
  35. 69
      src/web/webpack/webpack.common.js
  36. 87
      src/web/webpack/webpack.dev.js
  37. 52
      src/web/webpack/webpack.prod.js
  38. 8757
      src/web/yarn.lock

5
src/main/resources/com/fr/plugin/db/redis/locale/redis_zh_CN.properties

@ -15,8 +15,7 @@ Plugin-Redis_Formula=\u516C\u5F0F
Plugin-Redis_Query=\u67E5\u8BE2
Plugin-Redis_Connection_Successfully=\u8FDE\u63A5\u6210\u529F
Plugin-Redis_Connection_Failed=\u8FDE\u63A5\u5931\u8D25
Plugin-Redis_Script_Text_Description=\u53EF\u4EE5\u4F7F\u7528JavaScript\u811A\u672C\u6765\u8F6C\u6362\u67E5\u8BE2\u7ED3\u679C\u3002\n\u6CE8\u610F1\uFF1A\u5185\u7F6E\u53C2\u6570$content\u548C$column\uFF0C\u5206\u522B\u8868\u793A\u539F\u59CB\u7684\u6570\u636E\u96C6\u5185\u5BB9\uFF08\u4E8C\u7EF4\u6570\u7EC4\uFF09\u548C\u6570\u636E\u96C6\u7684\u5217\u540D\uFF08\u4E00\u7EF4\u6570\u7EC4\uFF09\u3002\u5982\u679C\u8FD4\u56DE\u4E00\u4E2A\u4E8C\u7EF4\u6570\u7EC4\uFF0C\u8868\u793A\u4EC5\u4FEE\u6539\u6570\u636E\u96C6\u5185\u5BB9\uFF0C\u4E0D\u4FEE\u6539\u6570\u636E\u96C6\u7684\u5217\u540D\uFF1B\u5982\u679C\u8FD4\u56DE\u4E00\u4E2A\u5982\u4E0B\u683C\u5F0F\u7684\u5BF9\u8C61:\
{content:[][],column:[]}\uFF0C\u8868\u793A\u540C\u65F6\u4FEE\u6539\u4E86\u6570\u636E\u96C6\u7684\u5185\u5BB9\u548C\u5217\u540D\u3002\n\u6CE8\u610F2\uFF1A\u5982\u679C\u5E0C\u671B\u5F15\u7528\u5916\u90E8JS\uFF0C\u53EF\u4EE5\u5728\u811A\u672C\u6846\u4E2D\u5199\u5165file:///$ENV_HOME/transform.js\uFF0C\u5176\u4E2D$ENV_HOME\u8868\u793A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u3002
Plugin-Redis_Script_Text_Description=\u53EF\u4EE5\u4F7F\u7528JavaScript\u811A\u672C\u6765\u8F6C\u6362\u67E5\u8BE2\u7ED3\u679C\u3002\n\u6CE8\u610F1\uFF1A\u5185\u7F6E\u53C2\u6570$content\u548C$column\uFF0C\u5206\u522B\u8868\u793A\u539F\u59CB\u7684\u6570\u636E\u96C6\u5185\u5BB9\uFF08\u4E8C\u7EF4\u6570\u7EC4\uFF09\u548C\u6570\u636E\u96C6\u7684\u5217\u540D\uFF08\u4E00\u7EF4\u6570\u7EC4\uFF09\u3002\u5982\u679C\u8FD4\u56DE\u4E00\u4E2A\u4E8C\u7EF4\u6570\u7EC4\uFF0C\u8868\u793A\u4EC5\u4FEE\u6539\u6570\u636E\u96C6\u5185\u5BB9\uFF0C\u4E0D\u4FEE\u6539\u6570\u636E\u96C6\u7684\u5217\u540D\uFF1B\u5982\u679C\u8FD4\u56DE\u4E00\u4E2A\u5982\u4E0B\u683C\u5F0F\u7684\u5BF9\u8C61:\n{content:[][],column:[]}\uFF0C\u8868\u793A\u540C\u65F6\u4FEE\u6539\u4E86\u6570\u636E\u96C6\u7684\u5185\u5BB9\u548C\u5217\u540D\u3002\n\u6CE8\u610F2\uFF1A\u5982\u679C\u5E0C\u671B\u5F15\u7528\u5916\u90E8JS\uFF0C\u53EF\u4EE5\u5728\u811A\u672C\u6846\u4E2D\u5199\u5165file:///$ENV_HOME/transform.js\uFF0C\u5176\u4E2D$ENV_HOME\u8868\u793A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u3002
Plugin-Redis_Pool_Max_Total=\u6700\u5927\u8FDE\u63A5\u6570
Plugin-Redis_Script_Query_Text=\u67E5\u8BE2\u811A\u672C
Plugin-Redis_Pool_Max_Wait=\u6700\u5927\u7B49\u5F85\u65F6\u95F4
@ -39,3 +38,5 @@ Plugin-Redis_Cluster_Config_Description=1\u3001\u4F7F\u7528\u9017\u53F7\u5206\u5
Plugin-Redis_Script_Engine_Type=\u811A\u672C\u5F15\u64CE
Plugin-Redis_Script_Engine_Type_Default=\u9ED8\u8BA4
Plugin-Redis_Script_Engine_Type_V8=V8\u9AD8\u901F\u5F15\u64CE
Plugin-Redis_Proxy_Private_Key_Path_Mark=\u8bf7\u8f93\u5165\u670d\u52a1\u5668\u6587\u4ef6\u8def\u5f84
Plugin-Redis_Connection_Form_OriginalCharsetName= \u7f16\u7801

5
src/web/.eslintignore

@ -0,0 +1,5 @@
types
node_modules/
dist/
assets/
webpack/

236
src/web/.eslintrc

@ -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" // 强制回调错误处理
}
}

10
src/web/.gitignore vendored

@ -0,0 +1,10 @@
/node_modules
/bower_components
.idea/
.DS_Store
/constants/classes/
dist
package-lock.json
.vscode
yarn-error.log
.history/

29
src/web/babel.config.js

@ -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,
};
};

30
src/web/index.html

@ -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>

26
src/web/jest.config.js

@ -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'],
},
},
},
};

21
src/web/lib/i18n/i18n.js

@ -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}};
`);
}

29
src/web/lib/upgrade/index.js

@ -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);
});
}

75
src/web/package.json

@ -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"
}
}

44
src/web/private/i18n.ts

@ -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': '编码',
};

10
src/web/readme.md

@ -1 +1,11 @@
# 前端代码
### 数据连接
安装完插件后,在新建数据连接的地方可以看到Redis类型的数据连接:
![1](screenshots/1.png)
### 新建数据集
点击新建数据连接即可看到新建Redis的表单:
![1](screenshots/2.png)
按照要求输入即可。
### 查看数据连接
数据连接新建完成后,点击数据连接的名称查看该数据连接:
![1](screenshots/3.png)

BIN
src/web/screenshots/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 734 KiB

BIN
src/web/screenshots/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
src/web/screenshots/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

3
src/web/src/i18n.ts

@ -0,0 +1,3 @@
import { i18n } from '../private/i18n';
BI.addI18n(i18n);

6
src/web/src/index.edit.ts

@ -0,0 +1,6 @@
import { RedisEdit } from './modules/app.edit';
BI.createWidget({
type: RedisEdit.xtype,
element: '#wrapper',
});

6
src/web/src/index.show.ts

@ -0,0 +1,6 @@
import { RedisShow } from './modules/app.show';
BI.createWidget({
type: RedisShow.xtype,
element: '#wrapper',
});

16
src/web/src/index.ts

@ -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);
});

183
src/web/src/modules/app.edit.ts

@ -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,
};
}
}

115
src/web/src/modules/app.show.ts

@ -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'),
}],
},
],
};
}
}

29
src/web/src/modules/components/form_item/form_item.ts

@ -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,
],
};
}
}

74
src/web/src/modules/components/pool/pool.ts

@ -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,
}],
},
],
};
}
}

20
src/web/src/modules/components/pool/pool_edit.model.ts

@ -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;
},
}
}

134
src/web/src/modules/components/pool/pool_edit.ts

@ -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(),
};
}
}

83
src/web/src/modules/components/proxy/proxy.ts

@ -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,
}],
},
],
};
}
}

142
src/web/src/modules/components/proxy/proxy_edit.ts

@ -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(),
};
}
}

63
src/web/src/modules/constants/constant.ts

@ -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',
},
];

85
src/web/src/modules/core/core.ts

@ -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, []>;

75
src/web/src/ui/fineui.ts

@ -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';

1
src/web/src/ui/index.ts

@ -0,0 +1 @@
export * from './fineui';

37
src/web/tsconfig.json

@ -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"
]
}

8
src/web/types/globals.d.ts vendored

@ -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;

15
src/web/webpack/dirs.js

@ -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'),
};

69
src/web/webpack/webpack.common.js

@ -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
}
}
],
},
],
},
};

87
src/web/webpack/webpack.dev.js

@ -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,
}),
],
});

52
src/web/webpack/webpack.prod.js

@ -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',
}
})]
},
},
],
},
],
},
});

8757
src/web/yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save