diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index ffb355f8..00000000 --- a/.eslintignore +++ /dev/null @@ -1,14 +0,0 @@ -dist -coverage -node_modules -dest -types -public/entry -public/runtime - -comp-entry.ts -config-entry.ts -value-entry.ts - -magic-admin/web/public/runtime -magic-admin/server/static \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 3751e8b3..00000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,71 +0,0 @@ -module.exports = { - env: { - node: true, - browser: true, - es2021: true, - }, - globals: { - describe: true, - it: true, - expect: true, - beforeEach: true, - }, - extends: [ - 'eslint-config-tencent', - 'eslint-config-tencent/ts', - 'plugin:vue/vue3-essential', - './prettier.cjs', - ], - parser: 'vue-eslint-parser', - parserOptions: { - ecmaVersion: 12, - parser: '@typescript-eslint/parser', - extraFileExtensions: ['.vue'], - sourceType: 'module', - }, - plugins: [ - 'vue', - '@typescript-eslint', - 'simple-import-sort' - ], - ignorePatterns: ['.eslintrc.cjs'], - rules: { - 'vue/no-mutating-props': 'off', - 'vue/multi-word-component-names': 'off', - 'no-param-reassign': 'off', - '@typescript-eslint/naming-convention': 'off', - '@typescript-eslint/no-require-imports': 'off', - "@typescript-eslint/no-misused-promises": [ - "error", - { - "checksVoidReturn": false - } - ], - 'simple-import-sort/imports': [ - "error", { - groups: [ - ['./polyfills'], - // Node.js builtins. You could also generate this regex if you use a `.js` config. - // For example: `^(${require("module").builtinModules.join("|")})(/|$)` - [ - "^(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)", - ], - ["^(node)(:.*|$)"], - // Packages. `react|vue` related packages come first. - ["^(react|vue|vite)", "^@?\\w"], - ["^(@tmagic)(/.*|$)"], - // Internal packages. - ["^(@|@editor|@data-source)(/.*|$)"], - // Side effect imports. - ["^\\u0000"], - // Parent imports. Put `..` last. - ["^\\.\\.(?!/?$)", "^\\.\\./?$"], - // Other relative imports. Put same-folder imports and `.` last. - ["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"], - // Style imports. - ["^.+\\.s?css$"], - ], - } - ] - }, -}; diff --git a/.gitignore b/.gitignore index 9f828c73..f72059d3 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ coverage auto-imports.d.ts components.d.ts docs/.vitepress/cache + +.eslintcache diff --git a/docs/.vitepress/theme/components/demo-block.vue b/docs/.vitepress/theme/components/demo-block.vue index 368d2dc2..bfccc7a3 100644 --- a/docs/.vitepress/theme/components/demo-block.vue +++ b/docs/.vitepress/theme/components/demo-block.vue @@ -17,11 +17,7 @@ -
+
@@ -30,23 +26,14 @@ - - {{type === 'form' ? '查看结果' : '在线运行'}} + + {{ type === 'form' ? '查看结果' : '在线运行' }}
- +
@@ -60,7 +47,9 @@ transition: 0.2s; &.hover { - box-shadow: 0 0 8px 0 rgba(232, 237, 250, 0.6), 0 2px 4px 0 rgba(232, 237, 250, 0.5); + box-shadow: + 0 0 8px 0 rgba(232, 237, 250, 0.6), + 0 2px 4px 0 rgba(232, 237, 250, 0.5); } code { @@ -219,9 +208,7 @@ export function stripTemplate(content) { } export default { - props: [ - 'type', 'config' - ], + props: ['type', 'config'], data() { return { @@ -293,12 +280,15 @@ export default { }, text() { - return this.isStringConfig ? - hljs.highlight('js', this.config).value : - hljs.highlight('js', serialize(this.config, { - space: 2, - unsafe: true, - }).replace(/"(\w+)":\s/g, '$1: ')).value; + return this.isStringConfig + ? hljs.highlight('js', this.config).value + : hljs.highlight( + 'js', + serialize(this.config, { + space: 2, + unsafe: true, + }).replace(/"(\w+)":\s/g, '$1: '), + ).value; }, formConfig() { @@ -307,7 +297,7 @@ export default { isStringConfig() { return typeof this.config === 'string'; - } + }, }, watch: { @@ -356,7 +346,7 @@ export default { }); }, - beforeDestroy() { + beforeUnmount() { this.removeScrollHandler(); }, }; diff --git a/eslint-config/flat/base.mjs b/eslint-config/flat/base.mjs new file mode 100644 index 00000000..9deaa414 --- /dev/null +++ b/eslint-config/flat/base.mjs @@ -0,0 +1,566 @@ +export default { + rules: { + /** + * 不要在中括号中添加空格 + */ + 'array-bracket-spacing': ['error', 'never'], + /** + * 数组的方法除了 forEach 之外,回调函数必须有返回值 + */ + 'array-callback-return': 'warn', + /** + * 要求箭头函数体使用大括号 + */ + 'arrow-body-style': ['warn', 'as-needed'], + /** + * 要求箭头函数的参数使用圆括号 + */ + 'arrow-parens': [ + 'warn', + 'as-needed', + { + requireForBlockBody: true, + }, + ], + /** + * 强制箭头函数的箭头前后使用一致的空格 + */ + 'arrow-spacing': 'warn', + /** + * 要求打开的块标志和同一行上的标志拥有一致的间距。此规则还会在同一行关闭的块标记和前边的标记强制实施一致的间距。 + */ + 'block-spacing': 'error', + /** + * 强制在代码块中使用一致的大括号风格 + */ + 'brace-style': 'error', + /** + * 使用驼峰命名法(camelCase)命名对象、函数和实例。 + */ + camelcase: [ + 'error', + { + ignoreDestructuring: true, + properties: 'never', + }, + ], + /** + * 添加尾随逗号 + */ + 'comma-dangle': ['warn', 'always-multiline'], + /** + * 强制在逗号前后使用一致的空格 + */ + 'comma-spacing': [ + 'error', + { + before: false, + after: true, + }, + ], + /** + * 强制使用一致的逗号风格 + */ + 'comma-style': ['error', 'last'], + /** + * 强制在计算的属性的方括号中使用一致的空格 + */ + 'computed-property-spacing': ['warn', 'never'], + /** + * 禁止使用 foo['bar'],必须写成 foo.bar + */ + 'dot-notation': 'warn', + /** + * 要求或禁止文件末尾存在空行 + */ + 'eol-last': ['error', 'always'], + /** + * 必须使用 === 或 !==,禁止使用 == 或 != + */ + eqeqeq: ['warn', 'always'], + /** + * 要求或禁止在函数标识符和其调用之间有空格 + */ + 'func-call-spacing': ['error', 'never'], + /** + * 必须只使用函数声明或只使用函数表达式 + */ + 'func-style': ['off', 'expression'], + /** + * 强制在函数括号内使用一致的换行 + */ + 'function-paren-newline': ['warn', 'multiline'], + /** + * 强制 generator 函数中 * 号周围使用一致的空格 + */ + 'generator-star-spacing': [ + 'warn', + { + before: false, + after: true, + }, + ], + /** + * 限制变量名长度 + */ + 'id-length': 'off', + /** + * 强制隐式返回的箭头函数体的位置 + */ + 'implicit-arrow-linebreak': ['warn', 'beside'], + /** + * 使用 2 个空格缩进 + */ + indent: [ + 'warn', + 2, + { + SwitchCase: 1, + VariableDeclarator: 1, + outerIIFEBody: 1, + FunctionDeclaration: { + parameters: 1, + body: 1, + }, + FunctionExpression: { + parameters: 1, + body: 1, + }, + CallExpression: { + arguments: 1, + }, + ArrayExpression: 1, + ObjectExpression: 1, + ImportDeclaration: 1, + flatTernaryExpressions: false, + ignoredNodes: [ + 'JSXElement', + 'JSXElement > *', + 'JSXAttribute', + 'JSXIdentifier', + 'JSXNamespacedName', + 'JSXMemberExpression', + 'JSXSpreadAttribute', + 'JSXExpressionContainer', + 'JSXOpeningElement', + 'JSXClosingElement', + 'JSXFragment', + 'JSXOpeningFragment', + 'JSXClosingFragment', + 'JSXText', + 'JSXEmptyExpression', + 'JSXSpreadChild', + ], + ignoreComments: false, + }, + ], + /** + * 强制在对象字面量的属性中键和值之间使用一致的间距 + */ + 'key-spacing': 'error', + /** + * 强制在关键字前后使用一致的空格 + */ + 'keyword-spacing': [ + 'error', + { + overrides: { + if: { + after: true, + }, + for: { + after: true, + }, + while: { + after: true, + }, + else: { + after: true, + }, + }, + }, + ], + /** + * 只允许使用 unix 的 LF 作为换行符,Windows 的 CRLF 不可以使用 + */ + 'linebreak-style': ['warn', 'unix'], + /** + * 强制一行的最大长度,限制单行不能超过100个字符,字符串和正则表达式除外。 + */ + 'max-len': [ + 'off', + { + code: 120, + ignoreStrings: true, + ignoreUrls: true, + ignoreRegExpLiterals: true, + ignoreTemplateLiterals: true, + }, + ], + /** + * 只有在命名构造器或者类的时候,才用帕斯卡拼命名法(PascalCase),即首字母大写。 + */ + 'new-cap': [ + 'error', + { + newIsCap: true, + newIsCapExceptions: [], + capIsNew: false, + capIsNewExceptions: ['Immutable.Map', 'Immutable.Set', 'Immutable.List'], + properties: false, + }, + ], + /** + * 在编写多个方法链式调用(超过两个方法链式调用)时。 使用前导点,强调这行是一个方法调用,而不是一个语句。 + */ + 'newline-per-chained-call': [ + 'warn', + { + ignoreChainWithDepth: 2, + }, + ], + /** + * 使用字面量语法创建数组。 + */ + 'no-array-constructor': ['error'], + /** + * 在 case 和 default 的子句中,如果存在声明 (例如. let, const, function, 和 class),使用大括号来创建块级作用域。 + */ + 'no-case-declarations': 'error', + /** + * 避免搞混箭头函数符号 (=>) 和比较运算符 (<=, >=)。 + */ + 'no-confusing-arrow': 'warn', + /** + * 禁止对使用 const 定义的常量重新赋值 + */ + 'no-const-assign': 'error', + /** + * 禁止重复定义类的成员 + */ + 'no-dupe-class-members': 'error', + /** + * 禁止在 else 内使用 return,必须改为提前结束 + */ + 'no-else-return': [ + 'warn', + { + allowElseIf: false, + }, + ], + /** + * 禁止使用 eval + */ + 'no-eval': 'error', + /** + * 不要使用迭代器。 + * @reason 推荐使用 JavaScript 的高阶函数代替 for-in。 + */ + 'no-iterator': 'warn', + /** + * 禁止在循环内的函数内部出现循环体条件语句中定义的变量 + */ + 'no-loop-func': 'error', + /** + * 禁止混合使用不同的操作符: + * - 禁止 `%`, `**` 之间混用 + * - 禁止 `%` 与其它运算符之间混用 + * - 禁止乘除运算符之间混用 + * - 禁止位运算符之间的任何混用 + * - 禁止比较运算符之间混用 + * - 禁止 `&&`, `||` 之间混用 + */ + 'no-mixed-operators': [ + 'error', + { + groups: [ + ['%', '**'], + ['%', '+'], + ['%', '-'], + ['%', '*'], + ['%', '/'], + ['&', '|', '<<', '>>', '>>>'], + ['==', '!=', '===', '!=='], + ['&&', '||'], + ], + allowSamePrecedence: false, + }, + ], + /** + * 禁止连续赋值,比如 foo = bar = 1 + */ + 'no-multi-assign': 'error', + /** + * 不要使用多个空行填充代码。 + */ + 'no-multiple-empty-lines': 'error', + /** + * 禁止使用嵌套的三元表达式,比如 a ? b : c ? d : e + */ + 'no-nested-ternary': 'warn', + /** + * 禁止使用 new Function + * @reason 这和 eval 是等价的 + */ + 'no-new-func': 'error', + /** + * 禁止直接 new Object + */ + 'no-new-object': 'error', + /** + * 禁止使用 new 来生成 String, Number 或 Boolean + */ + 'no-new-wrappers': 'warn', + /** + * 禁止对函数的参数重新赋值 + */ + 'no-param-reassign': [ + 'warn', + { + props: true, + ignorePropertyModificationsFor: [ + 'acc', + 'accumulator', + 'e', + 'ctx', + 'req', + 'request', + 'res', + 'response', + '$scope', + 'staticContext', + 'state', + ], + }, + ], + /** + * 禁止使用 ++ 或 -- + */ + 'no-plusplus': [ + 'error', + { + allowForLoopAfterthoughts: true, + }, + ], + /** + * 禁止使用 hasOwnProperty, isPrototypeOf 或 propertyIsEnumerable + */ + 'no-prototype-builtins': 'error', + /** + * 计算指数时,可以使用 ** 运算符。 + */ + 'no-restricted-properties': [ + 'warn', + { + object: 'Math', + property: 'pow', + message: 'Please use ** instand', + }, + ], + /** + * 推荐使用 JavaScript 的高阶函数代替 for-in + */ + 'no-restricted-syntax': [ + 'warn', + { + selector: 'ForInStatement', + message: + 'for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.', + }, + { + selector: 'LabeledStatement', + message: 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.', + }, + { + selector: 'WithStatement', + message: '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.', + }, + ], + /** + * 避免在行尾添加空格。 + */ + 'no-trailing-spaces': 'error', + /** + * 变量应先声明再使用,禁止引用任何未声明的变量,除非你明确知道引用的变量存在于当前作用域链上。 + */ + 'no-undef': ['error'], + /** + * 禁止变量名出现下划线 + */ + 'no-underscore-dangle': 'warn', + /** + * 必须使用 !a 替代 a ? false : true + */ + 'no-unneeded-ternary': 'warn', + /** + * 已定义的变量必须使用 + * 但不检查最后一个使用的参数之前的参数 + * 也不检查 rest 属性的兄弟属性 + */ + 'no-unused-vars': [ + 'error', + { + args: 'after-used', + ignoreRestSiblings: true, + argsIgnorePattern: '^_.+', + varsIgnorePattern: '^_.+', + }, + ], + /** + * 禁止出现没必要的 constructor + */ + 'no-useless-constructor': 'warn', + /** + * 禁止出现没必要的转义 + */ + 'no-useless-escape': 'error', + /** + * 禁止使用 var + */ + 'no-var': 'error', + /** + * 禁止属性前有空白 + */ + 'no-whitespace-before-property': 'warn', + /** + * 强制单个语句的位置 + */ + 'nonblock-statement-body-position': ['error', 'beside'], + /** + * 强制在大括号中使用一致的空格 + */ + 'object-curly-spacing': ['warn', 'always'], + /** + * 将对象方法、属性简写,且间歇属性放在前面。 + */ + 'object-shorthand': 'warn', + /** + * 禁止变量申明时用逗号一次申明多个 + */ + 'one-var': ['warn', 'never'], + /** + * 避免在赋值语句 = 前后换行。如果你的代码单行长度超过了 max-len 定义的长度而不得不换行,那么使用括号包裹。 + */ + 'operator-linebreak': [ + 'error', + 'before', + { + overrides: { + '=': 'none', + }, + }, + ], + /** + * 要求或禁止块内填充 + */ + 'padded-blocks': ['error', 'never'], + /** + * 要求回调函数使用箭头函数 + */ + 'prefer-arrow-callback': 'warn', + /** + * 申明后不再被修改的变量必须使用 const 来申明 + */ + 'prefer-const': [ + 'error', + { + destructuring: 'any', + ignoreReadBeforeAssign: false, + }, + ], + /** + * 优先使用解构赋值 + */ + 'prefer-destructuring': [ + 'warn', + { + VariableDeclarator: { + array: false, + object: true, + }, + AssignmentExpression: { + array: true, + object: false, + }, + }, + { + enforceForRenamedProperties: false, + }, + ], + /** + * 必须使用 ...args 而不是 arguments + */ + 'prefer-rest-params': 'warn', + /** + * 必须使用 ... 而不是 apply,比如 foo(...args) + */ + 'prefer-spread': 'warn', + /** + * 必须使用模版字符串而不是字符串连接 + */ + 'prefer-template': 'error', + /** + * 要求对象字面量属性名称用引号括起来 + */ + 'quote-props': [ + 'error', + 'as-needed', + { + keywords: false, + }, + ], + /** + * 使用单引号 '' 定义字符串 + */ + quotes: [ + 'warn', + 'single', + { + allowTemplateLiterals: false, + }, + ], + /** + * parseInt 必须传入第二个参数 + */ + radix: 'warn', + /** + * 要加分号 + */ + semi: ['error', 'always'], + /** + * 强制在块之前使用一致的空格 + */ + 'space-before-blocks': 'error', + /** + * 强制在 function 的左括号之前使用一致的空格 + */ + 'space-before-function-paren': [ + 'error', + { + anonymous: 'always', + named: 'never', + asyncArrow: 'always', + }, + ], + /** + * 强制在圆括号内使用一致的空格 + */ + 'space-in-parens': ['error', 'never'], + /** + * 要求操作符周围有空格 + */ + 'space-infix-ops': 'error', + /** + * 注释的斜线或 * 后必须有空格 + */ + 'spaced-comment': ['error', 'always'], + /** + * 要求或禁止模板字符串中的嵌入表达式周围空格的使用 + */ + 'template-curly-spacing': ['error', 'never'], + /** + * 要求立即执行的函数使用括号括起来 + */ + 'wrap-iife': ['error', 'outside'], + }, +}; diff --git a/eslint-config/flat/import-sort.mjs b/eslint-config/flat/import-sort.mjs new file mode 100644 index 00000000..0bce8d51 --- /dev/null +++ b/eslint-config/flat/import-sort.mjs @@ -0,0 +1,38 @@ +import simpleImportSort from 'eslint-plugin-simple-import-sort'; + +export default { + files: ['**/*.{js,mjs,cjs,ts,vue}'], + plugins: { + 'simple-import-sort': simpleImportSort, + }, + rules: { + 'simple-import-sort/imports': [ + 'error', + { + groups: [ + ['./polyfills'], + ['^(node)(:.*|$)'], + // Node.js builtins. You could also generate this regex if you use a `.js` config. + // For example: `^(${require("module").builtinModules.join("|")})(/|$)` + [ + '^(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)', + ], + // Packages. `react|vue` related packages come first. + ['^(react|vue|vite|vitest)', '^@?\\w'], + ['^(@tencent)(/.*|$)'], + ['^(@tmagic)(/.*|$)'], + // Internal packages. + ['^(@|@editor|@data-source)(/.*|$)'], + // Side effect imports. + ['^\\u0000'], + // Parent imports. Put `..` last. + ['^\\.\\.(?!/?$)', '^\\.\\./?$'], + // Other relative imports. Put same-folder imports and `.` last. + ['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'], + // Style imports. + ['^.+\\.s?css$'], + ], + }, + ], + }, +}; diff --git a/eslint-config/flat/import.mjs b/eslint-config/flat/import.mjs new file mode 100644 index 00000000..8455faef --- /dev/null +++ b/eslint-config/flat/import.mjs @@ -0,0 +1,37 @@ +import { rules } from 'eslint-plugin-import'; + +export default { + plugins: { + import: { + meta: { name: 'eslint-plugin-import' }, + rules, + }, + }, + files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'], + languageOptions: { + ecmaVersion: 2018, + sourceType: 'module', + }, + rules: { + /** + * 导入语句前不允许有任何非导入语句 + */ + 'import/first': 'error', + /** + * 禁止重复导入模块 + */ + 'import/no-duplicates': 'error', + /** + * 禁止使用 let 导出 + */ + 'import/no-mutable-exports': 'warn', + /** + * 禁用导入的模块时使用 webpack 特有的语法(感叹号) + */ + 'import/no-webpack-loader-syntax': 'warn', + /** + * 当只有一个导出时,必须使用 export default 来导出 + */ + 'import/prefer-default-export': 'off', + }, +}; diff --git a/prettier.cjs b/eslint-config/flat/prettier.mjs similarity index 98% rename from prettier.cjs rename to eslint-config/flat/prettier.mjs index 063c17bd..556276e4 100644 --- a/prettier.cjs +++ b/eslint-config/flat/prettier.mjs @@ -1,5 +1,4 @@ -module.exports = { - plugins: ['prettier'], +export default { rules: { 'wrap-iife': 'off', 'template-curly-spacing': 'off', diff --git a/eslint-config/flat/ts.mjs b/eslint-config/flat/ts.mjs new file mode 100644 index 00000000..f3d3a3b5 --- /dev/null +++ b/eslint-config/flat/ts.mjs @@ -0,0 +1,376 @@ +export default { + files: ['**/*.ts', '**/*.tsx'], + rules: { + 'brace-style': 'off', + 'no-empty-function': 'off', + // https://github.com/typescript-eslint/typescript-eslint/issues/491 + 'no-invalid-this': 'off', + 'no-magic-numbers': 'off', + 'react/sort-comp': 'off', + 'func-call-spacing': 'off', + 'comma-spacing': 'off', + 'dot-notation': 'off', + indent: 'off', + 'keyword-spacing': 'off', + camelcase: 'off', + 'no-underscore-dangle': 'off', + 'no-array-constructor': 'off', + 'no-dupe-class-members': 'off', + 'no-undef': 'off', + 'no-unused-vars': 'off', + 'no-useless-constructor': 'off', + quotes: 'off', + semi: 'off', + 'space-before-function-paren': 'off', + // https://github.com/typescript-eslint/typescript-eslint/issues/600 + 'spaced-comment': ['error', 'always', { markers: ['/'] }], + /** + * 重载的函数必须写在一起 + * @reason 增加可读性 + */ + '@typescript-eslint/adjacent-overload-signatures': 'error', + /** 同 JS 规则的 TS 版本 */ + '@stylistic/ts/brace-style': 'error', + /** 同 JS 规则的 TS 版本 */ + '@stylistic/comma-spacing': [ + 'error', + { + before: false, + after: true, + }, + ], + /** + * 类型断言必须使用 as Type,禁止使用 ,禁止对对象字面量进行类型断言(断言成 any 是允许的) + * @reason 容易被理解为 jsx + */ + '@typescript-eslint/consistent-type-assertions': [ + 'error', + { + assertionStyle: 'as', + objectLiteralTypeAssertions: 'never', + }, + ], + /** + * 优先使用 interface 而不是 type + */ + '@typescript-eslint/consistent-type-definitions': 'off', + /** 同 JS 规则的 TS 版本 */ + '@typescript-eslint/dot-notation': 'warn', + /** + * 必须设置类的成员的可访问性 + * @reason 将不需要公开的成员设为私有的,可以增强代码的可理解性,对文档输出也很友好 + */ + '@typescript-eslint/explicit-member-accessibility': 'off', + /** + * 要求或禁止在函数标识符和其调用之间有空格 + */ + '@stylistic/ts/func-call-spacing': ['error', 'never'], + /** 同 JS 规则的 TS 版本 */ + '@stylistic/ts/indent': [ + 'warn', + 2, + { + SwitchCase: 1, + VariableDeclarator: 1, + outerIIFEBody: 1, + FunctionDeclaration: { + parameters: 1, + body: 1, + }, + FunctionExpression: { + parameters: 1, + body: 1, + }, + CallExpression: { + arguments: 1, + }, + ArrayExpression: 1, + ObjectExpression: 1, + ImportDeclaration: 1, + flatTernaryExpressions: false, + ignoredNodes: [ + 'JSXElement', + 'JSXElement > *', + 'JSXAttribute', + 'JSXIdentifier', + 'JSXNamespacedName', + 'JSXMemberExpression', + 'JSXSpreadAttribute', + 'JSXExpressionContainer', + 'JSXOpeningElement', + 'JSXClosingElement', + 'JSXFragment', + 'JSXOpeningFragment', + 'JSXClosingFragment', + 'JSXText', + 'JSXEmptyExpression', + 'JSXSpreadChild', + ], + ignoreComments: false, + }, + ], + /** 同 JS 规则的 TS 版本 */ + '@stylistic/ts/keyword-spacing': [ + 'error', + { + overrides: { + if: { + after: true, + }, + for: { + after: true, + }, + while: { + after: true, + }, + else: { + after: true, + }, + }, + before: true, + after: true, + }, + ], + /** + * 指定类成员的排序规则 + * @reason 优先级: + * 1. static > instance + * 2. field > constructor > method + * 3. public > protected > private + */ + '@typescript-eslint/member-ordering': [ + 'error', + { + default: [ + 'public-static-field', + 'protected-static-field', + 'private-static-field', + 'static-field', + 'public-static-method', + 'protected-static-method', + 'private-static-method', + 'static-method', + 'public-instance-field', + 'protected-instance-field', + 'private-instance-field', + 'public-field', + 'protected-field', + 'private-field', + 'instance-field', + 'field', + 'constructor', + 'public-instance-method', + 'protected-instance-method', + 'private-instance-method', + 'public-method', + 'protected-method', + 'private-method', + 'instance-method', + 'method', + ], + }, + ], + /** + * 接口中的方法必须用属性的方式定义 + */ + '@typescript-eslint/method-signature-style': 'off', + /** 代替 JS 规则 camelCase 的 TS 规则 */ + '@typescript-eslint/naming-convention': [ + 'warn', + { + selector: 'function', + format: ['camelCase', 'PascalCase'], + }, + { + selector: 'variable', + format: ['camelCase', 'UPPER_CASE'], + }, + { + selector: 'variable', + modifiers: ['global'], + format: ['camelCase', 'PascalCase', 'UPPER_CASE'], + }, + { + selector: 'variable', + format: ['camelCase', 'PascalCase'], + types: ['function'], + }, + { + selector: 'variable', + modifiers: ['exported'], + format: ['UPPER_CASE'], + types: ['boolean', 'string', 'number', 'array'], + }, + { + selector: 'variable', + modifiers: ['exported'], + format: ['camelCase', 'PascalCase'], + types: ['function'], + }, + { + selector: ['class', 'typeLike'], + format: ['PascalCase'], + }, + { + selector: ['classMethod', 'classProperty'], + leadingUnderscore: 'forbid', + trailingUnderscore: 'forbid', + format: ['camelCase'], + }, + ], + /** 同 JS 规则的 TS 版本 */ + '@typescript-eslint/no-array-constructor': 'error', + /** 同 JS 规则的 TS 版本 */ + '@typescript-eslint/no-dupe-class-members': 'error', + /** + * 禁止定义空的接口 + */ + '@typescript-eslint/no-empty-interface': 'error', + /** + * 禁止给一个初始化时直接赋值为 number, string 的变量显式的声明类型 + * @reason 可以简化代码 + */ + '@typescript-eslint/no-inferrable-types': 'warn', + /** + * 禁止对 promise 的误用,详见示例 + */ + '@typescript-eslint/no-misused-promises': [ + 'error', + { + checksConditionals: true, + }, + ], + /** + * 禁止使用 namespace 来定义命名空间 + * @reason 使用 es6 引入模块,才是更标准的方式。 + * 但是允许使用 declare namespace ... {} 来定义外部命名空间 + */ + '@typescript-eslint/no-namespace': [ + 'error', + { + allowDeclarations: true, + allowDefinitionFiles: true, + }, + ], + /** + * 禁止在 optional chaining 之后使用 non-null 断言(感叹号) + * @reason optional chaining 后面的属性一定是非空的 + */ + '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', + /** + * 禁止给类的构造函数的参数添加修饰符 + */ + '@typescript-eslint/no-parameter-properties': 'off', + /** + * 禁止使用 require + * @reason 统一使用 import 来引入模块,特殊情况使用单行注释允许 require 引入 + */ + '@typescript-eslint/no-require-imports': 'error', + /** + * 禁止将 this 赋值给其他变量,除非是解构赋值 + */ + '@typescript-eslint/no-this-alias': [ + 'error', + { + allowDestructuring: true, + }, + ], + /** + * 禁止无用的表达式 + */ + '@typescript-eslint/no-unused-expressions': [ + 'error', + { + allowShortCircuit: true, + allowTernary: true, + allowTaggedTemplates: true, + }, + ], + /** 同 JS 规则的 TS 版本 */ + '@typescript-eslint/no-unused-vars': [ + 'error', + { + args: 'after-used', + ignoreRestSiblings: true, + argsIgnorePattern: '^_.+', + varsIgnorePattern: '^_.+', + }, + ], + /** + * 禁止出现没必要的 constructor + */ + '@typescript-eslint/no-useless-constructor': 'warn', + /** + * 使用 for 循环遍历数组时,如果索引仅用于获取成员,则必须使用 for of 循环替代 for 循环 + * @reason for of 循环更加易读 + */ + '@typescript-eslint/prefer-for-of': 'warn', + /** + * 使用函数类型别名替代包含函数调用声明的接口 + */ + '@typescript-eslint/prefer-function-type': 'warn', + /** + * 禁止使用 module 来定义命名空间 + * @reason module 已成为 js 的关键字 + */ + '@typescript-eslint/prefer-namespace-keyword': 'error', + /** + * 使用 optional chaining 替代 && + */ + '@typescript-eslint/prefer-optional-chain': 'error', + /** 同 JS 规则的 TS 版本 */ + '@stylistic/ts/quotes': [ + 'warn', + 'single', + { + allowTemplateLiterals: false, + }, + ], + /** 同 JS 规则的 TS 版本 */ + '@stylistic/ts/semi': ['error', 'always'], + /** 同 JS 规则的 TS 版本 */ + '@stylistic/ts/space-before-function-paren': [ + 'error', + { + anonymous: 'always', + named: 'never', + asyncArrow: 'always', + }, + ], + /** + * 禁止使用三斜杠导入文件 + * @reason 三斜杠是已废弃的语法,但在类型声明文件中还是可以使用的 + */ + '@typescript-eslint/triple-slash-reference': [ + 'error', + { + path: 'never', + types: 'always', + lib: 'always', + }, + ], + /** + * 在类型注释周围需要一致的间距 + */ + '@stylistic/ts/type-annotation-spacing': 'error', + /** + * interface 和 type 定义时必须声明成员的类型 + */ + '@typescript-eslint/typedef': [ + 'error', + { + arrayDestructuring: false, + arrowParameter: false, + memberVariableDeclaration: false, + objectDestructuring: false, + parameter: false, + propertyDeclaration: true, + variableDeclaration: false, + }, + ], + /** + * 函数重载时,若能通过联合类型将两个函数的类型声明合为一个,则使用联合类型而不是两个函数声明 + */ + '@typescript-eslint/unified-signatures': 'error', + }, +}; diff --git a/eslint-config/index.mjs b/eslint-config/index.mjs new file mode 100644 index 00000000..b3f4ace3 --- /dev/null +++ b/eslint-config/index.mjs @@ -0,0 +1,39 @@ +import js from '@eslint/js'; +import stylistic from '@stylistic/eslint-plugin'; +import stylisticTs from '@stylistic/eslint-plugin-ts'; +import parserTs from '@typescript-eslint/parser'; +import { defineConfig } from 'eslint/config'; +import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; +import pluginVue from 'eslint-plugin-vue'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +import tencentEslintBaseConfig from './flat/base.mjs'; +import tencentEslintImportexport from './flat/import.mjs'; +import ImportSortConfig from './flat/import-sort.mjs'; +import tencentEslintPrettierConfig from './flat/prettier.mjs'; +import tencentEslintTsConfig from './flat/ts.mjs'; + +export default (tsconfigRootDir) => + defineConfig([ + { files: ['**/*.{js,mjs,cjs}'], plugins: { js }, extends: ['js/recommended'] }, + { files: ['**/*.{js,mjs,cjs,ts,vue}'], languageOptions: { globals: { ...globals.browser, ...globals.node } } }, + ...tseslint.config(tencentEslintBaseConfig, tencentEslintImportexport, tseslint.configs.base, { + plugins: { + '@stylistic': stylistic, + '@stylistic/ts': stylisticTs, + }, + languageOptions: { + parser: parserTs, + parserOptions: { + project: tsconfigRootDir, + }, + }, + ...tencentEslintTsConfig, + }), + pluginVue.configs['flat/essential'], + { files: ['**/*.vue'], languageOptions: { parserOptions: { parser: tseslint.parser } } }, + eslintPluginPrettierRecommended, + tencentEslintPrettierConfig, + ImportSortConfig, + ]); diff --git a/eslint-config/package.json b/eslint-config/package.json new file mode 100644 index 00000000..87956646 --- /dev/null +++ b/eslint-config/package.json @@ -0,0 +1,29 @@ +{ + "name": "@tmagic/eslint-config", + "version": "0.0.1", + "main": "index.mjs", + "type": "module", + "repository": { + "directory": "eslint-config", + "type": "git", + "url": "https://github.com/Tencent/tmagic-editor.git" + }, + "dependencies": { + "@eslint/js": "^9.24.0", + "@typescript-eslint/parser": "^8.30.1", + "@typescript-eslint/eslint-plugin": "^8.30.1", + "@stylistic/eslint-plugin": "^4.2.0", + "@stylistic/eslint-plugin-ts": "^4.2.0", + "eslint-config-prettier": "^10.1.2", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-simple-import-sort": "^12.1.1", + "eslint-plugin-vue": "^10.0.0", + "eslint-plugin-prettier": "^5.2.6", + "globals": "^16.0.0", + "typescript-eslint": "^8.30.1" + }, + "peerDependencies": { + "eslint": ">=9.24.0", + "prettier": ">=3.5.3" + } +} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000..8ad0ffe6 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,41 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { defineConfig, globalIgnores } from 'eslint/config'; + +import eslintConfig from '@tmagic/eslint-config'; + +export default defineConfig([ + globalIgnores([ + './docs/**/*', + './packages/cli/lib/**/*', + '*/**/auto-imports.d.ts', + '*/**/components.d.ts', + '*/**/dist/**/*', + '*/**/.tmagic/**/*', + '*/**/public/**/*', + '*/**/types/**/*', + '*/**/*.config.ts', + 'vite-env.d.ts', + ]), + ...eslintConfig(path.join(path.dirname(fileURLToPath(import.meta.url)), 'tsconfig.json')), + { + files: ['**/*.vue'], + rules: { + 'vue/no-mutating-props': 'off', + 'vue/multi-word-component-names': 'off', + }, + }, + { + files: ['**/*.{js,mjs,cjs,ts,vue}'], + rules: { + 'no-param-reassign': 'off', + }, + }, + { + files: ['**/*.tsx'], + rules: { + '@typescript-eslint/naming-convention': 'off', + }, + }, +]); diff --git a/package.json b/package.json index 8f762037..ffeb9f29 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ "clean:top": "rimraf */**/dist */**/types */dist coverage dwt* temp packages/cli/lib", "clean:modules": "rimraf node_modules **/node_modules **/**/node_modules", "clean:all": "pnpm clean:top && pnpm clean:modules", - "lint": "eslint . --ext .js,.vue,.ts,.tsx", - "lint-fix": "eslint . --fix --ext .vue,.js,.ts,.tsx", + "lint": "eslint --cache .", + "lint-fix": "eslint --fix --cache .", "playground": "pnpm --filter \"runtime-vue3\" build:libs && pnpm --filter \"runtime-vue3\" --filter \"tmagic-playground\" dev", "pg": "pnpm playground", "playground:vue2": "pnpm --filter \"runtime-vue2\" build:libs && pnpm --filter \"runtime-vue2\" --filter \"tmagic-playground\" dev:vue2", @@ -46,9 +46,8 @@ "@commitlint/cli": "^18.6.1", "@commitlint/config-conventional": "^18.6.3", "@rollup/plugin-alias": "^5.1.1", + "@tmagic/eslint-config": "workspace: ^*", "@types/node": "18.19.61", - "@typescript-eslint/eslint-plugin": "^5.62.0", - "@typescript-eslint/parser": "^5.62.0", "@vitejs/plugin-vue": "^5.2.3", "@vitest/coverage-v8": "^2.1.9", "@vue/compiler-sfc": "^3.5.13", @@ -59,12 +58,7 @@ "cz-conventional-changelog": "^3.3.0", "element-plus": "^2.9.7", "enquirer": "^2.4.1", - "eslint": "^8.57.1", - "eslint-config-tencent": "^1.1.2", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-simple-import-sort": "^10.0.0", - "eslint-plugin-vue": "^9.33.0", + "eslint": "^9.25.0", "execa": "^4.1.0", "highlight.js": "^11.11.1", "husky": "^7.0.4", @@ -72,7 +66,7 @@ "lint-staged": "^11.2.6", "minimist": "^1.2.8", "picocolors": "^1.1.1", - "prettier": "^2.8.8", + "prettier": "^3.5.3", "recast": "^0.23.11", "rimraf": "^3.0.2", "rollup": "^4.38.0", @@ -93,7 +87,7 @@ } }, "lint-staged": { - "*.{js,ts,vue}": "eslint --fix", + "*.{js,ts,vue}": "eslint --fix --cache", "*.scss": "prettier --write" } } diff --git a/packages/cli/src/Core.ts b/packages/cli/src/Core.ts index 63e280d3..4b4a377f 100644 --- a/packages/cli/src/Core.ts +++ b/packages/cli/src/Core.ts @@ -1,4 +1,4 @@ -import path from 'path'; +import path from 'node:path'; import fs from 'fs-extra'; @@ -6,6 +6,7 @@ import { ModuleMainFilePath, UserConfig } from './types'; import { prepareEntryFile, resolveAppPackages } from './utils'; export default class Core { + // eslint-disable-next-line @typescript-eslint/no-require-imports public version = require('../package.json').version; public options: UserConfig; diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index b1de994d..221f61e5 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -29,6 +29,7 @@ export const cli = (defaultAppConfig: UserConfig): void => { const program = cac('tmagic'); // display core version and cli version + // eslint-disable-next-line @typescript-eslint/no-require-imports const versionCli = require('../package.json').version; program.version(`tmagic/cli@${versionCli}`); diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 0b7dd97f..00e87cfe 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -1,4 +1,4 @@ -import path from 'path'; +import path from 'node:path'; import fs from 'fs-extra'; diff --git a/packages/cli/src/utils/backupPackageFile.ts b/packages/cli/src/utils/backupPackageFile.ts index ca91cc16..9655ddf9 100644 --- a/packages/cli/src/utils/backupPackageFile.ts +++ b/packages/cli/src/utils/backupPackageFile.ts @@ -1,5 +1,5 @@ -import fs from 'fs'; -import path from 'path'; +import fs from 'node:fs'; +import path from 'node:path'; export const backupFile = (runtimeSource: string, file: string) => { const filePath = path.join(runtimeSource, file); diff --git a/packages/cli/src/utils/loadUserConfig.ts b/packages/cli/src/utils/loadUserConfig.ts index 4cc44678..62b43297 100644 --- a/packages/cli/src/utils/loadUserConfig.ts +++ b/packages/cli/src/utils/loadUserConfig.ts @@ -10,6 +10,7 @@ export const hasExportDefault = (mod: unknown): mod is { default: T } = isPlainObject(mod) && !!mod.__esModule && Object.prototype.hasOwnProperty.call(mod, 'default'); export const loadUserConfigCjs: UserConfigLoader = async (userConfigPath) => { + // eslint-disable-next-line @typescript-eslint/no-require-imports const required = require(userConfigPath); return hasExportDefault(required) ? required.default : required; }; diff --git a/packages/cli/src/utils/prepareEntryFile.ts b/packages/cli/src/utils/prepareEntryFile.ts index dad50df5..8a924829 100644 --- a/packages/cli/src/utils/prepareEntryFile.ts +++ b/packages/cli/src/utils/prepareEntryFile.ts @@ -95,7 +95,7 @@ export const prepareEntryFile = async (app: App) => { app.writeTemp(fileName, content); } else { fileName = `${file}.js`; - app.writeTemp(`${file}.d.ts`, `const type: Record;\n\nexport default type;`); + app.writeTemp(`${file}.d.ts`, 'const type: Record;\n\nexport default type;'); } app.writeTemp(fileName, content); }); @@ -136,6 +136,7 @@ export const generateContent = ( }; export const prettyCode = (code: string) => + // eslint-disable-next-line @typescript-eslint/no-require-imports recast.prettyPrint(recast.parse(code.replace(/\\/g, '/'), { parser: require('recast/parsers/typescript') }), { tabWidth: 2, trailingComma: true, diff --git a/packages/cli/src/utils/resolveAppPackages.ts b/packages/cli/src/utils/resolveAppPackages.ts index e7a3a410..bc0ab99f 100644 --- a/packages/cli/src/utils/resolveAppPackages.ts +++ b/packages/cli/src/utils/resolveAppPackages.ts @@ -127,6 +127,7 @@ const typeAssertion = function ({ if (isFile(defaultFile)) { const defaultCode = fs.readFileSync(defaultFile, { encoding: 'utf-8', flag: 'r' }); + // eslint-disable-next-line @typescript-eslint/no-require-imports const ast = recast.parse(defaultCode, { parser: require('recast/parsers/typescript') }); if ( isDatasource( @@ -518,6 +519,7 @@ const setPackages = (packages: ModuleMainFilePath, app: App, packagePath: string .replace('\n', ''); const indexCode = fs.readFileSync(indexPath, { encoding: 'utf-8', flag: 'r' }); + // eslint-disable-next-line @typescript-eslint/no-require-imports const ast: Ast = recast.parse(indexCode, { parser: require('recast/parsers/typescript') }); const result = typeAssertion({ ast, indexPath, componentFileAffix, datasoucreSuperClass }); diff --git a/packages/core/src/Node.ts b/packages/core/src/Node.ts index 9483b96c..b0aa65eb 100644 --- a/packages/core/src/Node.ts +++ b/packages/core/src/Node.ts @@ -148,7 +148,7 @@ class Node extends EventEmitter { } private listenLifeSafe() { - this.once('created', async (instance: any) => { + this.once('created', (instance: any) => { this.once('destroy', () => { this.instance = null; if (typeof this.data.destroy === 'function') { @@ -165,24 +165,27 @@ class Node extends EventEmitter { } } - await this.runHookCode('created'); + this.runHookCode('created'); }); - this.once('mounted', async (instance: any) => { - if (instance) { - this.registerMethod(instance); - if (instance.config) { - this.setData(instance.config); + this.once('mounted', (instance: any) => { + const handler = async () => { + if (instance) { + this.registerMethod(instance); + if (instance.config) { + this.setData(instance.config); + } } - } - for (let eventConfig = this.eventQueue.shift(); eventConfig; eventConfig = this.eventQueue.shift()) { - if (typeof instance[eventConfig.method] === 'function') { - await instance[eventConfig.method](eventConfig.fromCpt, ...eventConfig.args); + for (let eventConfig = this.eventQueue.shift(); eventConfig; eventConfig = this.eventQueue.shift()) { + if (typeof instance[eventConfig.method] === 'function') { + await instance[eventConfig.method](eventConfig.fromCpt, ...eventConfig.args); + } } - } - await this.runHookCode('mounted'); + this.runHookCode('mounted'); + }; + handler(); }); } } diff --git a/packages/data-source/src/DataSourceManager.ts b/packages/data-source/src/DataSourceManager.ts index 939ebb2e..ddccec33 100644 --- a/packages/data-source/src/DataSourceManager.ts +++ b/packages/data-source/src/DataSourceManager.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/naming-convention */ /* * Tencent is pleased to support the open source community by making TMagicEditor available. * diff --git a/packages/data-source/src/utils.ts b/packages/data-source/src/utils.ts index 61f1c95e..3192a942 100644 --- a/packages/data-source/src/utils.ts +++ b/packages/data-source/src/utils.ts @@ -139,6 +139,7 @@ export const compliedDataSourceField = (value: any, data: DataSourceManagerData) try { return getValueByKeyPath(fields.join('.'), dsData); + // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e) { return value; } @@ -151,6 +152,7 @@ export const template = (value: string, data?: DataSourceManagerData) => value.replaceAll(dataSourceTemplateRegExp, (match, $1) => { try { return getValueByKeyPath($1, data); + // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e: any) { return match; } diff --git a/packages/dep/tests/utils.spec.ts b/packages/dep/tests/utils.spec.ts index 6a00e9c0..fdbfa3f3 100644 --- a/packages/dep/tests/utils.spec.ts +++ b/packages/dep/tests/utils.spec.ts @@ -125,7 +125,7 @@ describe('utils', () => { test('isUseDataSourceField', () => { expect(utils.isUseDataSourceField([`${DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}dsID`, 'field'], 'dsID')).toBeTruthy(); expect(utils.isUseDataSourceField([`${DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}dsID`, 'field'], 'dsID1')).toBeFalsy(); - expect(utils.isUseDataSourceField([`abcdsID`, 'field'], 'dsID')).toBeFalsy(); + expect(utils.isUseDataSourceField(['abcdsID', 'field'], 'dsID')).toBeFalsy(); expect(utils.isUseDataSourceField([123, 'field'], 123)).toBeFalsy(); }); diff --git a/packages/design/src/Popover.vue b/packages/design/src/Popover.vue index 302ebfa0..40d2b3d2 100644 --- a/packages/design/src/Popover.vue +++ b/packages/design/src/Popover.vue @@ -27,9 +27,9 @@ import type { PopoverProps } from './types'; defineSlots<{ /** 触发 Popover 显示的 HTML 元素 */ - reference(props: {}): any; + reference(_props: {}): any; /** Popover 内嵌 HTML 文本 */ - default(props: {}): any; + default(_props: {}): any; }>(); defineOptions({ @@ -130,7 +130,7 @@ const mouseenterHandler = () => { popoverVisible.value = true; }; -let timer: NodeJS.Timeout | null = null; +let timer: ReturnType | null = null; const mouseleaveHandler = () => { if (props.disabled) return; diff --git a/packages/design/src/index.ts b/packages/design/src/index.ts index 21da4b41..406f3815 100644 --- a/packages/design/src/index.ts +++ b/packages/design/src/index.ts @@ -9,7 +9,6 @@ import './theme/index.scss'; export * from './types'; export * from './config'; -/* eslint-disable @typescript-eslint/no-unused-vars */ export { default as TMagicAutocomplete } from './Autocomplete.vue'; export { default as TMagicBadge } from './Badge.vue'; export { default as TMagicButton } from './Button.vue'; diff --git a/packages/editor/src/Editor.vue b/packages/editor/src/Editor.vue index 7edfc23b..f37b89bc 100644 --- a/packages/editor/src/Editor.vue +++ b/packages/editor/src/Editor.vue @@ -142,7 +142,7 @@ import codeBlockService from './services/codeBlock'; import componentListService from './services/componentList'; import dataSourceService from './services/dataSource'; import depService from './services/dep'; -import editorService, { type EditorService } from './services/editor'; +import editorService from './services/editor'; import eventsService from './services/events'; import historyService from './services/history'; import keybindingService from './services/keybinding'; @@ -153,25 +153,9 @@ import uiService from './services/ui'; import keybindingConfig from './utils/keybinding-config'; import { defaultEditorProps, EditorProps } from './editorProps'; import { initServiceEvents, initServiceState } from './initService'; -import type { - EventBus, - FrameworkSlots, - PropsPanelSlots, - Services, - SidebarSlots, - StageOptions, - WorkspaceSlots, -} from './type'; +import type { EditorSlots, EventBus, Services, StageOptions } from './type'; -defineSlots< - FrameworkSlots & - WorkspaceSlots & - SidebarSlots & - PropsPanelSlots & { - workspace(props: { editorService: EditorService }): any; - 'workspace-content'(props: { editorService: EditorService }): any; - } ->(); +defineSlots(); defineOptions({ name: 'MEditor', diff --git a/packages/editor/src/components/CodeBlockEditor.vue b/packages/editor/src/components/CodeBlockEditor.vue index 4bcd534a..21541977 100644 --- a/packages/editor/src/components/CodeBlockEditor.vue +++ b/packages/editor/src/components/CodeBlockEditor.vue @@ -233,7 +233,7 @@ const changeHandler = (values: CodeBlockContent) => { changedValue.value = values; }; -const beforeClose = (done: (cancel?: boolean) => void) => { +const beforeClose = (done: (_cancel?: boolean) => void) => { if (!changedValue.value) { done(); return; diff --git a/packages/editor/src/components/FloatingBox.vue b/packages/editor/src/components/FloatingBox.vue index d4b3a4bc..c55abbf0 100644 --- a/packages/editor/src/components/FloatingBox.vue +++ b/packages/editor/src/components/FloatingBox.vue @@ -39,7 +39,7 @@ const props = withDefaults( defineProps<{ position?: Position; title?: string; - beforeClose?: (done: (cancel?: boolean) => void) => void; + beforeClose?: (_done: (_cancel?: boolean) => void) => void; }>(), { title: '', diff --git a/packages/editor/src/components/ScrollBar.vue b/packages/editor/src/components/ScrollBar.vue index 772fcc00..18399d62 100644 --- a/packages/editor/src/components/ScrollBar.vue +++ b/packages/editor/src/components/ScrollBar.vue @@ -93,7 +93,9 @@ const scrollBy = (delta: number) => { position: absolute; background-color: transparent; opacity: 0.3; - transition: background-color 0.2s linear, opacity 0.2s linear; + transition: + background-color 0.2s linear, + opacity 0.2s linear; .m-editor-scroll-bar-thumb { background-color: #aaa; @@ -108,7 +110,9 @@ const scrollBy = (delta: number) => { .m-editor-scroll-bar-thumb { height: 6px; - transition: background-color 0.2s linear, height 0.2s ease-in-out; + transition: + background-color 0.2s linear, + height 0.2s ease-in-out; bottom: 2px; } } @@ -120,7 +124,9 @@ const scrollBy = (delta: number) => { .m-editor-scroll-bar-thumb { width: 6px; - transition: background-color 0.2s linear, width 0.2s ease-in-out; + transition: + background-color 0.2s linear, + width 0.2s ease-in-out; right: 2px; } } diff --git a/packages/editor/src/components/SearchInput.vue b/packages/editor/src/components/SearchInput.vue index cfe0d28e..abc7935e 100644 --- a/packages/editor/src/components/SearchInput.vue +++ b/packages/editor/src/components/SearchInput.vue @@ -27,7 +27,7 @@ const emit = defineEmits(['search']); const filterText = ref(''); -let timer: NodeJS.Timeout | null = null; +let timer: ReturnType | null = null; const filterTextChangeHandler = () => { timer && clearTimeout(timer); timer = setTimeout(() => { diff --git a/packages/editor/src/components/Tree.vue b/packages/editor/src/components/Tree.vue index 9e0759e2..6e543d33 100644 --- a/packages/editor/src/components/Tree.vue +++ b/packages/editor/src/components/Tree.vue @@ -38,9 +38,9 @@ import type { LayerNodeStatus, TreeNodeData } from '@editor/type'; import TreeNode from './TreeNode.vue'; defineSlots<{ - 'tree-node-content'(props: { data: TreeNodeData }): any; - 'tree-node-label'(props: { data: TreeNodeData }): any; - 'tree-node-tool'(props: { data: TreeNodeData }): any; + 'tree-node-content'(_props: { data: TreeNodeData }): any; + 'tree-node-label'(_props: { data: TreeNodeData }): any; + 'tree-node-tool'(_props: { data: TreeNodeData }): any; }>(); defineOptions({ diff --git a/packages/editor/src/components/TreeNode.vue b/packages/editor/src/components/TreeNode.vue index 5626d770..958e7364 100644 --- a/packages/editor/src/components/TreeNode.vue +++ b/packages/editor/src/components/TreeNode.vue @@ -72,9 +72,9 @@ import type { LayerNodeStatus, TreeNodeData } from '@editor/type'; import { updateStatus } from '@editor/utils/tree'; defineSlots<{ - 'tree-node-label'(props: { data: TreeNodeData }): any; - 'tree-node-tool'(props: { data: TreeNodeData }): any; - 'tree-node-content'(props: { data: TreeNodeData }): any; + 'tree-node-label'(_props: { data: TreeNodeData }): any; + 'tree-node-tool'(_props: { data: TreeNodeData }): any; + 'tree-node-content'(_props: { data: TreeNodeData }): any; }>(); defineOptions({ diff --git a/packages/editor/src/fields/DataSourceInput.vue b/packages/editor/src/fields/DataSourceInput.vue index 49592024..e27b2faa 100644 --- a/packages/editor/src/fields/DataSourceInput.vue +++ b/packages/editor/src/fields/DataSourceInput.vue @@ -179,7 +179,11 @@ const curCharIsDot = (dotIndex: number) => dotIndex > -1 && dotIndex === getSele * @param leftCurlyBracketIndex 左大括号字符索引 * @param cb 建议的方法 */ -const dsQuerySearch = (queryString: string, leftCurlyBracketIndex: number, cb: (data: { value: string }[]) => void) => { +const dsQuerySearch = ( + queryString: string, + leftCurlyBracketIndex: number, + cb: (_data: { value: string }[]) => void, +) => { let result: DataSourceSchema[] = []; if (curCharIsLeftCurlyBracket(leftCurlyBracketIndex)) { @@ -211,7 +215,7 @@ const fieldQuerySearch = ( queryString: string, leftAngleIndex: number, dotIndex: number, - cb: (data: { value: string }[]) => void, + cb: (_data: { value: string }[]) => void, ) => { let result: DataSchema[] = []; @@ -272,7 +276,7 @@ const fieldQuerySearch = ( * @param queryString 当前输入框内的字符串 * @param cb 建议回调 */ -const querySearch = (queryString: string, cb: (data: { value: string }[]) => void) => { +const querySearch = (queryString: string, cb: (_data: { value: string }[]) => void) => { inputText = queryString; const selectionStart = getSelectionStart(); diff --git a/packages/editor/src/fields/DataSourceMethods.vue b/packages/editor/src/fields/DataSourceMethods.vue index d192dd37..2680a65d 100644 --- a/packages/editor/src/fields/DataSourceMethods.vue +++ b/packages/editor/src/fields/DataSourceMethods.vue @@ -80,7 +80,7 @@ const methodColumns: ColumnConfig[] = [ { text: '编辑', handler: (method: CodeBlockContent, index: number) => { - let codeContent = method.content || `({ params, dataSource, app }) => {\n // place your code here\n}`; + let codeContent = method.content || '({ params, dataSource, app }) => {\n // place your code here\n}'; if (typeof codeContent !== 'string') { codeContent = codeContent.toString(); @@ -114,7 +114,7 @@ const methodColumns: ColumnConfig[] = [ const createCodeHandler = () => { codeConfig.value = { name: '', - content: `({ params, dataSource, app, flowState }) => {\n // place your code here\n}`, + content: '({ params, dataSource, app, flowState }) => {\n // place your code here\n}', params: [], }; @@ -130,7 +130,7 @@ const submitCodeHandler = (value: CodeBlockContent, data: ContainerChangeEventDa // 在保存的时候转换代码内容 const parseDSL = getEditorConfig('parseDSL'); if (typeof value.content === 'string') { - value.content = parseDSL<(...args: any[]) => any>(value.content); + value.content = parseDSL<(..._args: any[]) => any>(value.content); } } if (editIndex > -1) { diff --git a/packages/editor/src/fields/UISelect.vue b/packages/editor/src/fields/UISelect.vue index 6cd79554..35275f67 100644 --- a/packages/editor/src/fields/UISelect.vue +++ b/packages/editor/src/fields/UISelect.vue @@ -70,7 +70,7 @@ const uiSelectMode = ref(false); const cancelHandler = () => { uiService.set('uiSelectMode', false); uiSelectMode.value = false; - globalThis.document.removeEventListener(UI_SELECT_MODE_EVENT_NAME, clickHandler as EventListener); + globalThis.document.removeEventListener(UI_SELECT_MODE_EVENT_NAME, clickHandler as any); }; const clickHandler = ({ detail }: Event & { detail: HTMLElement | MNode }) => { @@ -97,7 +97,7 @@ const toName = computed(() => { const startSelect = () => { uiService.set('uiSelectMode', true); uiSelectMode.value = true; - globalThis.document.addEventListener(UI_SELECT_MODE_EVENT_NAME, clickHandler as EventListener); + globalThis.document.addEventListener(UI_SELECT_MODE_EVENT_NAME, clickHandler as any); }; const deleteHandler = () => { diff --git a/packages/editor/src/hooks/use-code-block-edit.ts b/packages/editor/src/hooks/use-code-block-edit.ts index d644a25b..9a6df770 100644 --- a/packages/editor/src/hooks/use-code-block-edit.ts +++ b/packages/editor/src/hooks/use-code-block-edit.ts @@ -16,7 +16,7 @@ export const useCodeBlockEdit = (codeBlockService: Services['codeBlockService']) const createCodeBlock = async () => { codeConfig.value = { name: '', - content: `({app, params, flowState}) => {\n // place your code here\n}`, + content: '({app, params, flowState}) => {\n // place your code here\n}', params: [], }; diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index a6d0cbfc..55f24ee2 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -122,7 +122,6 @@ export default { app.use(formPlugin, opt || {}); app.use(tablePlugin); - // eslint-disable-next-line no-param-reassign app.config.globalProperties.$TMAGIC_EDITOR = option; setEditorConfig(option); diff --git a/packages/editor/src/initService.ts b/packages/editor/src/initService.ts index 2751c90c..b2ef21a3 100644 --- a/packages/editor/src/initService.ts +++ b/packages/editor/src/initService.ts @@ -269,13 +269,13 @@ export const initServiceEvents = ( } }; - const dsDepCollectedHandler = async () => { + const dsDepCollectedHandler = () => { const root = editorService.get('root'); - const app = await getTMagicApp(); - - if (root && app?.dsl) { - app.dsl.dataSourceDeps = root.dataSourceDeps; - } + getTMagicApp()?.then((app?: TMagicCore) => { + if (root && app?.dsl) { + app.dsl.dataSourceDeps = root.dataSourceDeps; + } + }); }; const collectIdle = (nodes: MComponent[], deep: boolean, type?: DepTargetType) => @@ -317,7 +317,7 @@ export const initServiceEvents = ( depService.addTarget(createDataSourceCondTarget(ds, reactive({}))); }; - const rootChangeHandler = async (value: MApp | null, preValue?: MApp | null) => { + const rootChangeHandler = (value: MApp | null, preValue?: MApp | null) => { if (!value) return; value.codeBlocks = value.codeBlocks || {}; @@ -347,39 +347,40 @@ export const initServiceEvents = ( delete value.dataSourceCondDeps; } - const nodeId = editorService.get('node')?.id || props.defaultSelected; - let node; - if (nodeId) { - node = editorService.getNodeById(nodeId); - } + const handler = async () => { + const nodeId = editorService.get('node')?.id || props.defaultSelected; + let node; + if (nodeId) { + node = editorService.getNodeById(nodeId); + } + if (node && node !== value) { + await editorService.select(node.id); + } else if (value.items?.length) { + await editorService.select(value.items[0]); + } else if (value.id) { + editorService.set('nodes', [value]); + editorService.set('parent', null); + editorService.set('page', null); + } - if (node && node !== value) { - await editorService.select(node.id); - } else if (value.items?.length) { - await editorService.select(value.items[0]); - } else if (value.id) { - editorService.set('nodes', [value]); - editorService.set('parent', null); - editorService.set('page', null); - } + if (toRaw(value) !== toRaw(preValue)) { + emit('update:modelValue', value); + } + }; - if (toRaw(value) !== toRaw(preValue)) { - emit('update:modelValue', value); - } + handler(); }; // 新增节点,收集依赖 - const nodeAddHandler = async (nodes: MComponent[]) => { - await collectIdle(nodes, true); - - updateStageNodes(nodes); + const nodeAddHandler = (nodes: MComponent[]) => { + collectIdle(nodes, true).then(() => { + updateStageNodes(nodes); + }); }; // 节点更新,收集依赖 // 仅当修改到数据源相关的才收集 - const nodeUpdateHandler = async ( - data: { newNode: MComponent; oldNode: MComponent; changeRecords?: ChangeRecord[] }[], - ) => { + const nodeUpdateHandler = (data: { newNode: MComponent; oldNode: MComponent; changeRecords?: ChangeRecord[] }[]) => { const needRecollectNodes: MComponent[] = []; const normalNodes: MComponent[] = []; for (const { newNode, oldNode, changeRecords } of data) { @@ -424,9 +425,12 @@ export const initServiceEvents = ( if (needRecollectNodes.length) { // 有数据源依赖,需要等依赖重新收集完才更新stage - await collectIdle(needRecollectNodes, true, DepTargetType.DATA_SOURCE); - await collectIdle(needRecollectNodes, true, DepTargetType.DATA_SOURCE_COND); - updateStageNodes(needRecollectNodes); + const handler = async () => { + await collectIdle(needRecollectNodes, true, DepTargetType.DATA_SOURCE); + await collectIdle(needRecollectNodes, true, DepTargetType.DATA_SOURCE_COND); + updateStageNodes(needRecollectNodes); + }; + handler(); } else { updateStageNodes(normalNodes); // 在上面判断是否需要收集数据源依赖中已经更新stage @@ -443,9 +447,10 @@ export const initServiceEvents = ( }; // 由于历史记录变化是更新整个page,所以历史记录变化时,需要重新收集依赖 - const historyChangeHandler = async (page: MPage | MPageFragment) => { - await collectIdle([page], true); - updateStageNode(page); + const historyChangeHandler = (page: MPage | MPageFragment) => { + collectIdle([page], true).then(() => { + updateStageNode(page); + }); }; editorService.on('history-change', historyChangeHandler); @@ -454,27 +459,28 @@ export const initServiceEvents = ( editorService.on('remove', nodeRemoveHandler); editorService.on('update', nodeUpdateHandler); - const dataSourceAddHandler = async (config: DataSourceSchema) => { - initDataSourceDepTarget(config); - const app = await getTMagicApp(); + const dataSourceAddHandler = (config: DataSourceSchema) => { + const handler = async () => { + initDataSourceDepTarget(config); + const app = await getTMagicApp(); - if (!app?.dataSourceManager) { - return; - } + if (!app?.dataSourceManager) { + return; + } - app.dataSourceManager.addDataSource(config); + app.dataSourceManager.addDataSource(config); - const newDs = app.dataSourceManager.get(config.id); + const newDs = app.dataSourceManager.get(config.id); - if (newDs) { - app.dataSourceManager.init(newDs); - } + if (newDs) { + app.dataSourceManager.init(newDs); + } + }; + + handler(); }; - const dataSourceUpdateHandler = async ( - config: DataSourceSchema, - { changeRecords }: { changeRecords: ChangeRecord[] }, - ) => { + const dataSourceUpdateHandler = (config: DataSourceSchema, { changeRecords }: { changeRecords: ChangeRecord[] }) => { const updateDsData = async () => { const app = await getTMagicApp(); @@ -565,29 +571,33 @@ export const initServiceEvents = ( depService.removeTarget(id, DepTargetType.DATA_SOURCE_METHOD); }; - const dataSourceRemoveHandler = async (id: string) => { + const dataSourceRemoveHandler = (id: string) => { const root = editorService.get('root'); if (!root) { return; } - const nodeIds = Object.keys(root.dataSourceDeps?.[id] || {}); - const nodes = getNodes(nodeIds, root.items); + const handler = async () => { + const nodeIds = Object.keys(root.dataSourceDeps?.[id] || {}); + const nodes = getNodes(nodeIds, root.items); - await Promise.all([ - collectIdle(nodes, false, DepTargetType.DATA_SOURCE), - collectIdle(nodes, false, DepTargetType.DATA_SOURCE_COND), - collectIdle(nodes, false, DepTargetType.DATA_SOURCE_METHOD), - ]); + await Promise.all([ + collectIdle(nodes, false, DepTargetType.DATA_SOURCE), + collectIdle(nodes, false, DepTargetType.DATA_SOURCE_COND), + collectIdle(nodes, false, DepTargetType.DATA_SOURCE_METHOD), + ]); - updateDataSourceSchema(); + updateDataSourceSchema(); - const app = await getTMagicApp(); - app?.dataSourceManager?.removeDataSource(id); + const app = await getTMagicApp(); + app?.dataSourceManager?.removeDataSource(id); - updateStageNodes(nodes); - removeDataSourceTarget(id); + updateStageNodes(nodes); + removeDataSourceTarget(id); + }; + + handler(); }; dataSourceService.on('add', dataSourceAddHandler); diff --git a/packages/editor/src/layouts/Framework.vue b/packages/editor/src/layouts/Framework.vue index 49ee2a92..123fc189 100644 --- a/packages/editor/src/layouts/Framework.vue +++ b/packages/editor/src/layouts/Framework.vue @@ -91,7 +91,7 @@ defineOptions({ defineProps<{ disabledPageFragment: boolean; pageBarSortOptions?: PageBarSortOptions; - pageFilterFunction?: (page: MPage | MPageFragment, keyword: string) => boolean; + pageFilterFunction?: (_page: MPage | MPageFragment, _keyword: string) => boolean; }>(); const codeOptions = inject('codeOptions', {}); diff --git a/packages/editor/src/layouts/NavMenu.vue b/packages/editor/src/layouts/NavMenu.vue index 5588480b..6fa5da3a 100644 --- a/packages/editor/src/layouts/NavMenu.vue +++ b/packages/editor/src/layouts/NavMenu.vue @@ -69,7 +69,7 @@ const getConfig = (item: MenuItem): (MenuButton | MenuComponent)[] => { type: 'button', className: 'delete', icon: markRaw(Delete), - tooltip: `刪除(Delete)`, + tooltip: '刪除(Delete)', disabled: () => editorService.get('node')?.type === NodeType.PAGE, handler: () => { const node = editorService.get('node'); diff --git a/packages/editor/src/layouts/page-bar/PageBar.vue b/packages/editor/src/layouts/page-bar/PageBar.vue index dcd2e482..e6b83b61 100644 --- a/packages/editor/src/layouts/page-bar/PageBar.vue +++ b/packages/editor/src/layouts/page-bar/PageBar.vue @@ -91,7 +91,7 @@ const props = withDefaults( defineProps<{ disabledPageFragment: boolean; pageBarSortOptions?: PageBarSortOptions; - filterFunction?: (page: MPage | MPageFragment, keyword: string) => boolean; + filterFunction?: (_page: MPage | MPageFragment, _keyword: string) => boolean; }>(), { filterFunction: (page, keyword) => page.name?.includes(keyword) || `${page.id}`.includes(keyword), diff --git a/packages/editor/src/layouts/props-panel/FormPanel.vue b/packages/editor/src/layouts/props-panel/FormPanel.vue index 65c03a6f..92d23e4e 100644 --- a/packages/editor/src/layouts/props-panel/FormPanel.vue +++ b/packages/editor/src/layouts/props-panel/FormPanel.vue @@ -56,7 +56,7 @@ import { useServices } from '@editor/hooks/use-services'; import CodeEditor from '../CodeEditor.vue'; defineSlots<{ - 'props-form-panel-header'(props: {}): any; + 'props-form-panel-header'(_props: {}): any; }>(); defineOptions({ @@ -70,7 +70,7 @@ const props = defineProps<{ labelWidth?: string; codeValueKey?: string; labelPosition?: string; - extendState?: (state: FormState) => Record | Promise>; + extendState?: (_state: FormState) => Record | Promise>; }>(); const emit = defineEmits<{ diff --git a/packages/editor/src/layouts/props-panel/PropsPanel.vue b/packages/editor/src/layouts/props-panel/PropsPanel.vue index ba9c2c4b..4a9b56d0 100644 --- a/packages/editor/src/layouts/props-panel/PropsPanel.vue +++ b/packages/editor/src/layouts/props-panel/PropsPanel.vue @@ -82,7 +82,7 @@ defineOptions({ defineProps<{ disabledShowSrc?: boolean; - extendState?: (state: FormState) => Record | Promise>; + extendState?: (_state: FormState) => Record | Promise>; }>(); const emit = defineEmits<{ diff --git a/packages/editor/src/layouts/sidebar/ComponentListPanel.vue b/packages/editor/src/layouts/sidebar/ComponentListPanel.vue index 28aca978..abe1fe3c 100644 --- a/packages/editor/src/layouts/sidebar/ComponentListPanel.vue +++ b/packages/editor/src/layouts/sidebar/ComponentListPanel.vue @@ -80,7 +80,7 @@ const collapseValue = computed(() => .map((x, i) => `${i}`), ); -let timeout: NodeJS.Timeout | undefined; +let timeout: ReturnType | undefined; let clientX: number; let clientY: number; diff --git a/packages/editor/src/layouts/sidebar/code-block/CodeBlockList.vue b/packages/editor/src/layouts/sidebar/code-block/CodeBlockList.vue index c0ab5367..8f9376eb 100644 --- a/packages/editor/src/layouts/sidebar/code-block/CodeBlockList.vue +++ b/packages/editor/src/layouts/sidebar/code-block/CodeBlockList.vue @@ -55,7 +55,7 @@ defineOptions({ const props = defineProps<{ indent?: number; nextLevelIndentIncrement?: number; - customError?: (id: Id, errorType: CodeDeleteErrorType) => any; + customError?: (_id: Id, _errorType: CodeDeleteErrorType) => any; }>(); const emit = defineEmits<{ diff --git a/packages/editor/src/layouts/sidebar/code-block/CodeBlockListPanel.vue b/packages/editor/src/layouts/sidebar/code-block/CodeBlockListPanel.vue index bd8ee66d..945c8274 100644 --- a/packages/editor/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +++ b/packages/editor/src/layouts/sidebar/code-block/CodeBlockListPanel.vue @@ -79,7 +79,7 @@ defineOptions({ const props = defineProps<{ indent?: number; nextLevelIndentIncrement?: number; - customError?: (id: Id, errorType: CodeDeleteErrorType) => any; + customError?: (_id: Id, _errorType: CodeDeleteErrorType) => any; customContentMenu: CustomContentMenuFunction; }>(); diff --git a/packages/editor/src/layouts/workspace/viewer/Stage.vue b/packages/editor/src/layouts/workspace/viewer/Stage.vue index d98d5ea3..9b2ae33b 100644 --- a/packages/editor/src/layouts/workspace/viewer/Stage.vue +++ b/packages/editor/src/layouts/workspace/viewer/Stage.vue @@ -149,7 +149,7 @@ watch(zoom, (zoom) => { stage.setZoom(zoom); }); -let timeoutId: NodeJS.Timeout | null = null; +let timeoutId: ReturnType | null = null; watch(page, (page) => { if (runtime && page) { editorService.set('stageLoading', true); diff --git a/packages/editor/src/services/BaseService.ts b/packages/editor/src/services/BaseService.ts index 9cd18ca7..20bd6805 100644 --- a/packages/editor/src/services/BaseService.ts +++ b/packages/editor/src/services/BaseService.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-misused-promises */ /* * Tencent is pleased to support the open source community by making TMagicEditor available. * diff --git a/packages/editor/src/services/editor.ts b/packages/editor/src/services/editor.ts index 260be3a8..8cfd36e4 100644 --- a/packages/editor/src/services/editor.ts +++ b/packages/editor/src/services/editor.ts @@ -1088,10 +1088,11 @@ class Editor extends BaseService { this.isHistoryStateChange = true; await this.update(value.data); this.set('modifiedNodeIds', value.modifiedNodeIds); - setTimeout(async () => { + setTimeout(() => { if (!value.nodeId) return; - await this.select(value.nodeId); - this.get('stage')?.select(value.nodeId); + this.select(value.nodeId).then(() => { + this.get('stage')?.select(value.nodeId); + }); }, 0); this.emit('history-change', value.data); } diff --git a/packages/editor/src/type.ts b/packages/editor/src/type.ts index 339ae008..096c724d 100644 --- a/packages/editor/src/type.ts +++ b/packages/editor/src/type.ts @@ -65,6 +65,15 @@ import type { StageOverlayService } from './services/stageOverlay'; import type { StorageService } from './services/storage'; import type { UiService } from './services/ui'; import type { UndoRedo } from './utils/undo-redo'; + +export type EditorSlots = FrameworkSlots & + WorkspaceSlots & + SidebarSlots & + PropsPanelSlots & { + workspace(props: { editorService: EditorService }): any; + 'workspace-content'(props: { editorService: EditorService }): any; + }; + export interface FrameworkSlots { header(props: {}): any; nav(props: {}): any; diff --git a/packages/editor/src/utils/props.ts b/packages/editor/src/utils/props.ts index 0f1d954b..b27c7784 100644 --- a/packages/editor/src/utils/props.ts +++ b/packages/editor/src/utils/props.ts @@ -179,13 +179,15 @@ export const fillConfig = (config: FormConfig = [], labelWidth = '80px'): FormCo append: { type: 'button', text: '复制', - handler: async (vm, { model }) => { - try { - await navigator.clipboard.writeText(`${model.id}`); - tMagicMessage.success('已复制'); - } catch (err) { - tMagicMessage.error('复制失败'); - } + handler: (vm, { model }) => { + navigator.clipboard + .writeText(`${model.id}`) + .then(() => { + tMagicMessage.success('已复制'); + }) + .catch(() => { + tMagicMessage.error('复制失败'); + }); }, }, }, diff --git a/packages/form/src/Form.vue b/packages/form/src/Form.vue index aae3f495..3824d78a 100644 --- a/packages/form/src/Form.vue +++ b/packages/form/src/Form.vue @@ -63,7 +63,7 @@ const props = withDefaults( keyProp?: string; popperClass?: string; preventSubmitDefault?: boolean; - extendState?: (state: FormState) => Record | Promise>; + extendState?: (_state: FormState) => Record | Promise>; }>(), { config: () => [], @@ -101,7 +101,7 @@ const formState: FormState = reactive({ parentValues: props.parentValues, values, lastValuesProcessed, - $emit: emit as (event: string, ...args: any[]) => void, + $emit: emit as (_event: string, ..._args: any[]) => void, fields, setField: (prop: string, field: any) => fields.set(prop, field), getField: (prop: string) => fields.get(prop), diff --git a/packages/form/src/FormDrawer.vue b/packages/form/src/FormDrawer.vue index 649473de..c7ad3a00 100644 --- a/packages/form/src/FormDrawer.vue +++ b/packages/form/src/FormDrawer.vue @@ -82,7 +82,7 @@ withDefaults( labelPosition?: string; preventSubmitDefault?: boolean; /** 关闭前的回调,会暂停 Drawer 的关闭; done 是个 function type 接受一个 boolean 参数, 执行 done 使用 true 参数或不提供参数将会终止关闭 */ - beforeClose?: (done: (cancel?: boolean) => void) => void; + beforeClose?: (_done: (_cancel?: boolean) => void) => void; }>(), { closeOnPressEscape: true, diff --git a/packages/form/src/containers/Container.vue b/packages/form/src/containers/Container.vue index e3d334db..55a1e21c 100644 --- a/packages/form/src/containers/Container.vue +++ b/packages/form/src/containers/Container.vue @@ -449,7 +449,6 @@ const onChangeHandler = async function (v: any, eventData: ContainerChangeEventD valueProp = valueProp ? `${valueProp}.${eventData.modifyKey}` : eventData.modifyKey!; // 需要清除掉modifyKey,不然往上层抛出后还会被认为需要修改 - // eslint-disable-next-line no-param-reassign delete eventData.modifyKey; } else if (isValidName() && props.model !== value && (v !== value || props.model[name.value] !== value)) { // field内容下包含field-link时,model===value, 这里避免循环引用 diff --git a/packages/form/src/containers/Table.vue b/packages/form/src/containers/Table.vue index 27e4c19c..c047b844 100644 --- a/packages/form/src/containers/Table.vue +++ b/packages/form/src/containers/Table.vue @@ -273,22 +273,18 @@ const isFullscreen = ref(false); const modelName = computed(() => props.name || props.config.name || ''); -const data = computed(() => - props.config.pagination - ? (props.model[modelName.value] || []).filter( - (item: any, index: number) => - index >= pagecontext.value * pagesize.value && index + 1 <= (pagecontext.value + 1) * pagesize.value, - ) - : props.model[modelName.value], -); +const getDataByPage = (data: any[] = []) => + data.filter( + (item: any, index: number) => + index >= pagecontext.value * pagesize.value && index + 1 <= (pagecontext.value + 1) * pagesize.value, + ); + +const pageinationData = computed(() => getDataByPage(props.model[modelName.value])); + +const data = computed(() => (props.config.pagination ? pageinationData.value : props.model[modelName.value])); const lastData = computed(() => - props.config.pagination - ? (props.lastValues[modelName.value] || []).filter( - (item: any, index: number) => - index >= pagecontext.value * pagesize.value && index + 1 <= (pagecontext.value + 1) * pagesize.value, - ) - : props.lastValues[modelName.value] || [], + props.config.pagination ? getDataByPage(props.lastValues[modelName.value]) : props.lastValues[modelName.value] || [], ); const sortChange = ({ prop, order }: SortProp) => { diff --git a/packages/form/src/containers/Tabs.vue b/packages/form/src/containers/Tabs.vue index a5632c3d..f5d77f87 100644 --- a/packages/form/src/containers/Tabs.vue +++ b/packages/form/src/containers/Tabs.vue @@ -41,21 +41,21 @@ config.dynamic ? (name ? model[name] : model)[tabIndex] : tab.name - ? (name ? model[name] : model)[tab.name] - : name - ? model[name] - : model + ? (name ? model[name] : model)[tab.name] + : name + ? model[name] + : model " :last-values=" isEmpty(lastValues) ? {} : config.dynamic - ? (name ? lastValues[name] : lastValues)[tabIndex] - : tab.name - ? (name ? lastValues[name] : lastValues)[tab.name] - : name - ? lastValues[name] - : lastValues + ? (name ? lastValues[name] : lastValues)[tabIndex] + : tab.name + ? (name ? lastValues[name] : lastValues)[tab.name] + : name + ? lastValues[name] + : lastValues " :is-compare="isCompare" :prop="config.dynamic ? `${prop}${prop ? '.' : ''}${String(tabIndex)}` : prop" diff --git a/packages/form/src/fields/Display.vue b/packages/form/src/fields/Display.vue index d3073d2c..c9bb96ae 100644 --- a/packages/form/src/fields/Display.vue +++ b/packages/form/src/fields/Display.vue @@ -13,7 +13,6 @@ defineOptions({ const props = defineProps>(); if (props.config.initValue && props.model) { - // eslint-disable-next-line vue/no-setup-props-destructure props.model[props.name] = props.config.initValue; } diff --git a/packages/form/src/fields/Select.vue b/packages/form/src/fields/Select.vue index bdb110dc..ecd64a63 100644 --- a/packages/form/src/fields/Select.vue +++ b/packages/form/src/fields/Select.vue @@ -21,7 +21,7 @@ >