chore: upgrade deps & code style

This commit is contained in:
buqiyuan 2022-07-03 02:38:58 +08:00
parent 9a05f858db
commit 74df8be4f2
119 changed files with 4233 additions and 3088 deletions

5
.browserslistrc Normal file
View File

@ -0,0 +1,5 @@
> 1%
last 2 versions
not dead
not ie 11
chrome 79

View File

@ -12,4 +12,9 @@ dist
.husky .husky
.local .local
/bin /bin
/src/mock
Dockerfile Dockerfile
commitlint.config.js
components.d.ts
auto-imports.d.ts

View File

@ -1,6 +1,4 @@
// @ts-check module.exports = {
const { defineConfig } = require('eslint-define-config');
module.exports = defineConfig({
root: true, root: true,
env: { env: {
browser: true, browser: true,
@ -15,28 +13,50 @@ module.exports = defineConfig({
jsxPragma: 'React', jsxPragma: 'React',
ecmaFeatures: { ecmaFeatures: {
jsx: true, jsx: true,
tsx: true,
}, },
}, },
plugins: ['@typescript-eslint', 'prettier', 'import'],
extends: [ extends: [
'plugin:vue/vue3-recommended', 'eslint:recommended',
'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-recommended',
'prettier', 'prettier',
'plugin:prettier/recommended', ],
overrides: [
{
files: ['*.ts', '*.tsx', '*.vue'],
rules: {
'no-undef': 'off',
},
},
], ],
rules: { rules: {
'vue/script-setup-uses-vars': 'error', // js/ts
'@typescript-eslint/ban-ts-ignore': 'off', // 'no-console': ['warn', { allow: ['error'] }],
'@typescript-eslint/explicit-function-return-type': 'off', 'no-restricted-syntax': ['error', 'LabeledStatement', 'WithStatement'],
'@typescript-eslint/no-explicit-any': 'off', camelcase: ['error', { properties: 'never' }],
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off', 'no-var': 'error',
'vue/custom-event-name-casing': 'off', 'no-empty': ['error', { allowEmptyCatch: true }],
'no-use-before-define': 'off', 'no-void': 'error',
'@typescript-eslint/no-use-before-define': 'off', 'prefer-const': ['warn', { destructuring: 'all', ignoreReadBeforeAssign: true }],
'prefer-template': 'error',
'object-shorthand': ['error', 'always', { ignoreConstructors: false, avoidQuotes: true }],
'block-scoped-var': 'error',
'no-constant-condition': ['error', { checkLoops: false }],
'no-redeclare': 'off',
'@typescript-eslint/no-redeclare': 'error',
'@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off', '@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
// '@typescript-eslint/consistent-type-imports': ['error', { disallowTypeAnnotations: false }],
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-unused-vars': [ '@typescript-eslint/no-unused-vars': [
'error', 'error',
{ {
@ -51,29 +71,43 @@ module.exports = defineConfig({
varsIgnorePattern: '^_', varsIgnorePattern: '^_',
}, },
], ],
'space-before-function-paren': 'off',
'vue/attributes-order': 'off', // vue
'vue/one-component-per-file': 'off', 'vue/no-v-html': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off', 'vue/require-default-prop': 'off',
'vue/require-explicit-emits': 'off', 'vue/require-explicit-emits': 'off',
'vue/html-self-closing': [ 'vue/multi-word-component-names': 'off',
'vue/one-component-per-file': 'off',
// prettier
'prettier/prettier': 'error',
// import
'import/first': 'error',
'import/no-duplicates': 'error',
'import/order': [
'error', 'error',
{ {
html: { groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
void: 'always',
normal: 'never', pathGroups: [
component: 'always', {
pattern: 'vue',
group: 'external',
position: 'before',
}, },
svg: 'always', {
math: 'always', pattern: '@vue/**',
group: 'external',
position: 'before',
},
{
pattern: 'ant-design-vue',
group: 'internal',
}, },
], ],
'vue/multi-word-component-names': 'off', pathGroupsExcludedImportTypes: ['type'],
}, },
}); ],
},
};

6
.gitattributes vendored Normal file
View File

@ -0,0 +1,6 @@
* text=auto eol=lf
*.ts linguist-detectable=false
*.css linguist-detectable=false
*.scss linguist-detectable=false
*.js linguist-detectable=true
*.vue linguist-detectable=true

View File

@ -6,30 +6,48 @@ env:
on: on:
push: push:
branches: [main] branches: [main]
jobs: jobs:
repo-sync: repo-sync:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout Code - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
# 设置服务器时区为东八区
- name: Set time zone
run: sudo timedatectl set-timezone 'Asia/Shanghai'
- name: Setup node
uses: actions/setup-node@v3
with: with:
# Full git history is needed to get a proper list of changed files within `super-linter` node-version: '16'
fetch-depth: 0 registry-url: https://registry.npmjs.com/
- name: Setup Node.js v14.x
uses: actions/setup-node@v2
with:
node-version: '14.x'
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v2 uses: pnpm/action-setup@v2
with: with:
version: latest version: latest
- name: Install - name: Cache ~/.pnpm-store
run: pnpm install uses: actions/cache@v3
env:
cache-name: cache-pnpm-store
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-preview-${{ env.cache-name }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-preview-${{ env.cache-name }}-
${{ runner.os }}-preview-
${{ runner.os }}-
- name: Install dependencies
run: pnpm i --frozen-lockfile
- name: Build - name: Build
run: pnpm build run: pnpm build
env:
FORCE_COLOR: 2
- name: Deploy - name: Deploy
uses: peaceiris/actions-gh-pages@v3 uses: peaceiris/actions-gh-pages@v3
@ -60,6 +78,6 @@ jobs:
# 注意替换为你的 Gitee 仓库,仓库名严格区分大小写,请准确填写,否则会出错 # 注意替换为你的 Gitee 仓库,仓库名严格区分大小写,请准确填写,否则会出错
gitee-repo: buqiyuan/vite-vue3-lowcode gitee-repo: buqiyuan/vite-vue3-lowcode
# 是否强制使用 HTTPS # 是否强制使用 HTTPS
https: false https: true
# 要部署的分支,默认是 master若是其他分支则需要指定指定的分支必须存在 # 要部署的分支,默认是 master若是其他分支则需要指定指定的分支必须存在
branch: gh-pages branch: gh-pages

4
.gitignore vendored
View File

@ -51,3 +51,7 @@ testem.log
# System Files # System Files
.DS_Store .DS_Store
Thumbs.db Thumbs.db
# auto generate file
components.d.ts
auto-imports.d.ts

5
.npmrc Normal file
View File

@ -0,0 +1,5 @@
# shamefully-hoist=true
# strict-peer-dependencies=false
registry = https://registry.npmmirror.com

View File

@ -1,6 +1,6 @@
{ {
"recommendations": [ "recommendations": [
"johnsoncodehk.volar", "vue.volar",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint", "stylelint.vscode-stylelint",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",

47
.vscode/settings.json vendored
View File

@ -1,11 +1,32 @@
{ {
"typescript.tsdk": "./node_modules/typescript/lib", "typescript.tsdk": "./node_modules/typescript/lib",
"volar.tsPlugin": true,
"volar.tsPluginStatus": false,
"npm.packageManager": "pnpm", "npm.packageManager": "pnpm",
"editor.tabSize": 2, "editor.tabSize": 2,
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",
"files.eol": "\n", "files.eol": "\n",
"eslint.probe": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"html",
"vue",
"markdown",
"json",
"jsonc"
],
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"html",
"vue",
"markdown",
"json",
"jsonc"
],
"search.exclude": { "search.exclude": {
"**/node_modules": true, "**/node_modules": true,
"**/*.log": true, "**/*.log": true,
@ -55,9 +76,9 @@
"**/yarn.lock": true "**/yarn.lock": true
}, },
"stylelint.enable": true, "stylelint.enable": true,
"stylelint.packageManager": "yarn", "stylelint.packageManager": "pnpm",
"path-intellisense.mappings": { "path-intellisense.mappings": {
"/@/": "${workspaceRoot}/src" "@/": "${workspaceRoot}/src"
}, },
"[javascriptreact]": { "[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
@ -83,28 +104,24 @@
"[markdown]": { "[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": true "source.fixAll.eslint": true
}, },
"[vue]": { "[vue]": {
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": false "source.fixAll.stylelint": true
} }
}, },
"i18n-ally.localesPaths": [ "i18n-ally.localesPaths": ["src/locales/lang"],
"src/locales/lang"
],
"i18n-ally.keystyle": "nested", "i18n-ally.keystyle": "nested",
"i18n-ally.sortKeys": true, "i18n-ally.sortKeys": true,
"i18n-ally.namespace": true, "i18n-ally.namespace": true,
"i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}", "i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
"i18n-ally.enabledParsers": [ "i18n-ally.enabledParsers": ["ts"],
"ts"
],
"i18n-ally.sourceLanguage": "en", "i18n-ally.sourceLanguage": "en",
"i18n-ally.displayLanguage": "zh-CN", "i18n-ally.displayLanguage": "zh-CN",
"i18n-ally.enabledFrameworks": [ "i18n-ally.enabledFrameworks": ["vue", "react"]
"vue",
"react"
],
} }

9
auto-imports.d.ts vendored
View File

@ -1,17 +1,19 @@
// Generated by 'unplugin-auto-import' // Generated by 'unplugin-auto-import'
// We suggest you to commit this file into source control export {};
declare global { declare global {
const EffectScope: typeof import('vue')['EffectScope'];
const computed: typeof import('vue')['computed']; const computed: typeof import('vue')['computed'];
const createApp: typeof import('vue')['createApp']; const createApp: typeof import('vue')['createApp'];
const customRef: typeof import('vue')['customRef']; const customRef: typeof import('vue')['customRef'];
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']; const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'];
const defineComponent: typeof import('vue')['defineComponent']; const defineComponent: typeof import('vue')['defineComponent'];
const effectScope: typeof import('vue')['effectScope']; const effectScope: typeof import('vue')['effectScope'];
const EffectScope: typeof import('vue')['EffectScope'];
const getCurrentInstance: typeof import('vue')['getCurrentInstance']; const getCurrentInstance: typeof import('vue')['getCurrentInstance'];
const getCurrentScope: typeof import('vue')['getCurrentScope']; const getCurrentScope: typeof import('vue')['getCurrentScope'];
const h: typeof import('vue')['h']; const h: typeof import('vue')['h'];
const inject: typeof import('vue')['inject']; const inject: typeof import('vue')['inject'];
const isProxy: typeof import('vue')['isProxy'];
const isReactive: typeof import('vue')['isReactive'];
const isReadonly: typeof import('vue')['isReadonly']; const isReadonly: typeof import('vue')['isReadonly'];
const isRef: typeof import('vue')['isRef']; const isRef: typeof import('vue')['isRef'];
const markRaw: typeof import('vue')['markRaw']; const markRaw: typeof import('vue')['markRaw'];
@ -50,5 +52,6 @@ declare global {
const useSlots: typeof import('vue')['useSlots']; const useSlots: typeof import('vue')['useSlots'];
const watch: typeof import('vue')['watch']; const watch: typeof import('vue')['watch'];
const watchEffect: typeof import('vue')['watchEffect']; const watchEffect: typeof import('vue')['watchEffect'];
const watchPostEffect: typeof import('vue')['watchPostEffect'];
const watchSyncEffect: typeof import('vue')['watchSyncEffect'];
} }
export {};

16
components.d.ts vendored
View File

@ -1,8 +1,11 @@
// generated by unplugin-vue-components // generated by unplugin-vue-components
// We suggest you to commit this file into source control // We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/vue-next/pull/3399 // Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core';
declare module 'vue' { export {};
declare module '@vue/runtime-core' {
export interface GlobalComponents { export interface GlobalComponents {
ElAside: typeof import('element-plus/es')['ElAside']; ElAside: typeof import('element-plus/es')['ElAside'];
ElButton: typeof import('element-plus/es')['ElButton']; ElButton: typeof import('element-plus/es')['ElButton'];
@ -26,8 +29,11 @@ declare module 'vue' {
ElTag: typeof import('element-plus/es')['ElTag']; ElTag: typeof import('element-plus/es')['ElTag'];
ElTooltip: typeof import('element-plus/es')['ElTooltip']; ElTooltip: typeof import('element-plus/es')['ElTooltip'];
ElTree: typeof import('element-plus/es')['ElTree']; ElTree: typeof import('element-plus/es')['ElTree'];
InfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']; RouterLink: typeof import('vue-router')['RouterLink'];
RouterView: typeof import('vue-router')['RouterView'];
}
export interface ComponentCustomProperties {
vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll'];
} }
} }
export {};

View File

@ -7,7 +7,7 @@
"bootstrap": "pnpm install", "bootstrap": "pnpm install",
"serve": "npm run dev", "serve": "npm run dev",
"dev": "cross-env --max_old_space_size=4096 vite", "dev": "cross-env --max_old_space_size=4096 vite",
"build": "cross-env vite build", "build": "rimraf dist && cross-env NODE_ENV=production vite build",
"build:no-cache": "pnpm clean:cache && npm run build", "build:no-cache": "pnpm clean:cache && npm run build",
"build-tsc": "vue-tsc --noEmit && vite build", "build-tsc": "vue-tsc --noEmit && vite build",
"preview": "npm run build && vite preview", "preview": "npm run build && vite preview",
@ -15,77 +15,89 @@
"log": "conventional-changelog -p angular -i CHANGELOG.md -s", "log": "conventional-changelog -p angular -i CHANGELOG.md -s",
"clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite", "clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
"clean:lib": "rimraf node_modules", "clean:lib": "rimraf node_modules",
"format": "prettier --write .",
"lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx,.md,.json --max-warnings 0 && pretty-quick --check --branch main",
"lint:fix": "eslint --fix . --ext .vue,.js,.ts,.jsx,.tsx,.md,.json && pretty-quick --branch main",
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix", "lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
"lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"", "lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/", "lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged", "lint:lint-staged": "lint-staged",
"deploy": "npm run build && npx gh-pages -d dist", "deploy": "npm run build && npx gh-pages -d dist",
"test:gzip": "npx http-server dist --cors --gzip -c-1", "prepare": "husky install",
"test:br": "npx http-server dist --cors --brotli -c-1", "postversion": "git push && git push origin --tags",
"reinstall": "rimraf pnpm-lock.yaml && rimraf package.lock.json && rimraf node_modules && npm run bootstrap", "reinstall": "rimraf pnpm-lock.yaml && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
"prepare": "husky install" "version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md",
"test:gzip": "npx http-server dist --cors --gzip -c-1",
"test:br": "npx http-server dist --cors --brotli -c-1"
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^0.2.4", "@element-plus/icons-vue": "^2.0.6",
"@vant/touch-emulator": "^1.3.2", "@vant/touch-emulator": "^1.3.2",
"@vueuse/core": "^7.5.3", "@vueuse/core": "^8.7.5",
"@vueuse/integrations": "^7.5.3", "@vueuse/integrations": "^8.7.5",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"axios": "^0.24.0", "axios": "^0.27.2",
"dayjs": "^1.10.7", "dayjs": "^1.11.3",
"dexie": "^3.2.0", "dexie": "^3.2.2",
"element-plus": "1.3.0-beta.5", "element-plus": "2.2.8",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"monaco-editor": "^0.31.1", "monaco-editor": "^0.33.0",
"nanoid": "^3.1.32", "nanoid": "^4.0.0",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"nprogress": "^1.0.0-1", "nprogress": "^1.0.0-1",
"pinia": "^2.0.9", "pinia": "^2.0.14",
"qrcode": "^1.5.0", "qrcode": "^1.5.0",
"qs": "^6.10.3", "qs": "^6.11.0",
"vant": "3.4.1", "vant": "3.5.2",
"vue": "3.2.26", "vue": "3.2.37",
"vue-router": "^4.0.12", "vue-router": "^4.0.16",
"vuedraggable": "^4.1.0" "vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^16.0.2", "@commitlint/cli": "^17.0.3",
"@commitlint/config-conventional": "^16.0.0", "@commitlint/config-conventional": "^17.0.3",
"@types/lodash-es": "^4.17.5", "@types/lodash-es": "^4.17.6",
"@types/node": "^17.0.8", "@types/node": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.9.1", "@typescript-eslint/eslint-plugin": "^5.30.3",
"@typescript-eslint/parser": "^5.9.1", "@typescript-eslint/parser": "^5.30.3",
"@vitejs/plugin-legacy": "^1.6.4", "@vitejs/plugin-legacy": "^1.8.2",
"@vitejs/plugin-vue": "^2.0.1", "@vitejs/plugin-vue": "^2.3.3",
"@vitejs/plugin-vue-jsx": "^1.3.3", "@vitejs/plugin-vue-jsx": "^1.3.10",
"@vue/compiler-sfc": "3.2.26",
"commitizen": "^4.2.4", "commitizen": "^4.2.4",
"conventional-changelog-cli": "^2.2.2", "conventional-changelog-cli": "^2.2.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^8.7.0", "eslint": "^8.19.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.5.0",
"eslint-define-config": "^1.2.2", "eslint-define-config": "^1.5.1",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-import": "^2.26.0",
"eslint-plugin-vue": "^8.3.0", "eslint-plugin-prettier": "^4.2.1",
"husky": "^7.0.4", "eslint-plugin-vue": "^9.1.1",
"lint-staged": "^12.1.7", "husky": "^8.0.1",
"postcss-html": "^1.3.0", "lint-staged": "^13.0.3",
"prettier": "^2.5.1", "postcss": "^8.4.14",
"sass": "1.48.0", "postcss-html": "^1.4.1",
"stylelint": "^14.2.0", "postcss-scss": "^4.0.4",
"prettier": "^2.7.1",
"pretty-quick": "^3.1.3",
"rimraf": "^3.0.2",
"sass": "1.53.0",
"stylelint": "^14.9.1",
"stylelint-config-html": "^1.0.0", "stylelint-config-html": "^1.0.0",
"stylelint-config-prettier": "^9.0.3", "stylelint-config-prettier": "^9.0.3",
"stylelint-config-recommended": "^6.0.0", "stylelint-config-recommended": "^8.0.0",
"stylelint-config-standard": "^24.0.0", "stylelint-config-standard": "^26.0.0",
"stylelint-order": "^5.0.0", "stylelint-order": "^5.0.0",
"stylelint-scss": "^4.1.0", "stylelint-scss": "^4.2.0",
"typescript": "^4.5.4", "typescript": "^4.7.4",
"unplugin-auto-import": "^0.5.11", "unplugin-auto-import": "^0.9.2",
"unplugin-vue-components": "^0.17.11", "unplugin-vue-components": "^0.21.0",
"vite": "2.7.12", "unplugin-vue-define-options": "^0.6.1",
"vite-plugin-windicss": "^1.6.2", "vite": "2.9.13",
"vue-eslint-parser": "^8.0.1", "vite-plugin-checker": "^0.4.7",
"windicss": "^3.4.2" "vite-plugin-windicss": "^1.8.6",
"vue-eslint-parser": "^9.0.3",
"vue-tsc": "^0.38.2",
"windicss": "^3.5.6"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -104,17 +116,14 @@
}, },
"homepage": "https://github.com/buqiyuan/vite-vue3-lowcode#readme", "homepage": "https://github.com/buqiyuan/vite-vue3-lowcode#readme",
"engines": { "engines": {
"node": "^12 || >=14" "node": ">=14"
}, },
"lint-staged": { "lint-staged": {
"*.{js,jsx,ts,tsx}": [ "*.{js,jsx,ts,tsx}": [
"eslint --fix", "eslint --fix",
"prettier --write" "prettier --write"
], ],
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [ "*.json": [
"prettier --write--parser json"
],
"package.json": [
"prettier --write" "prettier --write"
], ],
"*.vue": [ "*.vue": [
@ -129,5 +138,6 @@
"*.md": [ "*.md": [
"prettier --write" "prettier --write"
] ]
} },
"packageManager": "^pnpm@6.32.4"
} }

File diff suppressed because it is too large Load Diff

View File

@ -7,46 +7,46 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { CacheEnum } from '@/enums' import { defineComponent, ref, watch } from 'vue';
import { VisualEditorModelValue } from '@/visual-editor/visual-editor.utils' import { useRoute } from 'vue-router';
import { defineComponent, ref, watch } from 'vue' import { CacheEnum } from '@/enums';
import { useRoute } from 'vue-router' import { VisualEditorModelValue } from '@/visual-editor/visual-editor.utils';
export default defineComponent({ export default defineComponent({
name: 'App', name: 'App',
setup() { setup() {
const keepAliveRef = ref() const keepAliveRef = ref();
const route = useRoute() const route = useRoute();
const jsonData: VisualEditorModelValue = JSON.parse( const jsonData: VisualEditorModelValue = JSON.parse(
localStorage.getItem(CacheEnum.PAGE_DATA_KEY) as string localStorage.getItem(CacheEnum.PAGE_DATA_KEY) as string,
) );
// //
const notNeedcachePages = Object.keys(jsonData.pages).filter( const notNeedcachePages = Object.keys(jsonData.pages).filter(
(key) => !jsonData.pages[key].config.keepAlive (key) => !jsonData.pages[key].config.keepAlive,
) );
console.log('notNeedcachePages:', notNeedcachePages) console.log('notNeedcachePages:', notNeedcachePages);
watch( watch(
() => route.path, () => route.path,
(path) => { (path) => {
if (notNeedcachePages.includes(path)) { if (notNeedcachePages.includes(path)) {
// keep-alive // keep-alive
const routeCaches = keepAliveRef.value?.$?.__v_cache const routeCaches = keepAliveRef.value?.$?.__v_cache;
console.log('keep-alive cache', path, routeCaches) console.log('keep-alive cache', path, routeCaches);
// keep-alive // keep-alive
routeCaches.delete(path) routeCaches.delete(path);
} }
} },
) );
return { keepAliveRef } return { keepAliveRef };
} },
}) });
</script> </script>
<style> <style>
body::-webkit-scrollbar { body::-webkit-scrollbar {
width: 0; width: 0;
} }
</style> </style>

View File

@ -1,21 +1,21 @@
import { createApp } from 'vue' import { createApp } from 'vue';
import App from './App.vue' import App from './App.vue';
import router from './router' import router from './router';
import { setupVant } from '@/plugins/vant' import { setupVant } from '@/plugins/vant';
import 'animate.css' import 'animate.css';
const app = createApp(App) const app = createApp(App);
// 安装vant插件 // 安装vant插件
setupVant(app) setupVant(app);
app.config.globalProperties.$$refs = {} app.config.globalProperties.$$refs = {};
// if (import.meta.env.DEV) { // if (import.meta.env.DEV) {
window.$$refs = app.config.globalProperties.$$refs window.$$refs = app.config.globalProperties.$$refs;
// } // }
app.use(router).mount('#app') app.use(router).mount('#app');

View File

@ -6,30 +6,30 @@
* @Description: * @Description:
* @FilePath: \vite-vue3-lowcode\preview\router.ts * @FilePath: \vite-vue3-lowcode\preview\router.ts
*/ */
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router' import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
import type { VisualEditorModelValue } from '@/visual-editor/visual-editor.utils' import type { VisualEditorModelValue } from '@/visual-editor/visual-editor.utils';
import { CacheEnum } from '@/enums' import { CacheEnum } from '@/enums';
const routes: Array<RouteRecordRaw> = [ const routes: Array<RouteRecordRaw> = [
{ {
path: '/:pathMatch(.*)*', path: '/:pathMatch(.*)*',
component: () => import('./views/preview.vue') component: () => import('./views/preview.vue'),
} },
] ];
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(), history: createWebHashHistory(),
routes routes,
}) });
// 获取本地缓存的页面数据 // 获取本地缓存的页面数据
const jsonData: VisualEditorModelValue = JSON.parse( const jsonData: VisualEditorModelValue = JSON.parse(
localStorage.getItem(CacheEnum.PAGE_DATA_KEY) as string localStorage.getItem(CacheEnum.PAGE_DATA_KEY) as string,
) );
router.beforeEach((to) => { router.beforeEach((to) => {
document.title = jsonData?.pages?.[to.path]?.title ?? document.title document.title = jsonData?.pages?.[to.path]?.title ?? document.title;
return true return true;
}) });
export default router export default router;

View File

@ -5,7 +5,7 @@ export enum ResultEnum {
SUCCESS = 0, SUCCESS = 0,
ERROR = -1, ERROR = -1,
TIMEOUT = 10042, TIMEOUT = 10042,
TYPE = 'success' TYPE = 'success',
} }
/** /**
@ -16,7 +16,7 @@ export enum RequestEnum {
POST = 'POST', POST = 'POST',
PATCH = 'PATCH', PATCH = 'PATCH',
PUT = 'PUT', PUT = 'PUT',
DELETE = 'DELETE' DELETE = 'DELETE',
} }
/** /**
@ -30,5 +30,5 @@ export enum ContentTypeEnum {
// form-data 一般配合qs // form-data 一般配合qs
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8', FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
// form-data 上传 // form-data 上传
FORM_DATA = 'multipart/form-data;charset=UTF-8' FORM_DATA = 'multipart/form-data;charset=UTF-8',
} }

View File

@ -10,8 +10,8 @@ import axios, { AxiosRequestConfig } from 'axios';
import qs from 'qs'; import qs from 'qs';
// import store from '@/store' // import store from '@/store'
import { Toast } from 'vant'; import { Toast } from 'vant';
import router from '@/router';
import { ContentTypeEnum } from './httpEnum'; import { ContentTypeEnum } from './httpEnum';
import router from '@/router';
// create an axios instance // create an axios instance
const service = axios.create({ const service = axios.create({
@ -85,7 +85,7 @@ service.interceptors.response.use(
if (error.message?.includes('timeout')) { if (error.message?.includes('timeout')) {
Toast('请求超时!'); Toast('请求超时!');
} }
console.log('err' + error); // for debug console.log(`err${error}`); // for debug
return Promise.reject(error); return Promise.reject(error);
}, },
); );

View File

@ -6,17 +6,17 @@
* @Description: * @Description:
* @FilePath: \vite-vue3-lowcode\preview\views\comp-render.tsx * @FilePath: \vite-vue3-lowcode\preview\views\comp-render.tsx
*/ */
import { defineComponent, PropType } from 'vue' import { defineComponent, PropType } from 'vue';
import type { VisualEditorBlockData } from '@/visual-editor/visual-editor.utils' import type { VisualEditorBlockData } from '@/visual-editor/visual-editor.utils';
import { visualConfig } from '@/visual.config' import { visualConfig } from '@/visual.config';
export default defineComponent({ export default defineComponent({
name: 'CompRender', name: 'CompRender',
props: { props: {
element: { element: {
type: Object as PropType<VisualEditorBlockData>, type: Object as PropType<VisualEditorBlockData>,
default: () => ({}) default: () => ({}),
} },
}, },
setup(props) { setup(props) {
return visualConfig.componentMap[props.element.componentKey].render({ return visualConfig.componentMap[props.element.componentKey].render({
@ -24,7 +24,7 @@ export default defineComponent({
props: props.element.props || {}, props: props.element.props || {},
model: {}, model: {},
block: props.element, block: props.element,
custom: {} custom: {},
}) });
} },
}) });

View File

@ -13,59 +13,59 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, reactive, toRefs, onMounted } from 'vue' import { defineComponent, reactive, toRefs, onMounted } from 'vue';
import { Toast } from 'vant' import { Toast } from 'vant';
import type { VisualEditorModelValue } from '@/visual-editor/visual-editor.utils' import router from '../router';
import SlotItem from './slot-item.vue' import SlotItem from './slot-item.vue';
import router from '../router' import type { VisualEditorModelValue } from '@/visual-editor/visual-editor.utils';
import { CacheEnum } from '@/enums' import { CacheEnum } from '@/enums';
export default defineComponent({ export default defineComponent({
name: 'Preview', name: 'Preview',
components: { components: {
SlotItem SlotItem,
}, },
setup() { setup() {
const jsonData: VisualEditorModelValue = JSON.parse( const jsonData: VisualEditorModelValue = JSON.parse(
localStorage.getItem(CacheEnum.PAGE_DATA_KEY) as string localStorage.getItem(CacheEnum.PAGE_DATA_KEY) as string,
) );
if (!jsonData || !Object.keys(jsonData.pages)) { if (!jsonData || !Object.keys(jsonData.pages)) {
Toast.fail('当前没有可以预览的页面!') Toast.fail('当前没有可以预览的页面!');
} }
const route = router.currentRoute const route = router.currentRoute;
const currentPage = jsonData.pages[route.value.path] const currentPage = jsonData.pages[route.value.path];
console.log('currentPage:', currentPage) console.log('currentPage:', currentPage);
const state = reactive({ const state = reactive({
blocks: currentPage?.blocks blocks: currentPage?.blocks,
}) });
// //
if (!state.blocks) { if (!state.blocks) {
router.replace('/') router.replace('/');
} }
onMounted(() => { onMounted(() => {
if (currentPage?.config) { if (currentPage?.config) {
const { bgImage, bgColor } = currentPage.config const { bgImage, bgColor } = currentPage.config;
const bodyStyleStr = ` const bodyStyleStr = `
body { body {
background-color: ${bgColor}; background-color: ${bgColor};
background-image: url(${bgImage}); background-image: url(${bgImage});
} }
` `;
document.styleSheets[0].insertRule(bodyStyleStr) document.styleSheets[0].insertRule(bodyStyleStr);
} }
}) });
return { return {
...toRefs(state), ...toRefs(state),
actions: jsonData.actions, actions: jsonData.actions,
models: jsonData.models models: jsonData.models,
} };
} },
}) });
</script> </script>

View File

@ -19,82 +19,84 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted, PropType } from 'vue' import { defineComponent, onMounted, PropType } from 'vue';
import CompRender from './comp-render' import request from '../utils/http/request';
import { useAnimate } from '@/hooks/useAnimate' import { ContentTypeEnum } from '../utils/http/httpEnum';
import type { import CompRender from './comp-render';
import type {
VisualEditorBlockData, VisualEditorBlockData,
VisualEditorActions, VisualEditorActions,
VisualEditorModel, VisualEditorModel,
FetchApiItem FetchApiItem,
} from '@/visual-editor/visual-editor.utils' } from '@/visual-editor/visual-editor.utils';
import request from '../utils/http/request' import { useAnimate } from '@/hooks/useAnimate';
import { ContentTypeEnum } from '../utils/http/httpEnum'
export default defineComponent({ export default defineComponent({
name: 'SlotItem', name: 'SlotItem',
components: { CompRender }, components: { CompRender },
props: { props: {
element: { element: {
type: [Object] as PropType<VisualEditorBlockData>, type: [Object] as PropType<VisualEditorBlockData>,
default: () => ({}) default: () => ({}),
}, },
actions: { actions: {
type: Object as PropType<VisualEditorActions>, type: Object as PropType<VisualEditorActions>,
default: () => ({}) default: () => ({}),
}, },
models: { models: {
type: Object as PropType<VisualEditorModel[]>, type: Object as PropType<VisualEditorModel[]>,
default: () => ({}) default: () => ({}),
} },
}, },
setup(props) { setup(props) {
// TODO // TODO
const events = props.element.actions.reduce((prev, curr) => { const events = props.element.actions.reduce((prev, curr) => {
prev[curr.event] = async () => { prev[curr.event] = async () => {
for (const handle of curr.handle) { for (const handle of curr.handle) {
const [scopeType, actionType, handleKey] = handle.link const [scopeType, actionType, handleKey] = handle.link;
if (scopeType === 'global') { if (scopeType === 'global') {
const apis: FetchApiItem[] = props.actions[actionType].apis const apis: FetchApiItem[] = props.actions[actionType].apis;
const { data, options } = apis.find((item) => item.key == handleKey)! // const { data, options } = apis.find((item) => item.key == handleKey)!;
const pramsObj = {} const { options } = apis.find((item) => item.key == handleKey)!;
// const pramsObj = {};
await request({ await request({
...options, ...options,
headers: { headers: {
'Content-Type': ContentTypeEnum[options.contentType] 'Content-Type': ContentTypeEnum[options.contentType],
}, },
data: { data: {
username: 'admin', username: 'admin',
password: '123456' password: '123456',
} },
}) });
} else if (scopeType === 'component') { } else if (scopeType === 'component') {
console.log('scopeType', scopeType);
} }
} }
} };
return prev return prev;
}, {}) }, {});
onMounted(() => { onMounted(() => {
const animations = props.element.animations const animations = props.element.animations;
if (animations?.length) { if (animations?.length) {
let animateEl = let animateEl =
(window.$$refs[props.element._vid]?.$el as HTMLElement) ?? (window.$$refs[props.element._vid]?.$el as HTMLElement) ??
(window.$$refs[props.element._vid] as HTMLElement) (window.$$refs[props.element._vid] as HTMLElement);
animateEl = animateEl?.closest('.__slot-item')?.firstChild as HTMLElement animateEl = animateEl?.closest('.__slot-item')?.firstChild as HTMLElement;
if (animateEl) { if (animateEl) {
useAnimate(animateEl, animations) useAnimate(animateEl, animations);
} }
} }
}) });
return { return {
events events,
} };
} },
}) });
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -7,21 +7,21 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { provide } from 'vue' import { provide } from 'vue';
import zhCn from 'element-plus/lib/locale/lang/zh-cn' import zhCn from 'element-plus/lib/locale/lang/zh-cn';
import { initVisualData, injectKey, localKey } from '@/visual-editor/hooks/useVisualData' import { initVisualData, injectKey, localKey } from '@/visual-editor/hooks/useVisualData';
const visualData = initVisualData() const visualData = initVisualData();
// //
provide(injectKey, visualData) provide(injectKey, visualData);
const { jsonData } = visualData const { jsonData } = visualData;
window.addEventListener('beforeunload', () => { window.addEventListener('beforeunload', () => {
sessionStorage.setItem(localKey, JSON.stringify(jsonData)) sessionStorage.setItem(localKey, JSON.stringify(jsonData));
}) });
</script> </script>
<style lang="scss"> <style lang="scss">
@import 'style/common'; @import 'style/common';
</style> </style>

View File

@ -1,5 +1,5 @@
export interface NavItem { export interface NavItem {
path: string path: string;
name: string name: string;
isActive: boolean isActive: boolean;
} }

View File

@ -5,7 +5,7 @@ export enum ResultEnum {
SUCCESS = 0, SUCCESS = 0,
ERROR = -1, ERROR = -1,
TIMEOUT = 10042, TIMEOUT = 10042,
TYPE = 'success' TYPE = 'success',
} }
/** /**
@ -16,7 +16,7 @@ export enum RequestEnum {
POST = 'POST', POST = 'POST',
PATCH = 'PATCH', PATCH = 'PATCH',
PUT = 'PUT', PUT = 'PUT',
DELETE = 'DELETE' DELETE = 'DELETE',
} }
/** /**
@ -30,5 +30,5 @@ export enum ContentTypeEnum {
// form-data 一般配合qs // form-data 一般配合qs
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8', FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
// form-data 上传 // form-data 上传
FORM_DATA = 'multipart/form-data;charset=UTF-8' FORM_DATA = 'multipart/form-data;charset=UTF-8',
} }

View File

@ -10,5 +10,5 @@
* @description * @description
*/ */
export enum CacheEnum { export enum CacheEnum {
PAGE_DATA_KEY = 'PAGE_DATA_KEY' PAGE_DATA_KEY = 'PAGE_DATA_KEY',
} }

View File

@ -7,55 +7,55 @@
* @FilePath: \vite-vue3-lowcode\src\hooks\useAnimate.ts * @FilePath: \vite-vue3-lowcode\src\hooks\useAnimate.ts
*/ */
import type { Animation } from '@/visual-editor/visual-editor.utils' import type { Animation } from '@/visual-editor/visual-editor.utils';
export const useAnimate = async ( export const useAnimate = async (
animateEl: HTMLElement, animateEl: HTMLElement,
animations: Animation | Animation[], animations: Animation | Animation[],
prefixCls = 'animate__' prefixCls = 'animate__',
) => { ) => {
animations = Array.isArray(animations) ? animations : [animations] animations = Array.isArray(animations) ? animations : [animations];
const play = (animate: Animation) => const play = (animate: Animation) =>
new Promise((resolve) => { new Promise((resolve) => {
if (animateEl) { if (animateEl) {
const animationName = `${prefixCls}${animate.value}` const animationName = `${prefixCls}${animate.value}`;
// 过滤可能残留的animate.css动画类名 // 过滤可能残留的animate.css动画类名
animateEl.classList.value = animateEl.classList.value animateEl.classList.value = animateEl.classList.value
.split(' ') .split(' ')
.filter((item) => !item.includes(prefixCls)) .filter((item) => !item.includes(prefixCls))
.join(' ') .join(' ');
// 设置动画属性 // 设置动画属性
const setAnimate = () => { const setAnimate = () => {
animateEl.style.setProperty('--animate-duration', `${animate.duration}s`) animateEl.style.setProperty('--animate-duration', `${animate.duration}s`);
animateEl.style.setProperty('animation-delay', `${animate.delay}s`) animateEl.style.setProperty('animation-delay', `${animate.delay}s`);
animateEl.style.setProperty( animateEl.style.setProperty(
'animation-iteration-count', 'animation-iteration-count',
`${animate.infinite ? 'infinite' : animate.count}` `${animate.infinite ? 'infinite' : animate.count}`,
) );
animateEl?.classList.add(`${prefixCls}animated`, animationName) animateEl?.classList.add(`${prefixCls}animated`, animationName);
} };
// 动画结束时,删除类名 // 动画结束时,删除类名
const handleAnimationEnd = (event?: AnimationEvent) => { const handleAnimationEnd = (event?: AnimationEvent) => {
event?.stopPropagation() event?.stopPropagation();
animateEl.classList.remove(`${prefixCls}animated`, animationName) animateEl.classList.remove(`${prefixCls}animated`, animationName);
animateEl.removeEventListener('animationend', handleAnimationEnd) animateEl.removeEventListener('animationend', handleAnimationEnd);
resolve('animation end') resolve('animation end');
} };
setAnimate() setAnimate();
animateEl?.addEventListener('animationend', handleAnimationEnd, { once: true }) animateEl?.addEventListener('animationend', handleAnimationEnd, { once: true });
// animateEl?.addEventListener('animationcancel', handleAnimationEnd, { once: true }) // animateEl?.addEventListener('animationcancel', handleAnimationEnd, { once: true })
} else { } else {
resolve('动画执行失败!执行动画元素不存在!') resolve('动画执行失败!执行动画元素不存在!');
} }
}) });
for (const item of animations) { for (const item of animations) {
await play(item) await play(item);
} }
} };

View File

@ -5,23 +5,23 @@
* @descriptionuseGlobalProperties * @descriptionuseGlobalProperties
* @update: 2021/5/3 21:13 * @update: 2021/5/3 21:13
*/ */
import { getCurrentInstance } from 'vue' import { getCurrentInstance } from 'vue';
import { RouteLocationNormalizedLoaded, Router } from 'vue-router' import { RouteLocationNormalizedLoaded, Router } from 'vue-router';
interface GlobalProperties { interface GlobalProperties {
$$refs: any $$refs: any;
$route: RouteLocationNormalizedLoaded $route: RouteLocationNormalizedLoaded;
$router: Router $router: Router;
} }
export const useGlobalProperties = () => { export const useGlobalProperties = () => {
const globalProperties = getCurrentInstance()!.appContext.config const globalProperties = getCurrentInstance()!.appContext.config
.globalProperties as GlobalProperties .globalProperties as GlobalProperties;
const registerRef = (el, _vid: string) => el && (globalProperties.$$refs[_vid] = el) const registerRef = (el, _vid: string) => el && (globalProperties.$$refs[_vid] = el);
return { return {
globalProperties, globalProperties,
registerRef registerRef,
} };
} };

View File

@ -1,11 +1,11 @@
import { Button } from 'vant' import { Button } from 'vant';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { import {
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorSwitchProp createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils' import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { useGlobalProperties } from '@/hooks/useGlobalProperties'
export default { export default {
key: 'button', key: 'button',
@ -13,21 +13,21 @@ export default {
label: '按钮', label: '按钮',
preview: () => <Button type={'primary'}></Button>, preview: () => <Button type={'primary'}></Button>,
render: ({ props, block, styles }) => { render: ({ props, block, styles }) => {
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
return () => ( return () => (
<div style={styles}> <div style={styles}>
<Button ref={(el) => registerRef(el, block._vid)} {...props}></Button> <Button ref={(el) => registerRef(el, block._vid)} {...props}></Button>
</div> </div>
) );
}, },
resize: { resize: {
height: true, height: true,
width: true width: true,
}, },
events: [ events: [
{ label: '点击按钮,且按钮状态不为加载或禁用时触发', value: 'click' }, { label: '点击按钮,且按钮状态不为加载或禁用时触发', value: 'click' },
{ label: '开始触摸按钮时触发', value: 'touchstart' } { label: '开始触摸按钮时触发', value: 'touchstart' },
], ],
props: { props: {
text: createEditorInputProp({ label: '按钮文字', defaultValue: '按钮' }), text: createEditorInputProp({ label: '按钮文字', defaultValue: '按钮' }),
@ -36,48 +36,48 @@ export default {
options: [ options: [
{ {
label: '主要按钮', label: '主要按钮',
value: 'primary' value: 'primary',
}, },
{ {
label: '成功按钮', label: '成功按钮',
value: 'success' value: 'success',
}, },
{ {
label: '默认按钮', label: '默认按钮',
value: 'default' value: 'default',
}, },
{ {
label: '警告按钮', label: '警告按钮',
value: 'warning' value: 'warning',
}, },
{ {
label: '危险按钮', label: '危险按钮',
value: 'danger' value: 'danger',
} },
], ],
defaultValue: 'default' defaultValue: 'default',
}), }),
size: createEditorSelectProp({ size: createEditorSelectProp({
label: '按钮尺寸', label: '按钮尺寸',
options: [ options: [
{ {
label: '大型', label: '大型',
value: 'large' value: 'large',
}, },
{ {
label: '普通', label: '普通',
value: 'normal' value: 'normal',
}, },
{ {
label: '小型', label: '小型',
value: 'small' value: 'small',
}, },
{ {
label: '迷你', label: '迷你',
value: 'mini' value: 'mini',
} },
], ],
defaultValue: 'normal' defaultValue: 'normal',
}), }),
'native-type': createEditorSelectProp({ 'native-type': createEditorSelectProp({
label: '原生button的type属性', label: '原生button的type属性',
@ -85,14 +85,14 @@ export default {
{ label: '普通button', value: 'button' }, { label: '普通button', value: 'button' },
{ {
label: '表单提交按钮', label: '表单提交按钮',
value: 'submit' value: 'submit',
}, },
{ {
label: '表单重置按钮', label: '表单重置按钮',
value: 'reset' value: 'reset',
} },
], ],
defaultValue: 'button' defaultValue: 'button',
}), }),
to: createEditorInputProp({ label: '路由跳转' }), to: createEditorInputProp({ label: '路由跳转' }),
url: createEditorInputProp({ label: '跳转链接' }), url: createEditorInputProp({ label: '跳转链接' }),
@ -103,7 +103,7 @@ export default {
block: createEditorSwitchProp({ label: '是否为块级元素', defaultValue: false }), block: createEditorSwitchProp({ label: '是否为块级元素', defaultValue: false }),
color: createEditorInputProp({ color: createEditorInputProp({
label: '按钮颜色', label: '按钮颜色',
tips: '按钮颜色,支持传入 linear-gradient 渐变色' tips: '按钮颜色,支持传入 linear-gradient 渐变色',
}), }),
disabled: createEditorSwitchProp({ label: '是否禁用按钮' }), disabled: createEditorSwitchProp({ label: '是否禁用按钮' }),
hairline: createEditorSwitchProp({ label: '是否使用 0.5px 边框' }), hairline: createEditorSwitchProp({ label: '是否使用 0.5px 边框' }),
@ -113,17 +113,17 @@ export default {
options: [ options: [
{ {
label: '左侧', label: '左侧',
value: 'left' value: 'left',
}, },
{ {
label: '右侧', label: '右侧',
value: 'right' value: 'right',
} },
] ],
}), }),
'icon-prefix': createEditorInputProp({ 'icon-prefix': createEditorInputProp({
label: '图标类名前缀', label: '图标类名前缀',
tips: '图标类名前缀,同 Icon 组件的 class-prefix 属性' tips: '图标类名前缀,同 Icon 组件的 class-prefix 属性',
}), }),
loading: createEditorSwitchProp({ label: '是否显示为加载状态' }), loading: createEditorSwitchProp({ label: '是否显示为加载状态' }),
'loading-size': createEditorInputProp({ label: '加载图标大小' }), 'loading-size': createEditorInputProp({ label: '加载图标大小' }),
@ -132,9 +132,9 @@ export default {
label: '加载图标类型', label: '加载图标类型',
options: [ options: [
{ label: 'circular', value: 'circular' }, { label: 'circular', value: 'circular' },
{ label: 'spinner', value: 'spinner' } { label: 'spinner', value: 'spinner' },
], ],
defaultValue: 'circular' defaultValue: 'circular',
}) }),
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -1,8 +1,8 @@
import { import {
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorSwitchProp createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
/** /**
* @name: createFieldProps * @name: createFieldProps
@ -19,21 +19,21 @@ export const createFieldProps = () => ({
options: [ options: [
{ {
label: '左对齐', label: '左对齐',
value: 'left' value: 'left',
}, },
{ {
label: '居中', label: '居中',
value: 'center' value: 'center',
}, },
{ {
label: '右对齐', label: '右对齐',
value: 'right' value: 'right',
} },
], ],
defaultValue: 'left' defaultValue: 'left',
}), }),
border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }), border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }),
readonly: createEditorSwitchProp({ label: '是否为只读状态' }), readonly: createEditorSwitchProp({ label: '是否为只读状态' }),
required: createEditorSwitchProp({ label: '是否显示表单必填星号' }), required: createEditorSwitchProp({ label: '是否显示表单必填星号' }),
rules: createEditorInputProp({ label: '表单校验规则' }) rules: createEditorInputProp({ label: '表单校验规则' }),
}) });

View File

@ -6,17 +6,17 @@
* @Description: - * @Description: -
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\checkbox\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\checkbox\index.tsx
*/ */
import { computed } from 'vue' import { computed } from 'vue';
import { Field, Checkbox, CheckboxGroup } from 'vant' import { Field, Checkbox, CheckboxGroup } from 'vant';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils' import { createFieldProps } from './createFieldProps';
import { createFieldProps } from './createFieldProps' import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties' import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { import {
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorCrossSortableProp, createEditorCrossSortableProp,
createEditorModelBindProp createEditorModelBindProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
export default { export default {
key: 'checkbox', key: 'checkbox',
@ -33,14 +33,16 @@ export default {
</CheckboxGroup> </CheckboxGroup>
), ),
render: ({ styles, block, props }) => { render: ({ styles, block, props }) => {
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
const checkList = computed({ const checkList = computed({
get() { get() {
return typeof props.modelValue === 'string' ? props.modelValue.split(',') : props.modelValue return typeof props.modelValue === 'string'
? props.modelValue.split(',')
: props.modelValue;
}, },
set: (val) => (props.modelValue = val) set: (val) => (props.modelValue = val),
}) });
return () => ( return () => (
<div style={styles}> <div style={styles}>
@ -61,16 +63,16 @@ export default {
</Checkbox> </Checkbox>
))} ))}
</CheckboxGroup> </CheckboxGroup>
) ),
}} }}
/> />
</div> </div>
) );
}, },
props: { props: {
modelValue: createEditorInputProp({ modelValue: createEditorInputProp({
label: '默认值', label: '默认值',
defaultValue: [] defaultValue: [],
}), }),
name: createEditorModelBindProp({ label: '字段绑定', defaultValue: '' }), name: createEditorModelBindProp({ label: '字段绑定', defaultValue: '' }),
label: createEditorInputProp({ label: '输入框左侧文本', defaultValue: '复选框' }), label: createEditorInputProp({ label: '输入框左侧文本', defaultValue: '复选框' }),
@ -81,33 +83,33 @@ export default {
defaultValue: [ defaultValue: [
{ label: '胡萝卜', value: 'carrot' }, { label: '胡萝卜', value: 'carrot' },
{ label: '白菜', value: 'cabbage' }, { label: '白菜', value: 'cabbage' },
{ label: '猪', value: 'pig' } { label: '猪', value: 'pig' },
] ],
}), }),
direction: createEditorSelectProp({ direction: createEditorSelectProp({
label: '排列方向', label: '排列方向',
options: [ options: [
{ {
label: '水平', label: '水平',
value: 'horizontal' value: 'horizontal',
}, },
{ {
label: '垂直', label: '垂直',
value: 'vertical' value: 'vertical',
} },
], ],
defaultValue: 'horizontal' defaultValue: 'horizontal',
}), }),
...createFieldProps() ...createFieldProps(),
}, },
events: [ events: [
{ label: '当绑定值变化时触发的事件', value: 'change' }, { label: '当绑定值变化时触发的事件', value: 'change' },
{ label: '点击复选框时触发', value: 'click' } { label: '点击复选框时触发', value: 'click' },
], ],
resize: { resize: {
width: true width: true,
}, },
model: { model: {
default: '绑定字段' default: '绑定字段',
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -1,8 +1,8 @@
import { import {
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorSwitchProp createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
/** /**
* @name: createFieldProps * @name: createFieldProps
@ -19,21 +19,21 @@ export const createFieldProps = () => ({
options: [ options: [
{ {
label: '左对齐', label: '左对齐',
value: 'left' value: 'left',
}, },
{ {
label: '居中', label: '居中',
value: 'center' value: 'center',
}, },
{ {
label: '右对齐', label: '右对齐',
value: 'right' value: 'right',
} },
], ],
defaultValue: 'left' defaultValue: 'left',
}), }),
border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }), border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }),
readonly: createEditorSwitchProp({ label: '是否为只读状态' }), readonly: createEditorSwitchProp({ label: '是否为只读状态' }),
required: createEditorSwitchProp({ label: '是否显示表单必填星号' }), required: createEditorSwitchProp({ label: '是否显示表单必填星号' }),
rules: createEditorInputProp({ label: '表单校验规则' }) rules: createEditorInputProp({ label: '表单校验规则' }),
}) });

View File

@ -1,25 +1,25 @@
import { Field, Popup, DatetimePicker } from 'vant' import { useAttrs, reactive } from 'vue';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils' import { Field, Popup, DatetimePicker } from 'vant';
import { createFieldProps } from './createFieldProps' import dayjs from 'dayjs';
import { useGlobalProperties } from '@/hooks/useGlobalProperties' import { createFieldProps } from './createFieldProps';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { import {
createEditorInputNumberProp, createEditorInputNumberProp,
createEditorInputProp, createEditorInputProp,
createEditorModelBindProp, createEditorModelBindProp,
createEditorSelectProp, createEditorSelectProp,
createEditorSwitchProp createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
import { useAttrs, reactive } from 'vue' import { isDate } from '@/visual-editor/utils/is';
import { isDate } from '@/visual-editor/utils/is'
import dayjs from 'dayjs'
const dateType = { const dateType = {
'month-day': 'MM-DD', 'month-day': 'MM-DD',
'year-month': 'YYYY-MM', 'year-month': 'YYYY-MM',
date: 'YYYY-MM-DD', date: 'YYYY-MM-DD',
datehour: 'YYYY-MM-DD HH', datehour: 'YYYY-MM-DD HH',
datetime: 'YYYY-MM-DD HH:mm:ss' datetime: 'YYYY-MM-DD HH:mm:ss',
} };
export default { export default {
key: 'datetimePicker', key: 'datetimePicker',
@ -27,23 +27,25 @@ export default {
label: '表单项类型 - 时间选择器', label: '表单项类型 - 时间选择器',
preview: () => <Field name="datetimePicker" label="时间选择器" placeholder={'点击选择'}></Field>, preview: () => <Field name="datetimePicker" label="时间选择器" placeholder={'点击选择'}></Field>,
render: ({ styles, block, props }) => { render: ({ styles, block, props }) => {
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
const attrs = useAttrs() const attrs = useAttrs();
const state = reactive({ const state = reactive({
showPicker: false, showPicker: false,
text: '', text: '',
currentDate: new Date() currentDate: new Date(),
}) });
const onConfirm = (value) => { const onConfirm = (value) => {
const date = isDate(value) ? dayjs(value).format(props.format || dateType[props.type]) : value const date = isDate(value)
props.modelValue = date ? dayjs(value).format(props.format || dateType[props.type])
state.text = date : value;
state.showPicker = false props.modelValue = date;
console.log(props) state.text = date;
} state.showPicker = false;
console.log(props);
};
return () => ( return () => (
<div style={styles}> <div style={styles}>
@ -60,7 +62,7 @@ export default {
<span class={'placeholder'}>{props.placeholder}</span> <span class={'placeholder'}>{props.placeholder}</span>
) : ( ) : (
state.text state.text
) ),
}} }}
/> />
<Popup v-model={[state.showPicker, 'show']} position={'bottom'} teleport="body"> <Popup v-model={[state.showPicker, 'show']} position={'bottom'} teleport="body">
@ -74,7 +76,7 @@ export default {
/> />
</Popup> </Popup>
</div> </div>
) );
}, },
props: { props: {
modelValue: createEditorInputProp({ label: '默认值' }), modelValue: createEditorInputProp({ label: '默认值' }),
@ -86,60 +88,60 @@ export default {
options: [ options: [
{ {
label: 'date', label: 'date',
value: 'date' value: 'date',
}, },
{ {
label: 'datetime', label: 'datetime',
value: 'datetime' value: 'datetime',
}, },
{ {
label: 'year-month', label: 'year-month',
value: 'year-month' value: 'year-month',
}, },
{ {
label: 'month-day', label: 'month-day',
value: 'month-day' value: 'month-day',
}, },
{ {
label: 'datehour', label: 'datehour',
value: 'datehour' value: 'datehour',
} },
], ],
defaultValue: 'datetime' defaultValue: 'datetime',
}), }),
format: createEditorInputProp({ format: createEditorInputProp({
label: '自定义日期格式化值', label: '自定义日期格式化值',
tips: 'YYYY-MM-DD HH:mm:ss', tips: 'YYYY-MM-DD HH:mm:ss',
defaultValue: '' defaultValue: '',
}), }),
cancelButtonText: createEditorInputProp({ label: '取消按钮文字' }), cancelButtonText: createEditorInputProp({ label: '取消按钮文字' }),
columnsOrder: createEditorInputProp({ columnsOrder: createEditorInputProp({
label: '自定义列排序数组', label: '自定义列排序数组',
tips: '可选值为year、month、day、hour、minute传多个值以英文逗号隔开' tips: '可选值为year、month、day、hour、minute传多个值以英文逗号隔开',
}), }),
confirmButtonText: createEditorInputProp({ label: '确认按钮文字' }), confirmButtonText: createEditorInputProp({ label: '确认按钮文字' }),
filter: createEditorInputProp({ label: '选项过滤函数' }), filter: createEditorInputProp({ label: '选项过滤函数' }),
formatter: createEditorInputProp({ label: '选项格式化函数' }), formatter: createEditorInputProp({ label: '选项格式化函数' }),
itemHeight: createEditorInputProp({ itemHeight: createEditorInputProp({
label: '选项高度', label: '选项高度',
tips: '支持 px vw vh rem 单位,默认 px' tips: '支持 px vw vh rem 单位,默认 px',
}), }),
loading: createEditorSwitchProp({ label: '是否显示加载状态' }), loading: createEditorSwitchProp({ label: '是否显示加载状态' }),
showToolbar: createEditorSwitchProp({ label: '是否显示顶部栏' }), showToolbar: createEditorSwitchProp({ label: '是否显示顶部栏' }),
swipeDuration: createEditorInputProp({ label: '快速滑动时惯性滚动的时长单位ms' }), swipeDuration: createEditorInputProp({ label: '快速滑动时惯性滚动的时长单位ms' }),
visibleItemCount: createEditorInputNumberProp({ label: '可见的选项个数', defaultValue: 6 }), visibleItemCount: createEditorInputNumberProp({ label: '可见的选项个数', defaultValue: 6 }),
placeholder: createEditorInputProp({ label: '占位符', defaultValue: '请选择' }), placeholder: createEditorInputProp({ label: '占位符', defaultValue: '请选择' }),
...createFieldProps() ...createFieldProps(),
}, },
events: [ events: [
{ label: '当值变化时触发的事件', value: 'change' }, { label: '当值变化时触发的事件', value: 'change' },
{ label: '点击完成按钮时触发的事件', value: 'confirm' }, { label: '点击完成按钮时触发的事件', value: 'confirm' },
{ label: '点击取消按钮时触发的事件', value: 'cancel' } { label: '点击取消按钮时触发的事件', value: 'cancel' },
], ],
resize: { resize: {
width: true width: true,
}, },
model: { model: {
default: '绑定字段' default: '绑定字段',
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -6,16 +6,16 @@
* @Description: 线 * @Description: 线
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\divider\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\divider\index.tsx
*/ */
import { Divider } from 'vant' import { computed } from 'vue';
import { Divider } from 'vant';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { import {
createEditorColorProp, createEditorColorProp,
createEditorSwitchProp, createEditorSwitchProp,
createEditorInputProp, createEditorInputProp,
createEditorSelectProp createEditorSelectProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils' import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { useGlobalProperties } from '@/hooks/useGlobalProperties'
import { computed } from 'vue'
export default { export default {
key: 'divider', key: 'divider',
@ -23,23 +23,23 @@ export default {
label: '分割线', label: '分割线',
preview: () => <Divider style="width:190px"></Divider>, preview: () => <Divider style="width:190px"></Divider>,
render: ({ props, block, styles }) => { render: ({ props, block, styles }) => {
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
const style = computed(() => ({ const style = computed(() => ({
width: '100%', width: '100%',
color: props['text-color'], color: props['text-color'],
borderColor: props['divider-color'] borderColor: props['divider-color'],
})) }));
return () => ( return () => (
<div style={styles}> <div style={styles}>
<Divider ref={(el) => registerRef(el, block._vid)} {...props} style={style.value}> <Divider ref={(el) => registerRef(el, block._vid)} {...props} style={style.value}>
{{ {{
default: () => props.text default: () => props.text,
}} }}
</Divider> </Divider>
</div> </div>
) );
}, },
props: { props: {
text: createEditorInputProp({ label: '展示文本', defaultValue: '文本' }), text: createEditorInputProp({ label: '展示文本', defaultValue: '文本' }),
@ -48,12 +48,12 @@ export default {
options: [ options: [
{ label: '左边', value: 'left' }, { label: '左边', value: 'left' },
{ label: '中间', value: 'center' }, { label: '中间', value: 'center' },
{ label: '右边', value: 'right' } { label: '右边', value: 'right' },
], ],
defaultValue: 'center' defaultValue: 'center',
}), }),
dashed: createEditorSwitchProp({ label: '是否为虚线' }), dashed: createEditorSwitchProp({ label: '是否为虚线' }),
'text-color': createEditorColorProp({ label: '文本颜色' }), 'text-color': createEditorColorProp({ label: '文本颜色' }),
'divider-color': createEditorColorProp({ label: '分割线颜色' }) 'divider-color': createEditorColorProp({ label: '分割线颜色' }),
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -7,14 +7,14 @@
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\image\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\image\index.tsx
*/ */
import { Image } from 'vant'; import { Image } from 'vant';
import { Picture } from '@element-plus/icons-vue';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { import {
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorSwitchProp, createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props'; } from '@/visual-editor/visual-editor.props';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties'; import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { Picture } from '@element-plus/icons-vue';
export default { export default {
key: 'image', key: 'image',

View File

@ -1,11 +1,11 @@
const modules = import.meta.globEager('./*/index.tsx') const modules = import.meta.globEager('./*/index.tsx');
const components = {} const components = {};
Object.keys(modules).forEach((key: string) => { Object.keys(modules).forEach((key: string) => {
const name = key.replace(/\.\/(.*)\/index\.(tsx|vue)/, '$1') const name = key.replace(/\.\/(.*)\/index\.(tsx|vue)/, '$1');
components[name] = modules[key]?.default || modules[key] components[name] = modules[key]?.default || modules[key];
}) });
console.log(components, 'base-widgets') console.log(components, 'base-widgets');
export default components export default components;

View File

@ -2,8 +2,8 @@ import {
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorSwitchProp, createEditorSwitchProp,
createEditorModelBindProp createEditorModelBindProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
/** /**
* @name: createFieldProps * @name: createFieldProps
@ -15,7 +15,7 @@ import {
export const createFieldProps = () => ({ export const createFieldProps = () => ({
modelValue: createEditorInputProp({ modelValue: createEditorInputProp({
label: '默认值', label: '默认值',
defaultValue: '' defaultValue: '',
}), }),
name: createEditorModelBindProp({ label: '字段绑定', defaultValue: '' }), name: createEditorModelBindProp({ label: '字段绑定', defaultValue: '' }),
label: createEditorInputProp({ label: '输入框左侧文本', defaultValue: '输入框' }), label: createEditorInputProp({ label: '输入框左侧文本', defaultValue: '输入框' }),
@ -27,9 +27,9 @@ export const createFieldProps = () => ({
{ label: '文本域', value: 'textarea' }, { label: '文本域', value: 'textarea' },
{ label: '密码', value: 'password' }, { label: '密码', value: 'password' },
{ label: '电话', value: 'tel' }, { label: '电话', value: 'tel' },
{ label: '小数点', value: 'digit' } { label: '小数点', value: 'digit' },
], ],
defaultValue: 'text' defaultValue: 'text',
}), }),
placeholder: createEditorInputProp({ label: '占位提示文字', defaultValue: '请输入' }), placeholder: createEditorInputProp({ label: '占位提示文字', defaultValue: '请输入' }),
colon: createEditorSwitchProp({ label: '是否在 label 后面添加冒号' }), colon: createEditorSwitchProp({ label: '是否在 label 后面添加冒号' }),
@ -41,18 +41,18 @@ export const createFieldProps = () => ({
options: [ options: [
{ {
label: '左对齐', label: '左对齐',
value: 'left' value: 'left',
}, },
{ {
label: '居中', label: '居中',
value: 'center' value: 'center',
}, },
{ {
label: '右对齐', label: '右对齐',
value: 'right' value: 'right',
} },
], ],
defaultValue: 'left' defaultValue: 'left',
}), }),
readonly: createEditorSwitchProp({ label: '是否为只读状态' }), readonly: createEditorSwitchProp({ label: '是否为只读状态' }),
required: createEditorSwitchProp({ label: '是否显示表单必填星号' }), required: createEditorSwitchProp({ label: '是否显示表单必填星号' }),
@ -62,80 +62,80 @@ export const createFieldProps = () => ({
'arrow-direction': createEditorInputProp({ 'arrow-direction': createEditorInputProp({
label: '箭头方向', label: '箭头方向',
defaultValue: '', defaultValue: '',
tips: '箭头方向,可选值为 left up down' tips: '箭头方向,可选值为 left up down',
}), }),
autosize: createEditorSwitchProp({ autosize: createEditorSwitchProp({
label: '自适应内容高度', label: '自适应内容高度',
defaultValue: false, defaultValue: false,
tips: '是否自适应内容高度,只对 textarea 有效,可传入对象,如 { maxHeight: 100, minHeight: 50 }单位为px' tips: '是否自适应内容高度,只对 textarea 有效,可传入对象,如 { maxHeight: 100, minHeight: 50 }单位为px',
}), }),
border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }), border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }),
center: createEditorSwitchProp({ label: '内容垂直居中' }), center: createEditorSwitchProp({ label: '内容垂直居中' }),
'clear-icon': createEditorInputProp({ 'clear-icon': createEditorInputProp({
label: '清除图标', label: '清除图标',
tips: '清除图标名称或图片链接' tips: '清除图标名称或图片链接',
}), }),
'clear-trigger': createEditorSelectProp({ 'clear-trigger': createEditorSelectProp({
label: '清除图标显示时机', label: '清除图标显示时机',
options: [ options: [
{ label: '输入框不为空时展示', value: 'always' }, { label: '输入框不为空时展示', value: 'always' },
{ label: '输入框聚焦且不为空时展示', value: 'focus' } { label: '输入框聚焦且不为空时展示', value: 'focus' },
], ],
defaultValue: 'always', defaultValue: 'always',
tips: '显示清除图标的时机always 表示输入框不为空时展示focus 表示输入框聚焦且不为空时展示' tips: '显示清除图标的时机always 表示输入框不为空时展示focus 表示输入框聚焦且不为空时展示',
}), }),
clearable: createEditorSwitchProp({ clearable: createEditorSwitchProp({
label: '是否启用清除图标', label: '是否启用清除图标',
defaultValue: false, defaultValue: false,
tips: '是否启用清除图标,点击清除图标后会清空输入框' tips: '是否启用清除图标,点击清除图标后会清空输入框',
}), }),
clickable: createEditorSwitchProp({ label: '是否开启点击反馈' }), clickable: createEditorSwitchProp({ label: '是否开启点击反馈' }),
'format-trigger': createEditorInputProp({ label: '格式化函数触发的时机' }), 'format-trigger': createEditorInputProp({ label: '格式化函数触发的时机' }),
formatter: createEditorInputProp({ label: '输入内容格式化函数' }), formatter: createEditorInputProp({ label: '输入内容格式化函数' }),
'icon-prefix': createEditorInputProp({ 'icon-prefix': createEditorInputProp({
label: '图标类名前缀', label: '图标类名前缀',
tips: '图标类名前缀,同 Icon 组件的 class-prefix 属性' tips: '图标类名前缀,同 Icon 组件的 class-prefix 属性',
}), }),
'input-align': createEditorSelectProp({ 'input-align': createEditorSelectProp({
label: '输入框对齐方式', label: '输入框对齐方式',
options: [ options: [
{ {
label: '左对齐', label: '左对齐',
value: 'left' value: 'left',
}, },
{ {
label: '居中', label: '居中',
value: 'center' value: 'center',
}, },
{ {
label: '右对齐', label: '右对齐',
value: 'right' value: 'right',
} },
], ],
defaultValue: 'left' defaultValue: 'left',
}), }),
'label-align': createEditorSelectProp({ 'label-align': createEditorSelectProp({
label: '左侧文本对齐方式', label: '左侧文本对齐方式',
options: [ options: [
{ {
label: '左对齐', label: '左对齐',
value: 'left' value: 'left',
}, },
{ {
label: '居中', label: '居中',
value: 'center' value: 'center',
}, },
{ {
label: '右对齐', label: '右对齐',
value: 'right' value: 'right',
} },
], ],
defaultValue: 'left' defaultValue: 'left',
}), }),
'label-width': createEditorInputProp({ label: '左侧文本宽度' }), 'label-width': createEditorInputProp({ label: '左侧文本宽度' }),
maxlength: createEditorInputProp({ label: '输入的最大字符数', defaultValue: 500 }), maxlength: createEditorInputProp({ label: '输入的最大字符数', defaultValue: 500 }),
'show-word-limit': createEditorSwitchProp({ 'show-word-limit': createEditorSwitchProp({
label: '是否显示字数统计', label: '是否显示字数统计',
tips: '需要设置 maxlength 属性' tips: '需要设置 maxlength 属性',
}) }),
}) });

View File

@ -6,10 +6,10 @@
* @Description: - * @Description: -
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\input\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\input\index.tsx
*/ */
import { Field } from 'vant' import { Field } from 'vant';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils' import { createFieldProps } from './createFieldProps';
import { createFieldProps } from './createFieldProps' import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties' import { useGlobalProperties } from '@/hooks/useGlobalProperties';
export default { export default {
key: 'input', key: 'input',
@ -19,11 +19,11 @@ export default {
<Field name="用户名" label="用户名" labelWidth={50} colon placeholder="请输入用户名" /> <Field name="用户名" label="用户名" labelWidth={50} colon placeholder="请输入用户名" />
), ),
render: ({ styles, block, props }) => { render: ({ styles, block, props }) => {
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
let rules = [] let rules = [];
try { try {
rules = JSON.parse(props.rules) rules = JSON.parse(props.rules);
} catch (e) {} } catch (e) {}
return () => ( return () => (
@ -36,7 +36,7 @@ export default {
rules={rules} rules={rules}
/> />
</div> </div>
) );
}, },
events: [ events: [
{ label: '输入框内容变化时触发', value: 'update:model-value' }, { label: '输入框内容变化时触发', value: 'update:model-value' },
@ -46,13 +46,13 @@ export default {
{ label: '点击组件时触发', value: 'click' }, { label: '点击组件时触发', value: 'click' },
{ label: '点击输入区域时触发', value: 'click-input' }, { label: '点击输入区域时触发', value: 'click-input' },
{ label: '点击左侧图标时触发', value: 'click-left-icon' }, { label: '点击左侧图标时触发', value: 'click-left-icon' },
{ label: '点击右侧图标时触发', value: 'click-right-icon' } { label: '点击右侧图标时触发', value: 'click-right-icon' },
], ],
props: createFieldProps(), props: createFieldProps(),
resize: { resize: {
width: true width: true,
}, },
model: { model: {
default: '绑定字段' default: '绑定字段',
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -6,11 +6,11 @@
* @Description: * @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\nav-bar\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\nav-bar\index.tsx
*/ */
import { NavBar } from 'vant' import { onBeforeUnmount, onMounted } from 'vue';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils' import { NavBar } from 'vant';
import { createEditorInputProp, createEditorSwitchProp } from '@/visual-editor/visual-editor.props' import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties' import { createEditorInputProp, createEditorSwitchProp } from '@/visual-editor/visual-editor.props';
import { onBeforeUnmount, onMounted } from 'vue' import { useGlobalProperties } from '@/hooks/useGlobalProperties';
export default { export default {
key: 'nav-bar', key: 'nav-bar',
@ -20,42 +20,42 @@ export default {
<NavBar title="标题" left-text="返回" right-text="按钮" left-arrow style={{ width: '100%' }} /> <NavBar title="标题" left-text="返回" right-text="按钮" left-arrow style={{ width: '100%' }} />
), ),
render: ({ props, block }) => { render: ({ props, block }) => {
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
onMounted(() => { onMounted(() => {
const compEl = window.$$refs[block._vid]?.$el const compEl = window.$$refs[block._vid]?.$el;
const draggableEl = compEl?.closest('div[data-draggable]') const draggableEl = compEl?.closest('div[data-draggable]');
const navbarEl = draggableEl?.querySelector('.van-nav-bar--fixed') as HTMLDivElement const navbarEl = draggableEl?.querySelector('.van-nav-bar--fixed') as HTMLDivElement;
const dragArea = document.querySelector( const dragArea = document.querySelector(
'.simulator-editor-content > .dragArea ' '.simulator-editor-content > .dragArea ',
) as HTMLDivElement ) as HTMLDivElement;
if (draggableEl && navbarEl && dragArea) { if (draggableEl && navbarEl && dragArea) {
navbarEl.style.position = 'unset' navbarEl.style.position = 'unset';
draggableEl.style.position = 'fixed' draggableEl.style.position = 'fixed';
draggableEl.style.top = '0' draggableEl.style.top = '0';
draggableEl.style.left = '0' draggableEl.style.left = '0';
draggableEl.style.width = '100%' draggableEl.style.width = '100%';
dragArea.style.paddingTop = '50px' dragArea.style.paddingTop = '50px';
} else { } else {
document.body.style.paddingTop = '46px' document.body.style.paddingTop = '46px';
const slotEl = compEl?.closest('__slot-item') const slotEl = compEl?.closest('__slot-item');
if (slotEl) { if (slotEl) {
slotEl.style.position = 'fixed' slotEl.style.position = 'fixed';
slotEl.style.bottom = '0' slotEl.style.bottom = '0';
} }
} }
}) });
onBeforeUnmount(() => { onBeforeUnmount(() => {
const dragArea = document.querySelector( const dragArea = document.querySelector(
'.simulator-editor-content > .dragArea ' '.simulator-editor-content > .dragArea ',
) as HTMLDivElement ) as HTMLDivElement;
if (dragArea) { if (dragArea) {
dragArea.style.paddingTop = '' dragArea.style.paddingTop = '';
} }
}) });
return () => <NavBar ref={(el) => registerRef(el, block._vid)} {...props} /> return () => <NavBar ref={(el) => registerRef(el, block._vid)} {...props} />;
}, },
props: { props: {
title: createEditorInputProp({ label: '标题', defaultValue: '标题' }), title: createEditorInputProp({ label: '标题', defaultValue: '标题' }),
@ -69,15 +69,15 @@ export default {
border: createEditorSwitchProp({ label: '是否显示下边框', defaultValue: false }), border: createEditorSwitchProp({ label: '是否显示下边框', defaultValue: false }),
leftText: createEditorInputProp({ label: '左侧文案', defaultValue: '返回' }), leftText: createEditorInputProp({ label: '左侧文案', defaultValue: '返回' }),
rightText: createEditorInputProp({ label: '右侧文案', defaultValue: '按钮' }), rightText: createEditorInputProp({ label: '右侧文案', defaultValue: '按钮' }),
leftArrow: createEditorSwitchProp({ label: '是否显示左侧箭头', defaultValue: true }) leftArrow: createEditorSwitchProp({ label: '是否显示左侧箭头', defaultValue: true }),
}, },
events: [ events: [
{ label: '点击左侧按钮时触发', value: 'click-left' }, { label: '点击左侧按钮时触发', value: 'click-left' },
{ label: '点击右侧按钮时触发', value: 'click-right' } { label: '点击右侧按钮时触发', value: 'click-right' },
], ],
showStyleConfig: false, showStyleConfig: false,
draggable: false, draggable: false,
resize: { resize: {
width: true width: true,
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -9,8 +9,8 @@
import { import {
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorSwitchProp createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
export const createFieldProps = () => ({ export const createFieldProps = () => ({
background: createEditorInputProp({ label: '滚动条背景' }), background: createEditorInputProp({ label: '滚动条背景' }),
@ -22,23 +22,23 @@ export const createFieldProps = () => ({
options: [ options: [
{ {
label: '默认', label: '默认',
value: '' value: '',
}, },
{ {
label: '可关闭', label: '可关闭',
value: 'closeable' value: 'closeable',
}, },
{ {
label: '链接', label: '链接',
value: 'link' value: 'link',
} },
] ],
}), }),
scrollable: createEditorSwitchProp({ label: '是否开启滚动播放,内容长度溢出时默认开启' }), scrollable: createEditorSwitchProp({ label: '是否开启滚动播放,内容长度溢出时默认开启' }),
speed: createEditorInputProp({ label: '滚动速率 (px/s)' }), speed: createEditorInputProp({ label: '滚动速率 (px/s)' }),
text: createEditorInputProp({ text: createEditorInputProp({
label: '通知文本内容', label: '通知文本内容',
defaultValue: '在代码阅读过程中人们说脏话的频率是衡量代码质量的唯一标准。' defaultValue: '在代码阅读过程中人们说脏话的频率是衡量代码质量的唯一标准。',
}), }),
wrapable: createEditorSwitchProp({ label: '是否开启文本换行,只在禁用滚动时生效' }) wrapable: createEditorSwitchProp({ label: '是否开启文本换行,只在禁用滚动时生效' }),
}) });

View File

@ -6,10 +6,10 @@
* @Description: * @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\notice-bar\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\notice-bar\index.tsx
*/ */
import { NoticeBar } from 'vant' import { NoticeBar } from 'vant';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils' import { createFieldProps } from './createFieldProps';
import { createFieldProps } from './createFieldProps' import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties' import { useGlobalProperties } from '@/hooks/useGlobalProperties';
export default { export default {
key: 'NoticeBar', key: 'NoticeBar',
@ -23,24 +23,24 @@ export default {
/> />
), ),
render: ({ block, props, styles }) => { render: ({ block, props, styles }) => {
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
return () => ( return () => (
<div style={styles}> <div style={styles}>
<NoticeBar ref={(el) => registerRef(el, block._vid)} style={{ width: '100%' }} {...props} /> <NoticeBar ref={(el) => registerRef(el, block._vid)} style={{ width: '100%' }} {...props} />
</div> </div>
) );
}, },
events: [ events: [
{ label: '点击通知栏时触发', value: 'click' }, { label: '点击通知栏时触发', value: 'click' },
{ label: '关闭通知栏时触发', value: 'close' }, { label: '关闭通知栏时触发', value: 'close' },
{ label: '每当滚动栏重新开始滚动时触发', value: 'replay' } { label: '每当滚动栏重新开始滚动时触发', value: 'replay' },
], ],
props: createFieldProps(), props: createFieldProps(),
resize: { resize: {
width: true width: true,
}, },
model: { model: {
default: '绑定字段' default: '绑定字段',
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -1,8 +1,8 @@
import { import {
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorSwitchProp createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
/** /**
* @name: createFieldProps * @name: createFieldProps
@ -19,21 +19,21 @@ export const createFieldProps = () => ({
options: [ options: [
{ {
label: '左对齐', label: '左对齐',
value: 'left' value: 'left',
}, },
{ {
label: '居中', label: '居中',
value: 'center' value: 'center',
}, },
{ {
label: '右对齐', label: '右对齐',
value: 'right' value: 'right',
} },
], ],
defaultValue: 'left' defaultValue: 'left',
}), }),
border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }), border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }),
readonly: createEditorSwitchProp({ label: '是否为只读状态' }), readonly: createEditorSwitchProp({ label: '是否为只读状态' }),
required: createEditorSwitchProp({ label: '是否显示表单必填星号' }), required: createEditorSwitchProp({ label: '是否显示表单必填星号' }),
rules: createEditorInputProp({ label: '表单校验规则' }) rules: createEditorInputProp({ label: '表单校验规则' }),
}) });

View File

@ -6,16 +6,16 @@
* @Description: - * @Description: -
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\picker\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\picker\index.tsx
*/ */
import { Field, Popup, Picker } from 'vant' import { reactive, useAttrs } from 'vue';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils' import { Field, Popup, Picker } from 'vant';
import { createFieldProps } from './createFieldProps' import { createFieldProps } from './createFieldProps';
import { useGlobalProperties } from '@/hooks/useGlobalProperties' import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { import {
createEditorCrossSortableProp, createEditorCrossSortableProp,
createEditorInputProp, createEditorInputProp,
createEditorModelBindProp createEditorModelBindProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
import { reactive, useAttrs } from 'vue'
export default { export default {
key: 'picker', key: 'picker',
@ -23,31 +23,31 @@ export default {
label: '表单项类型 - 选择器', label: '表单项类型 - 选择器',
preview: () => <Field name="picker" label="选择器" placeholder={'点击选择'}></Field>, preview: () => <Field name="picker" label="选择器" placeholder={'点击选择'}></Field>,
render: ({ styles, block, props }) => { render: ({ styles, block, props }) => {
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
const attrs = useAttrs() const attrs = useAttrs();
const state = reactive({ const state = reactive({
showPicker: false, showPicker: false,
text: '', text: '',
defaultIndex: 0 defaultIndex: 0,
}) });
const customFieldName = { const customFieldName = {
text: 'label', text: 'label',
value: 'value' value: 'value',
} };
const onConfirm = (value) => { const onConfirm = (value) => {
props.modelValue = value.value props.modelValue = value.value;
state.text = value[props.valueKey || 'text'] state.text = value[props.valueKey || 'text'];
state.showPicker = false state.showPicker = false;
console.log(props) console.log(props);
} };
return () => { return () => {
if (props.modelValue) { if (props.modelValue) {
state.defaultIndex = props.columns?.findIndex((item) => item.value == props.modelValue) state.defaultIndex = props.columns?.findIndex((item) => item.value == props.modelValue);
state.text = props.columns[state.defaultIndex]?.label state.text = props.columns[state.defaultIndex]?.label;
} }
return ( return (
@ -66,7 +66,7 @@ export default {
<span class={'placeholder'}>{props.placeholder}</span> <span class={'placeholder'}>{props.placeholder}</span>
) : ( ) : (
state.text state.text
) ),
}} }}
</Field> </Field>
<Popup v-model={[state.showPicker, 'show']} position={'bottom'}> <Popup v-model={[state.showPicker, 'show']} position={'bottom'}>
@ -81,8 +81,8 @@ export default {
/> />
</Popup> </Popup>
</div> </div>
) );
} };
}, },
props: { props: {
modelValue: createEditorInputProp({ label: '默认值' }), modelValue: createEditorInputProp({ label: '默认值' }),
@ -94,21 +94,21 @@ export default {
multiple: false, multiple: false,
defaultValue: [ defaultValue: [
{ label: '杭州', value: 'hangzhou' }, { label: '杭州', value: 'hangzhou' },
{ label: '上海', value: 'shanghai' } { label: '上海', value: 'shanghai' },
] ],
}), }),
placeholder: createEditorInputProp({ label: '占位符', defaultValue: '请选择' }), placeholder: createEditorInputProp({ label: '占位符', defaultValue: '请选择' }),
...createFieldProps() ...createFieldProps(),
}, },
events: [ events: [
{ label: '点击完成按钮时触发', value: 'confirm' }, { label: '点击完成按钮时触发', value: 'confirm' },
{ label: '点击取消按钮时触发', value: 'cancel' }, { label: '点击取消按钮时触发', value: 'cancel' },
{ label: '选项改变时触发', value: 'change' } { label: '选项改变时触发', value: 'change' },
], ],
resize: { resize: {
width: true width: true,
}, },
model: { model: {
default: '绑定字段' default: '绑定字段',
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -6,14 +6,14 @@
* @Description: * @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\process\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\process\index.tsx
*/ */
import { Progress } from 'vant' import { Progress } from 'vant';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { import {
createEditorColorProp, createEditorColorProp,
createEditorSwitchProp, createEditorSwitchProp,
createEditorInputProp, createEditorInputProp,
createEditorInputNumberProp createEditorInputNumberProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'
export default { export default {
key: 'process', key: 'process',
@ -21,13 +21,13 @@ export default {
label: '进度条', label: '进度条',
preview: () => <Progress style="width:190px" percentage={50} />, preview: () => <Progress style="width:190px" percentage={50} />,
render: ({ props, styles }) => { render: ({ props, styles }) => {
const RenderProgress = () => <Progress {...props} pivotText={props.pivotText || undefined} /> const RenderProgress = () => <Progress {...props} pivotText={props.pivotText || undefined} />;
return () => ( return () => (
<div style={styles}> <div style={styles}>
<RenderProgress /> <RenderProgress />
</div> </div>
) );
}, },
props: { props: {
percentage: createEditorInputNumberProp({ label: '进度百分比', defaultValue: 50 }), percentage: createEditorInputNumberProp({ label: '进度百分比', defaultValue: 50 }),
@ -38,6 +38,6 @@ export default {
pivotText: createEditorInputProp({ label: '进度文字内容' }), pivotText: createEditorInputProp({ label: '进度文字内容' }),
pivotColor: createEditorColorProp({ label: '进度文字背景色', defaultValue: '#1989fa' }), pivotColor: createEditorColorProp({ label: '进度文字背景色', defaultValue: '#1989fa' }),
textColor: createEditorColorProp({ label: '进度文字颜色', defaultValue: '#ffffff' }), textColor: createEditorColorProp({ label: '进度文字颜色', defaultValue: '#ffffff' }),
showPivot: createEditorSwitchProp({ label: '是否显示进度文字', defaultValue: true }) showPivot: createEditorSwitchProp({ label: '是否显示进度文字', defaultValue: true }),
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -1,8 +1,8 @@
import { import {
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorSwitchProp createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
/** /**
* @name: createFieldProps * @name: createFieldProps
@ -19,21 +19,21 @@ export const createFieldProps = () => ({
options: [ options: [
{ {
label: '左对齐', label: '左对齐',
value: 'left' value: 'left',
}, },
{ {
label: '居中', label: '居中',
value: 'center' value: 'center',
}, },
{ {
label: '右对齐', label: '右对齐',
value: 'right' value: 'right',
} },
], ],
defaultValue: 'left' defaultValue: 'left',
}), }),
border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }), border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }),
readonly: createEditorSwitchProp({ label: '是否为只读状态' }), readonly: createEditorSwitchProp({ label: '是否为只读状态' }),
required: createEditorSwitchProp({ label: '是否显示表单必填星号' }), required: createEditorSwitchProp({ label: '是否显示表单必填星号' }),
rules: createEditorInputProp({ label: '表单校验规则' }) rules: createEditorInputProp({ label: '表单校验规则' }),
}) });

View File

@ -6,16 +6,16 @@
* @Description: - * @Description: -
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\radio\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\radio\index.tsx
*/ */
import { Field, Radio, RadioGroup } from 'vant' import { Field, Radio, RadioGroup } from 'vant';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils' import { createFieldProps } from './createFieldProps';
import { createFieldProps } from './createFieldProps' import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties' import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { import {
createEditorCrossSortableProp, createEditorCrossSortableProp,
createEditorInputProp, createEditorInputProp,
createEditorModelBindProp, createEditorModelBindProp,
createEditorSelectProp createEditorSelectProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
export default { export default {
key: 'radio', key: 'radio',
@ -28,7 +28,7 @@ export default {
</RadioGroup> </RadioGroup>
), ),
render: ({ styles, block, props }) => { render: ({ styles, block, props }) => {
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
return () => ( return () => (
<div style={styles}> <div style={styles}>
@ -49,11 +49,11 @@ export default {
</Radio> </Radio>
))} ))}
</RadioGroup> </RadioGroup>
) ),
}} }}
/> />
</div> </div>
) );
}, },
props: { props: {
modelValue: createEditorInputProp({ label: '默认值', defaultValue: '' }), modelValue: createEditorInputProp({ label: '默认值', defaultValue: '' }),
@ -66,30 +66,30 @@ export default {
defaultValue: [ defaultValue: [
{ label: '胡萝卜', value: 'carrot' }, { label: '胡萝卜', value: 'carrot' },
{ label: '白菜', value: 'cabbage' }, { label: '白菜', value: 'cabbage' },
{ label: '猪', value: 'pig' } { label: '猪', value: 'pig' },
] ],
}), }),
direction: createEditorSelectProp({ direction: createEditorSelectProp({
label: '排列方向', label: '排列方向',
options: [ options: [
{ {
label: '水平', label: '水平',
value: 'horizontal' value: 'horizontal',
}, },
{ {
label: '垂直', label: '垂直',
value: 'vertical' value: 'vertical',
} },
], ],
defaultValue: 'horizontal' defaultValue: 'horizontal',
}), }),
...createFieldProps() ...createFieldProps(),
}, },
events: [{ label: '点击单选框时触发', value: 'click' }], events: [{ label: '点击单选框时触发', value: 'click' }],
resize: { resize: {
width: true width: true,
}, },
model: { model: {
default: '绑定字段' default: '绑定字段',
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -1,8 +1,8 @@
import { import {
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorSwitchProp createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
/** /**
* @name: createFieldProps * @name: createFieldProps
@ -19,21 +19,21 @@ export const createFieldProps = () => ({
options: [ options: [
{ {
label: '左对齐', label: '左对齐',
value: 'left' value: 'left',
}, },
{ {
label: '居中', label: '居中',
value: 'center' value: 'center',
}, },
{ {
label: '右对齐', label: '右对齐',
value: 'right' value: 'right',
} },
], ],
defaultValue: 'left' defaultValue: 'left',
}), }),
border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }), border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }),
readonly: createEditorSwitchProp({ label: '是否为只读状态' }), readonly: createEditorSwitchProp({ label: '是否为只读状态' }),
required: createEditorSwitchProp({ label: '是否显示表单必填星号' }), required: createEditorSwitchProp({ label: '是否显示表单必填星号' }),
rules: createEditorInputProp({ label: '表单校验规则' }) rules: createEditorInputProp({ label: '表单校验规则' }),
}) });

View File

@ -7,8 +7,8 @@
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\rate\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\rate\index.tsx
*/ */
import { Field, Rate } from 'vant'; import { Field, Rate } from 'vant';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { createFieldProps } from './createFieldProps'; import { createFieldProps } from './createFieldProps';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties'; import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { import {
createEditorInputNumberProp, createEditorInputNumberProp,

View File

@ -1,8 +1,8 @@
import { import {
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorSwitchProp createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
/** /**
* @name: createFieldProps * @name: createFieldProps
@ -19,21 +19,21 @@ export const createFieldProps = () => ({
options: [ options: [
{ {
label: '左对齐', label: '左对齐',
value: 'left' value: 'left',
}, },
{ {
label: '居中', label: '居中',
value: 'center' value: 'center',
}, },
{ {
label: '右对齐', label: '右对齐',
value: 'right' value: 'right',
} },
], ],
defaultValue: 'left' defaultValue: 'left',
}), }),
border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }), border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }),
readonly: createEditorSwitchProp({ label: '是否为只读状态' }), readonly: createEditorSwitchProp({ label: '是否为只读状态' }),
required: createEditorSwitchProp({ label: '是否显示表单必填星号' }), required: createEditorSwitchProp({ label: '是否显示表单必填星号' }),
rules: createEditorInputProp({ label: '表单校验规则' }) rules: createEditorInputProp({ label: '表单校验规则' }),
}) });

View File

@ -7,8 +7,9 @@
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\slider\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\slider\index.tsx
*/ */
import { Field, Slider } from 'vant'; import { Field, Slider } from 'vant';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'; import { omit } from 'lodash-es';
import { createFieldProps } from './createFieldProps'; import { createFieldProps } from './createFieldProps';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties'; import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { import {
createEditorInputNumberProp, createEditorInputNumberProp,
@ -16,7 +17,6 @@ import {
createEditorModelBindProp, createEditorModelBindProp,
createEditorSwitchProp, createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props'; } from '@/visual-editor/visual-editor.props';
import { omit } from 'lodash-es';
export default { export default {
key: 'slider', key: 'slider',

View File

@ -1,8 +1,8 @@
import { import {
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorSwitchProp createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
/** /**
* @name: createFieldProps * @name: createFieldProps
@ -19,21 +19,21 @@ export const createFieldProps = () => ({
options: [ options: [
{ {
label: '左对齐', label: '左对齐',
value: 'left' value: 'left',
}, },
{ {
label: '居中', label: '居中',
value: 'center' value: 'center',
}, },
{ {
label: '右对齐', label: '右对齐',
value: 'right' value: 'right',
} },
], ],
defaultValue: 'left' defaultValue: 'left',
}), }),
border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }), border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }),
readonly: createEditorSwitchProp({ label: '是否为只读状态' }), readonly: createEditorSwitchProp({ label: '是否为只读状态' }),
required: createEditorSwitchProp({ label: '是否显示表单必填星号' }), required: createEditorSwitchProp({ label: '是否显示表单必填星号' }),
rules: createEditorInputProp({ label: '表单校验规则' }) rules: createEditorInputProp({ label: '表单校验规则' }),
}) });

View File

@ -6,18 +6,18 @@
* @Description: ' - * @Description: ' -
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\stepper\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\stepper\index.tsx
*/ */
import { Field, Stepper } from 'vant' import { watchEffect } from 'vue';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils' import { Field, Stepper } from 'vant';
import { createFieldProps } from './createFieldProps' import { createFieldProps } from './createFieldProps';
import { useGlobalProperties } from '@/hooks/useGlobalProperties' import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { import {
createEditorInputNumberProp, createEditorInputNumberProp,
createEditorInputProp, createEditorInputProp,
createEditorSwitchProp, createEditorSwitchProp,
createEditorSelectProp, createEditorSelectProp,
createEditorModelBindProp createEditorModelBindProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
import { watchEffect } from 'vue'
export default { export default {
key: 'stepper', key: 'stepper',
@ -32,11 +32,11 @@ export default {
></Field> ></Field>
), ),
render: ({ styles, block, props }) => { render: ({ styles, block, props }) => {
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
watchEffect(() => { watchEffect(() => {
props.name = Array.isArray(props.name) ? [...props.name].pop() : props.name props.name = Array.isArray(props.name) ? [...props.name].pop() : props.name;
}) });
return () => ( return () => (
<div style={styles}> <div style={styles}>
@ -50,11 +50,11 @@ export default {
v-model={props.modelValue} v-model={props.modelValue}
{...props} {...props}
></Stepper> ></Stepper>
) ),
}} }}
/> />
</div> </div>
) );
}, },
props: { props: {
modelValue: createEditorInputNumberProp({ label: '默认值', defaultValue: 0 }), modelValue: createEditorInputNumberProp({ label: '默认值', defaultValue: 0 }),
@ -66,12 +66,12 @@ export default {
allowEmpty: createEditorSwitchProp({ label: '是否允许输入的值为空', defaultValue: false }), allowEmpty: createEditorSwitchProp({ label: '是否允许输入的值为空', defaultValue: false }),
buttonSize: createEditorInputProp({ buttonSize: createEditorInputProp({
label: '按钮大小以及输入框高度,默认单位为 px', label: '按钮大小以及输入框高度,默认单位为 px',
defaultValue: '28px' defaultValue: '28px',
}), }),
decimalLength: createEditorInputProp({ label: '固定显示的小数位数', defaultValue: '' }), decimalLength: createEditorInputProp({ label: '固定显示的小数位数', defaultValue: '' }),
defaultValue: createEditorInputProp({ defaultValue: createEditorInputProp({
label: '初始值,当 v-model 为空时生效', label: '初始值,当 v-model 为空时生效',
defaultValue: '1' defaultValue: '1',
}), }),
disableInput: createEditorSwitchProp({ label: '是否禁用输入框', defaultValue: false }), disableInput: createEditorSwitchProp({ label: '是否禁用输入框', defaultValue: false }),
disableMinus: createEditorSwitchProp({ label: '是否禁用减少按钮', defaultValue: false }), disableMinus: createEditorSwitchProp({ label: '是否禁用减少按钮', defaultValue: false }),
@ -90,17 +90,17 @@ export default {
options: [ options: [
{ {
label: '默认', label: '默认',
value: '' value: '',
}, },
{ label: '圆角风格', value: 'round' } { label: '圆角风格', value: 'round' },
], ],
defaultValue: '' defaultValue: '',
}) }),
}, },
resize: { resize: {
width: true width: true,
}, },
model: { model: {
default: '绑定字段' default: '绑定字段',
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -9,14 +9,17 @@
import { import {
createEditorInputProp, createEditorInputProp,
createEditorSwitchProp, createEditorSwitchProp,
createEditorCrossSortableProp createEditorCrossSortableProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
export const createFieldProps = () => ({ export const createFieldProps = () => ({
images: createEditorCrossSortableProp({ images: createEditorCrossSortableProp({
label: '图片列表', label: '图片列表',
labelPosition: 'top', labelPosition: 'top',
defaultValue: ['https://img.yzcdn.cn/vant/apple-1.jpg', 'https://img.yzcdn.cn/vant/apple-2.jpg'] defaultValue: [
'https://img.yzcdn.cn/vant/apple-1.jpg',
'https://img.yzcdn.cn/vant/apple-2.jpg',
],
}), }),
// width: createEditorInputProp({ label: '滑块宽度,单位为 px', defaultValue: 'auto' }), // width: createEditorInputProp({ label: '滑块宽度,单位为 px', defaultValue: 'auto' }),
height: createEditorInputProp({ label: '滑块高度,单位为 px', defaultValue: '200' }), height: createEditorInputProp({ label: '滑块高度,单位为 px', defaultValue: '200' }),
@ -29,5 +32,5 @@ export const createFieldProps = () => ({
showIndicators: createEditorSwitchProp({ label: '是否显示指示器', defaultValue: true }), showIndicators: createEditorSwitchProp({ label: '是否显示指示器', defaultValue: true }),
stopPropagation: createEditorSwitchProp({ label: '是否阻止滑动事件冒泡', defaultValue: true }), stopPropagation: createEditorSwitchProp({ label: '是否阻止滑动事件冒泡', defaultValue: true }),
touchable: createEditorSwitchProp({ label: '是否可以通过手势滑动', defaultValue: true }), touchable: createEditorSwitchProp({ label: '是否可以通过手势滑动', defaultValue: true }),
vertical: createEditorSwitchProp({ label: '是否为纵向滚动', defaultValue: false }) vertical: createEditorSwitchProp({ label: '是否为纵向滚动', defaultValue: false }),
}) });

View File

@ -6,16 +6,16 @@
* @Description: * @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\swipe\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\swipe\index.tsx
*/ */
import { Swipe, SwipeItem } from 'vant' import { Swipe, SwipeItem } from 'vant';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils' import { createFieldProps } from './createFieldProps';
import { createFieldProps } from './createFieldProps' import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties' import { useGlobalProperties } from '@/hooks/useGlobalProperties';
const swipeItemStyle = `color: #fff; const swipeItemStyle = `color: #fff;
font-size: 20px; font-size: 20px;
line-height: 150px; line-height: 150px;
text-align: center; text-align: center;
background-color: #39a9ed;` background-color: #39a9ed;`;
export default { export default {
key: 'swipe', key: 'swipe',
@ -30,7 +30,7 @@ export default {
</Swipe> </Swipe>
), ),
render: ({ block, props }) => { render: ({ block, props }) => {
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
return () => ( return () => (
<div> <div>
@ -48,15 +48,15 @@ export default {
))} ))}
</Swipe> </Swipe>
</div> </div>
) );
}, },
props: createFieldProps(), props: createFieldProps(),
events: [{ label: '每一页轮播结束后触发', value: 'change' }], events: [{ label: '每一页轮播结束后触发', value: 'change' }],
showStyleConfig: false, showStyleConfig: false,
resize: { resize: {
width: true width: true,
}, },
model: { model: {
default: '绑定字段' default: '绑定字段',
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -1,8 +1,8 @@
import { import {
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorSwitchProp createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
/** /**
* @name: createFieldProps * @name: createFieldProps
@ -19,21 +19,21 @@ export const createFieldProps = () => ({
options: [ options: [
{ {
label: '左对齐', label: '左对齐',
value: 'left' value: 'left',
}, },
{ {
label: '居中', label: '居中',
value: 'center' value: 'center',
}, },
{ {
label: '右对齐', label: '右对齐',
value: 'right' value: 'right',
} },
], ],
defaultValue: 'left' defaultValue: 'left',
}), }),
border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }), border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }),
readonly: createEditorSwitchProp({ label: '是否为只读状态' }), readonly: createEditorSwitchProp({ label: '是否为只读状态' }),
required: createEditorSwitchProp({ label: '是否显示表单必填星号' }), required: createEditorSwitchProp({ label: '是否显示表单必填星号' }),
rules: createEditorInputProp({ label: '表单校验规则' }) rules: createEditorInputProp({ label: '表单校验规则' }),
}) });

View File

@ -6,16 +6,16 @@
* @Description: - * @Description: -
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\switch\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\switch\index.tsx
*/ */
import { Field, Switch } from 'vant' import { Field, Switch } from 'vant';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils' import { createFieldProps } from './createFieldProps';
import { createFieldProps } from './createFieldProps' import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties' import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { import {
createEditorInputProp, createEditorInputProp,
createEditorSwitchProp, createEditorSwitchProp,
createEditorColorProp, createEditorColorProp,
createEditorModelBindProp createEditorModelBindProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
export default { export default {
key: 'switch', key: 'switch',
@ -25,7 +25,7 @@ export default {
<Field name="switch" label="开关" v-slots={{ input: () => <Switch size={20} /> }} /> <Field name="switch" label="开关" v-slots={{ input: () => <Switch size={20} /> }} />
), ),
render: ({ styles, block, props }) => { render: ({ styles, block, props }) => {
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
return () => ( return () => (
<div style={styles}> <div style={styles}>
@ -40,11 +40,11 @@ export default {
{...props} {...props}
v-model={props.modelValue} v-model={props.modelValue}
/> />
) ),
}} }}
/> />
</div> </div>
) );
}, },
props: { props: {
modelValue: createEditorInputProp({ label: '默认值', defaultValue: 'false' }), modelValue: createEditorInputProp({ label: '默认值', defaultValue: 'false' }),
@ -57,16 +57,16 @@ export default {
disabled: createEditorSwitchProp({ label: '是否为禁用状态' }), disabled: createEditorSwitchProp({ label: '是否为禁用状态' }),
loading: createEditorSwitchProp({ label: '是否为加载状态' }), loading: createEditorSwitchProp({ label: '是否为加载状态' }),
size: createEditorInputProp({ label: '开关尺寸', defaultValue: '20px' }), size: createEditorInputProp({ label: '开关尺寸', defaultValue: '20px' }),
...createFieldProps() ...createFieldProps(),
}, },
events: [ events: [
{ label: '开关状态切换时触发', value: 'change' }, { label: '开关状态切换时触发', value: 'change' },
{ label: '点击时触发', value: 'click' } { label: '点击时触发', value: 'click' },
], ],
resize: { resize: {
width: true width: true,
}, },
model: { model: {
default: '绑定字段' default: '绑定字段',
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -6,7 +6,9 @@
* @Description: * @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\tabbar\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\tabbar\index.tsx
*/ */
import { onMounted, onBeforeUnmount } from 'vue';
import { Tabbar, TabbarItem } from 'vant'; import { Tabbar, TabbarItem } from 'vant';
import { getTabbarItem } from './tabbar-item';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'; import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { import {
createEditorCrossSortableProp, createEditorCrossSortableProp,
@ -15,10 +17,8 @@ import {
createEditorColorProp, createEditorColorProp,
} from '@/visual-editor/visual-editor.props'; } from '@/visual-editor/visual-editor.props';
import { useGlobalProperties } from '@/hooks/useGlobalProperties'; import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { getTabbarItem } from './tabbar-item';
import { createNewBlock } from '@/visual-editor/visual-editor.utils'; import { createNewBlock } from '@/visual-editor/visual-editor.utils';
import { BASE_URL } from '@/visual-editor/utils'; import { BASE_URL } from '@/visual-editor/utils';
import { onMounted, onBeforeUnmount } from 'vue';
const defaultTabbarItems = [ const defaultTabbarItems = [
{ {

View File

@ -50,5 +50,5 @@ export const fontArr = [
{ label: '方正姚体', value: 'FZYaoti' }, { label: '方正姚体', value: 'FZYaoti' },
{ label: '思源黑体', value: 'Source Han Sans CN' }, { label: '思源黑体', value: 'Source Han Sans CN' },
{ label: '思源宋体', value: 'Source Han Serif SC' }, { label: '思源宋体', value: 'Source Han Serif SC' },
{ label: '文泉驿微米黑', value: 'WenQuanYi Micro Hei' } { label: '文泉驿微米黑', value: 'WenQuanYi Micro Hei' },
] ];

View File

@ -6,15 +6,15 @@
* @Description: * @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\text\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\text\index.tsx
*/ */
import { useGlobalProperties } from '@/hooks/useGlobalProperties' import { fontArr } from './fontArr';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { import {
createEditorColorProp, createEditorColorProp,
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorInputNumberProp createEditorInputNumberProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'
import { fontArr } from './fontArr'
export default { export default {
key: 'text', key: 'text',
@ -22,7 +22,7 @@ export default {
label: '文本', label: '文本',
preview: () => <span></span>, preview: () => <span></span>,
render: ({ props, block, styles }) => { render: ({ props, block, styles }) => {
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
return () => ( return () => (
<div <div
@ -31,12 +31,12 @@ export default {
color: props.color, color: props.color,
fontSize: `${parseFloat(props.size)}px`, fontSize: `${parseFloat(props.size)}px`,
fontFamily: props.font, fontFamily: props.font,
...styles ...styles,
}} }}
> >
{props.text || '默认文本'} {props.text || '默认文本'}
</div> </div>
) );
}, },
props: { props: {
text: createEditorInputProp({ label: '显示文本' }), text: createEditorInputProp({ label: '显示文本' }),
@ -44,7 +44,7 @@ export default {
color: createEditorColorProp({ label: '字体颜色' }), color: createEditorColorProp({ label: '字体颜色' }),
size: createEditorInputNumberProp({ size: createEditorInputNumberProp({
label: '字体大小', label: '字体大小',
defaultValue: 16 defaultValue: 16,
}) }),
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -11,24 +11,24 @@ import {
createEditorInputProp, createEditorInputProp,
createEditorSelectProp, createEditorSelectProp,
createEditorSwitchProp, createEditorSwitchProp,
createEditorTableProp createEditorTableProp,
} from '@/visual-editor/visual-editor.props' } from '@/visual-editor/visual-editor.props';
// 对齐方式 // 对齐方式
const alignOptions = [ const alignOptions = [
{ {
label: '左对齐', label: '左对齐',
value: 'left' value: 'left',
}, },
{ {
label: '右对齐', label: '右对齐',
value: 'right' value: 'right',
}, },
{ {
label: '居中对齐', label: '居中对齐',
value: 'center' value: 'center',
} },
] ];
export const compProps = { export const compProps = {
'slots.default.children': createEditorTableProp({ 'slots.default.children': createEditorTableProp({
@ -37,37 +37,37 @@ export const compProps = {
options: [ options: [
{ label: '显示值', field: 'label' }, { label: '显示值', field: 'label' },
{ label: '绑定值', field: 'value' }, { label: '绑定值', field: 'value' },
{ label: '备注', field: 'comments' } { label: '备注', field: 'comments' },
], ],
showKey: 'label' showKey: 'label',
}, },
defaultValue: [] defaultValue: [],
}), }),
colon: createEditorSwitchProp({ label: '是否在 label 后面添加冒号' }), colon: createEditorSwitchProp({ label: '是否在 label 后面添加冒号' }),
disabled: createEditorSwitchProp({ label: '是否禁用表单中的所有输入框' }), disabled: createEditorSwitchProp({ label: '是否禁用表单中的所有输入框' }),
errorMessageAlign: createEditorSelectProp({ errorMessageAlign: createEditorSelectProp({
label: '错误提示文案对齐方式', label: '错误提示文案对齐方式',
defaultValue: 'left', defaultValue: 'left',
options: alignOptions options: alignOptions,
}), }),
inputAlign: createEditorSelectProp({ inputAlign: createEditorSelectProp({
label: '输入框对齐方式', label: '输入框对齐方式',
defaultValue: 'left', defaultValue: 'left',
options: alignOptions options: alignOptions,
}), }),
labelAlign: createEditorSelectProp({ labelAlign: createEditorSelectProp({
label: '表单项 label 对齐方式', label: '表单项 label 对齐方式',
defaultValue: 'left', defaultValue: 'left',
options: alignOptions options: alignOptions,
}), }),
labelWidth: createEditorInputProp({ label: '表单项 label 宽度默认单位为px' }), labelWidth: createEditorInputProp({ label: '表单项 label 宽度默认单位为px' }),
readonly: createEditorSwitchProp({ label: '是否将表单中的所有输入框设置为只读状态' }), readonly: createEditorSwitchProp({ label: '是否将表单中的所有输入框设置为只读状态' }),
scrollToError: createEditorSwitchProp({ scrollToError: createEditorSwitchProp({
label: '在提交表单且校验不通过时滚动至错误的表单项' label: '在提交表单且校验不通过时滚动至错误的表单项',
}), }),
showError: createEditorSwitchProp({ label: '是否在校验不通过时标红输入框' }), showError: createEditorSwitchProp({ label: '是否在校验不通过时标红输入框' }),
showErrorMessage: createEditorSwitchProp({ showErrorMessage: createEditorSwitchProp({
label: '是否在校验不通过时在输入框下方展示错误提示' label: '是否在校验不通过时在输入框下方展示错误提示',
}), }),
submitOnEnter: createEditorSwitchProp({ label: '是否在按下回车键时提交表单' }), submitOnEnter: createEditorSwitchProp({ label: '是否在按下回车键时提交表单' }),
validateFirst: createEditorSwitchProp({ label: '是否在某一项校验不通过时停止校验' }), validateFirst: createEditorSwitchProp({ label: '是否在某一项校验不通过时停止校验' }),
@ -76,17 +76,17 @@ export const compProps = {
options: [ options: [
{ {
label: 'onChange', label: 'onChange',
value: 'onChange' value: 'onChange',
}, },
{ {
label: 'onSubmit', label: 'onSubmit',
value: 'onSubmit' value: 'onSubmit',
}, },
{ {
label: 'onBlur', label: 'onBlur',
value: 'onBlur' value: 'onBlur',
} },
], ],
defaultValue: 'onBlur' defaultValue: 'onBlur',
}) }),
} };

View File

@ -6,11 +6,11 @@
* @Description: * @Description:
* @FilePath: \vite-vue3-lowcode\src\packages\container-component\form\index.tsx * @FilePath: \vite-vue3-lowcode\src\packages\container-component\form\index.tsx
*/ */
import { Form, Field, Button } from 'vant' import { renderSlot, useSlots } from 'vue';
import { renderSlot, useSlots } from 'vue' import { Form, Field, Button } from 'vant';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils' import { compProps } from './compProps';
import { useGlobalProperties } from '@/hooks/useGlobalProperties' import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { compProps } from './compProps' import { useGlobalProperties } from '@/hooks/useGlobalProperties';
export default { export default {
key: 'form', key: 'form',
@ -27,13 +27,13 @@ export default {
</div> </div>
</Form> </Form>
), ),
render: function ({ props, styles, block }) { render({ props, styles, block }) {
const slots = useSlots() const slots = useSlots();
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
const onSubmit = (values) => { const onSubmit = (values) => {
console.log('onSubmit:', values) console.log('onSubmit:', values);
} };
return () => ( return () => (
<div style={styles}> <div style={styles}>
@ -46,15 +46,15 @@ export default {
{renderSlot(slots, 'default')} {renderSlot(slots, 'default')}
</Form> </Form>
</div> </div>
) );
}, },
resize: { resize: {
height: true, height: true,
width: true width: true,
}, },
events: [ events: [
{ label: '提交表单且验证通过后触发', value: 'submit' }, { label: '提交表单且验证通过后触发', value: 'submit' },
{ label: '提交表单且验证不通过后触发', value: 'failed' } { label: '提交表单且验证不通过后触发', value: 'failed' },
], ],
props: compProps props: compProps,
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -1,11 +1,11 @@
const modules = import.meta.globEager('./*/index.tsx') const modules = import.meta.globEager('./*/index.tsx');
const components = {} const components = {};
Object.keys(modules).forEach((key: string) => { Object.keys(modules).forEach((key: string) => {
const name = key.replace(/\.\/(.*)\/index\.(tsx|vue)/, '$1') const name = key.replace(/\.\/(.*)\/index\.(tsx|vue)/, '$1');
components[name] = modules[key]?.default || modules[key] components[name] = modules[key]?.default || modules[key];
}) });
console.log(components, 'container-component') console.log(components, 'container-component');
export default components export default components;

View File

@ -1,17 +1,16 @@
import { Col, Row } from 'vant' import { renderSlot, useSlots, watchEffect } from 'vue';
import { renderSlot, useSlots } from 'vue' import { Col, Row } from 'vant';
import { createEditorInputProp, createEditorSelectProp } from '@/visual-editor/visual-editor.props' import styleModule from './index.module.scss';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils' import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import styleModule from './index.module.scss' import { createEditorInputProp, createEditorSelectProp } from '@/visual-editor/visual-editor.props';
import { useGlobalProperties } from '@/hooks/useGlobalProperties' import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { watchEffect } from 'vue'
interface SlotItem { interface SlotItem {
value: string value: string;
[prop: string]: any [prop: string]: any;
} }
const slotsTemp = {} as any const slotsTemp = {} as any;
const createSlots = (str: string): SlotItem => const createSlots = (str: string): SlotItem =>
str.split(':').reduce( str.split(':').reduce(
@ -19,12 +18,12 @@ const createSlots = (str: string): SlotItem =>
prev[`slot${index}`] = { prev[`slot${index}`] = {
key: `slot${index}`, key: `slot${index}`,
span: curr, span: curr,
children: [] children: [],
} };
return prev return prev;
}, },
{ value: str } { value: str },
) );
export default { export default {
key: 'layout', key: 'layout',
@ -38,20 +37,20 @@ export default {
</Row> </Row>
), ),
render: ({ props, styles, block, custom }) => { render: ({ props, styles, block, custom }) => {
const slots = useSlots() const slots = useSlots();
const { registerRef } = useGlobalProperties() const { registerRef } = useGlobalProperties();
slotsTemp[block._vid] ??= {} slotsTemp[block._vid] ??= {};
watchEffect(() => { watchEffect(() => {
if (Object.keys(props.slots || {}).length) { if (Object.keys(props.slots || {}).length) {
Object.keys(props.slots).forEach((key) => { Object.keys(props.slots).forEach((key) => {
if (slotsTemp[block._vid][key]?.children) { if (slotsTemp[block._vid][key]?.children) {
props.slots[key].children = slotsTemp[block._vid][key].children props.slots[key].children = slotsTemp[block._vid][key].children;
} }
}) });
} }
}) });
return () => ( return () => (
<div style={styles}> <div style={styles}>
@ -64,20 +63,20 @@ export default {
{Object.values(Object.keys(props.slots).length ? props.slots : createSlots('12:12')) {Object.values(Object.keys(props.slots).length ? props.slots : createSlots('12:12'))
?.filter((item) => typeof item !== 'string') ?.filter((item) => typeof item !== 'string')
.map((spanItem: SlotItem, spanIndex) => { .map((spanItem: SlotItem, spanIndex) => {
slotsTemp[block._vid][`slot${spanIndex}`] = spanItem slotsTemp[block._vid][`slot${spanIndex}`] = spanItem;
return ( return (
<> <>
<Col span={spanItem.span}>{renderSlot(slots, `slot${spanIndex}`)}</Col> <Col span={spanItem.span}>{renderSlot(slots, `slot${spanIndex}`)}</Col>
</> </>
) );
})} })}
</Row> </Row>
</div> </div>
) );
}, },
resize: { resize: {
height: true, height: true,
width: true width: true,
}, },
props: { props: {
gutter: createEditorInputProp({ label: '列间隔' }), gutter: createEditorInputProp({ label: '列间隔' }),
@ -90,9 +89,9 @@ export default {
{ label: '18:6', value: createSlots('18:6') }, { label: '18:6', value: createSlots('18:6') },
{ label: '8:8:8', value: createSlots('8:8:8') }, { label: '8:8:8', value: createSlots('8:8:8') },
{ label: '6:12:6', value: createSlots('6:12:6') }, { label: '6:12:6', value: createSlots('6:12:6') },
{ label: '6:6:6:6', value: createSlots('6:6:6:6') } { label: '6:6:6:6', value: createSlots('6:6:6:6') },
], ],
defaultValue: createSlots('12:12') defaultValue: createSlots('12:12'),
}), }),
justify: createEditorSelectProp({ justify: createEditorSelectProp({
label: '主轴对齐方式', label: '主轴对齐方式',
@ -101,16 +100,16 @@ export default {
{ label: '居中排列', value: 'center' }, { label: '居中排列', value: 'center' },
{ label: '均匀对齐', value: 'space-around' }, { label: '均匀对齐', value: 'space-around' },
{ label: '两端对齐', value: 'space-between' }, { label: '两端对齐', value: 'space-between' },
{ label: '右对齐', value: 'end' } { label: '右对齐', value: 'end' },
] ],
}), }),
align: createEditorSelectProp({ align: createEditorSelectProp({
label: '交叉轴对齐方式', label: '交叉轴对齐方式',
options: [ options: [
{ label: '顶部对齐', value: 'top' }, { label: '顶部对齐', value: 'top' },
{ label: '垂直居中', value: 'center' }, { label: '垂直居中', value: 'center' },
{ label: '底部对齐', value: 'bottom' } { label: '底部对齐', value: 'bottom' },
] ],
}) }),
} },
} as VisualEditorComponent } as VisualEditorComponent;

View File

@ -1,9 +1,8 @@
import type { App } from 'vue' import { Lazyload } from 'vant';
import '@vant/touch-emulator' import type { App } from 'vue';
import 'vant/lib/index.css' import '@vant/touch-emulator';
import 'vant/lib/index.css';
import { Lazyload } from 'vant'
export const setupVant = (app: App) => { export const setupVant = (app: App) => {
app.use(Lazyload) app.use(Lazyload);
} };

View File

@ -1,28 +1,28 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router' import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
import NProgress from 'nprogress' // progress bar import NProgress from 'nprogress'; // progress bar
import 'nprogress/css/nprogress.css' // 进度条样式 import 'nprogress/css/nprogress.css'; // 进度条样式
NProgress.configure({ showSpinner: false }) // NProgress Configuration NProgress.configure({ showSpinner: false }); // NProgress Configuration
const routes: Array<RouteRecordRaw> = [ const routes: Array<RouteRecordRaw> = [
{ {
path: '/:pathMatch(.*)*', path: '/:pathMatch(.*)*',
component: () => import('@/visual-editor/index.vue') component: () => import('@/visual-editor/index.vue'),
} },
] ];
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(), history: createWebHashHistory(),
routes routes,
}) });
router.beforeEach(() => { router.beforeEach(() => {
NProgress.start() // start progress bar NProgress.start(); // start progress bar
return true return true;
}) });
router.afterEach(() => { router.afterEach(() => {
NProgress.done() // finish progress bar NProgress.done(); // finish progress bar
}) });
export default router export default router;

View File

@ -1,5 +1,5 @@
import type { App } from 'vue';
import { createPinia } from 'pinia'; import { createPinia } from 'pinia';
import type { App } from 'vue';
const store = createPinia(); const store = createPinia();

View File

@ -8,10 +8,10 @@
*/ */
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { ElInput, ElIcon } from 'element-plus'; import { ElInput, ElIcon } from 'element-plus';
import type { PropType } from 'vue';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import styles from './index.module.scss';
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue'; import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
import styles from './index.module.scss';
import type { PropType } from 'vue';
export const FormatInputNumber = defineComponent({ export const FormatInputNumber = defineComponent({
props: { props: {

View File

@ -5,11 +5,11 @@
* @descriptionMonacoEditor * @descriptionMonacoEditor
* @update: 2021/4/30 0:01 * @update: 2021/4/30 0:01
*/ */
import { Monaco } from './monaco' import { defineComponent, onMounted, PropType, shallowRef, ref, onBeforeUnmount, watch } from 'vue';
import { defineComponent, onMounted, PropType, shallowRef, ref, onBeforeUnmount, watch } from 'vue' import { Monaco } from './monaco';
import styles from './MonacoEditor.module.scss' import styles from './MonacoEditor.module.scss';
let subscription: Monaco.IDisposable | undefined let subscription: Monaco.IDisposable | undefined;
let preventTriggerChangeEvent = false let preventTriggerChangeEvent = false;
export default defineComponent({ export default defineComponent({
name: 'MonacoEditor', name: 'MonacoEditor',
@ -17,45 +17,45 @@ export default defineComponent({
code: { code: {
// 代码 // 代码
type: String as PropType<string>, type: String as PropType<string>,
required: true required: true,
}, },
layout: { layout: {
// 布局 // 布局
type: Object as PropType<Monaco.editor.IDimension>, type: Object as PropType<Monaco.editor.IDimension>,
required: true, required: true,
default: () => ({}) default: () => ({}),
}, },
options: { options: {
type: Object as PropType<Monaco.editor.IStandaloneEditorConstructionOptions>, type: Object as PropType<Monaco.editor.IStandaloneEditorConstructionOptions>,
default: () => ({}) default: () => ({}),
}, },
vid: [String, Number], vid: [String, Number],
onChange: { onChange: {
type: Function as PropType< type: Function as PropType<
(value: string, event: Monaco.editor.IModelContentChangedEvent) => void (value: string, event: Monaco.editor.IModelContentChangedEvent) => void
> >,
}, },
title: { title: {
type: String as PropType<string>, type: String as PropType<string>,
default: '' default: '',
} },
}, },
setup(props) { setup(props) {
// 需要一个shallowRef: 只监听value不关心实际对象 // 需要一个shallowRef: 只监听value不关心实际对象
const editorRef = shallowRef<Monaco.editor.IStandaloneCodeEditor | null>(null) const editorRef = shallowRef<Monaco.editor.IStandaloneCodeEditor | null>(null);
// 需要生成编辑器的Dom // 需要生成编辑器的Dom
const containerDomRef = ref(null) const containerDomRef = ref(null);
// 格式化代码 // 格式化代码
const formatCode = () => { const formatCode = () => {
window.requestIdleCallback( window.requestIdleCallback(
() => { () => {
editorRef.value!.getAction('editor.action.formatDocument').run() editorRef.value!.getAction('editor.action.formatDocument').run();
}, },
{ timeout: 800 } { timeout: 800 },
) );
} };
onMounted(() => { onMounted(() => {
// 组件初始化时创建一个MonacoEditor的实例 // 组件初始化时创建一个MonacoEditor的实例
@ -66,44 +66,44 @@ export default defineComponent({
formatOnPaste: true, // 当粘贴的时候自动进行一次格式化代码 formatOnPaste: true, // 当粘贴的时候自动进行一次格式化代码
tabSize: 2, // tab缩进长度 tabSize: 2, // tab缩进长度
minimap: { minimap: {
enabled: false // 不需要小的缩略图 enabled: false, // 不需要小的缩略图
}, },
fontFamily: '微软雅黑', //字体 fontFamily: '微软雅黑', //字体
// automaticLayout: true, //编辑器自适应布局,可能会影响性能 // automaticLayout: true, //编辑器自适应布局,可能会影响性能
overviewRulerBorder: false, overviewRulerBorder: false,
scrollBeyondLastLine: false, //滚动配置,溢出才滚动 scrollBeyondLastLine: false, //滚动配置,溢出才滚动
...props.options ...props.options,
}) });
// 如果代码有变化会在这里监听到当受到外部数据改变时不需要触发change事件 // 如果代码有变化会在这里监听到当受到外部数据改变时不需要触发change事件
subscription = editorRef.value.onDidChangeModelContent((event) => { subscription = editorRef.value.onDidChangeModelContent((event) => {
if (!preventTriggerChangeEvent) { if (!preventTriggerChangeEvent) {
// getValue: 获取编辑器中的所有文本 // getValue: 获取编辑器中的所有文本
props.onChange?.(editorRef.value!.getValue(), event) props.onChange?.(editorRef.value!.getValue(), event);
} }
}) });
formatCode() formatCode();
editorRef.value.layout(props.layout) editorRef.value.layout(props.layout);
}) });
onBeforeUnmount(() => { onBeforeUnmount(() => {
// 组件销毁时卸载编辑器 // 组件销毁时卸载编辑器
if (subscription) { if (subscription) {
subscription.dispose() subscription.dispose();
} }
}) });
// 更新编辑器 // 更新编辑器
const refreshEditor = () => { const refreshEditor = () => {
if (editorRef.value) { if (editorRef.value) {
const editor = editorRef.value const editor = editorRef.value;
// 获取编辑器的textModel文本 // 获取编辑器的textModel文本
const model = editor.getModel() const model = editor.getModel();
// 如果代码发生变化 这里需要更新一版 // 如果代码发生变化 这里需要更新一版
if (model && props.code !== model.getValue()) { if (model && props.code !== model.getValue()) {
// 这是进行一次常规化的操作 文档原文Push an "undo stop" in the undo-redo stack. // 这是进行一次常规化的操作 文档原文Push an "undo stop" in the undo-redo stack.
editor.pushUndoStop() editor.pushUndoStop();
preventTriggerChangeEvent = true preventTriggerChangeEvent = true;
/** /**
* @function , Push edit operations, basically editing the model. This is the preferred way of editing the model. The edit operations will land on the undo stack. * @function , Push edit operations, basically editing the model. This is the preferred way of editing the model. The edit operations will land on the undo stack.
* @param * @param
@ -115,20 +115,20 @@ export default defineComponent({
[ [
{ {
range: model.getFullModelRange(), range: model.getFullModelRange(),
text: props.code text: props.code,
} },
], ],
() => null () => null,
) );
} }
editor.pushUndoStop() editor.pushUndoStop();
preventTriggerChangeEvent = false preventTriggerChangeEvent = false;
formatCode() formatCode();
}
} }
};
watch(() => props.vid, refreshEditor, { immediate: true }) watch(() => props.vid, refreshEditor, { immediate: true });
return () => { return () => {
return ( return (
@ -140,7 +140,7 @@ export default defineComponent({
)} )}
<div class={styles.code} ref={containerDomRef}></div> <div class={styles.code} ref={containerDomRef}></div>
</div> </div>
) );
} };
} },
}) });

View File

@ -1,24 +1,24 @@
// https://github.com/vitejs/vite/discussions/1791#discussioncomment-321046 // https://github.com/vitejs/vite/discussions/1791#discussioncomment-321046
import * as Monaco from 'monaco-editor' import * as Monaco from 'monaco-editor';
// import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker' // import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker' import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker' import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
// import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker' // import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
// import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker' // import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
const global: any = globalThis || window const global: any = globalThis || window;
global.MonacoEnvironment = { global.MonacoEnvironment = {
getWorker(_: string, label: string) { getWorker(_: string, label: string) {
if (label === 'json') return new jsonWorker() if (label === 'json') return new jsonWorker();
if (label === 'css' || label === 'scss' || label === 'less') return new cssWorker() if (label === 'css' || label === 'scss' || label === 'less') return new cssWorker();
// if (label === 'html' || label === 'handlebars' || label === 'razor') return new htmlWorker() // if (label === 'html' || label === 'handlebars' || label === 'razor') return new htmlWorker()
// if (label === 'typescript' || label === 'javascript') return new tsWorker() // if (label === 'typescript' || label === 'javascript') return new tsWorker()
// return new editorWorker() // return new editorWorker()
} },
} };
const languages = Monaco.languages.getLanguages() const languages = Monaco.languages.getLanguages();
export { Monaco, languages } export { Monaco, languages };

View File

@ -68,11 +68,11 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { VideoPlay } from '@element-plus/icons-vue';
import Preview from './preview.vue'; import Preview from './preview.vue';
import { useTools } from './useTools';
import { useVisualData, localKey } from '@/visual-editor/hooks/useVisualData'; import { useVisualData, localKey } from '@/visual-editor/hooks/useVisualData';
import { BASE_URL } from '@/visual-editor/utils'; import { BASE_URL } from '@/visual-editor/utils';
import { useTools } from './useTools';
import { VideoPlay } from '@element-plus/icons-vue';
const isShowH5Preview = ref(false); const isShowH5Preview = ref(false);

View File

@ -11,40 +11,40 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue' import { defineComponent, reactive, toRefs } from 'vue';
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core';
import { BASE_URL } from '@/visual-editor/utils' import { BASE_URL } from '@/visual-editor/utils';
/** /**
* @name: preview * @name: preview
* @author: 卜启缘 * @author: 卜启缘
* @date: 2021/4/29 23:09 * @date: 2021/4/29 23:09
* @descriptionpreview * @descriptionpreview
* @update: 2021/4/29 23:09 * @update: 2021/4/29 23:09
*/ */
export default defineComponent({ export default defineComponent({
name: 'Preview', name: 'Preview',
props: { props: {
visible: { visible: {
type: Boolean, type: Boolean,
default: false default: false,
} },
}, },
emits: ['update:visible'], emits: ['update:visible'],
setup(props, { emit }) { setup(props, { emit }) {
const state = reactive({ const state = reactive({
dialogVisible: useVModel(props, 'visible', emit), dialogVisible: useVModel(props, 'visible', emit),
previewUrl: `${BASE_URL}preview/${location.hash}` previewUrl: `${BASE_URL}preview/${location.hash}`,
}) });
return { return {
...toRefs(state) ...toRefs(state),
} };
} },
}) });
</script> </script>
<style lang="scss"> <style lang="scss">
.h5-preview { .h5-preview {
overflow: hidden; overflow: hidden;
.el-dialog__body { .el-dialog__body {
@ -64,5 +64,5 @@ export default defineComponent({
width: 0; width: 0;
} }
} }
} }
</style> </style>

View File

@ -7,11 +7,8 @@
*/ */
import { reactive } from 'vue'; import { reactive } from 'vue';
import { ElMessage, ElRadio, ElRadioGroup } from 'element-plus'; import { ElMessage, ElRadio, ElRadioGroup } from 'element-plus';
import { useQRCode } from '@vueuse/integrations'; import { useQRCode } from '@vueuse/integrations/useQRCode';
import { useClipboard } from '@vueuse/core'; import { useClipboard } from '@vueuse/core';
import { useVisualData, localKey } from '@/visual-editor/hooks/useVisualData';
import { useModal } from '@/visual-editor/hooks/useModal';
import MonacoEditor from '@/visual-editor/components/common/monaco-editor/MonacoEditor';
import { import {
DocumentCopy, DocumentCopy,
Cellphone, Cellphone,
@ -23,6 +20,9 @@ import {
Download, Download,
Upload, Upload,
} from '@element-plus/icons-vue'; } from '@element-plus/icons-vue';
import { useVisualData, localKey } from '@/visual-editor/hooks/useVisualData';
import { useModal } from '@/visual-editor/hooks/useModal';
import MonacoEditor from '@/visual-editor/components/common/monaco-editor/MonacoEditor';
import 'element-plus/es/components/message/style/css'; import 'element-plus/es/components/message/style/css';
export const useTools = () => { export const useTools = () => {
@ -95,7 +95,7 @@ export const useTools = () => {
title: '真机预览', title: '真机预览',
icon: Cellphone, icon: Cellphone,
onClick: () => { onClick: () => {
const qrcode = useQRCode(location.origin + '/preview'); const qrcode = useQRCode(`${location.origin}/preview`);
useModal({ useModal({
title: '预览二维码(暂不可用)', title: '预览二维码(暂不可用)',
props: { props: {

View File

@ -8,11 +8,11 @@
*/ */
import { defineComponent, ref } from 'vue'; import { defineComponent, ref } from 'vue';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { visualConfig } from '@/visual.config'; import { Edit } from '@element-plus/icons-vue';
import styles from './index.module.scss'; import styles from './index.module.scss';
import { visualConfig } from '@/visual.config';
import { createNewBlock } from '@/visual-editor/visual-editor.utils'; import { createNewBlock } from '@/visual-editor/visual-editor.utils';
import DraggableTransitionGroup from '@/visual-editor/components/simulator-editor/draggable-transition-group.vue'; import DraggableTransitionGroup from '@/visual-editor/components/simulator-editor/draggable-transition-group.vue';
import { Edit } from '@element-plus/icons-vue';
export default defineComponent({ export default defineComponent({
name: 'BaseWidgets', name: 'BaseWidgets',

View File

@ -8,12 +8,12 @@
*/ */
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { visualConfig } from '@/visual.config';
import Draggable from 'vuedraggable'; import Draggable from 'vuedraggable';
import styles from './index.module.scss';
import { createNewBlock } from '@/visual-editor/visual-editor.utils';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { Suitcase } from '@element-plus/icons-vue'; import { Suitcase } from '@element-plus/icons-vue';
import styles from './index.module.scss';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { visualConfig } from '@/visual.config';
import { createNewBlock } from '@/visual-editor/visual-editor.utils';
export default defineComponent({ export default defineComponent({
name: 'ContainerComponent', name: 'ContainerComponent',

View File

@ -23,9 +23,9 @@
<div class="model-item-title"> <div class="model-item-title">
<span class="truncate w-160px">{{ item.name }}</span> <span class="truncate w-160px">{{ item.name }}</span>
<div class="model-actions"> <div class="model-actions">
<el-icon size="24" color="#2196f3" @click.stop="editApiItem(item)"> <ElIcon :size="24" color="#2196f3" @click.stop="editApiItem(item)">
<Edit /> <Edit />
</el-icon> </ElIcon>
<el-popconfirm <el-popconfirm
confirm-button-text="确定" confirm-button-text="确定"
cancel-button-text="取消" cancel-button-text="取消"
@ -34,7 +34,7 @@
@confirm="deleteFetchApi(item.key)" @confirm="deleteFetchApi(item.key)"
> >
<template #reference> <template #reference>
<el-icon size="24" color="#f44336"><Delete /></el-icon> <ElIcon :size="24" color="#f44336"><Delete /></ElIcon>
</template> </template>
</el-popconfirm> </el-popconfirm>
</div> </div>
@ -59,15 +59,16 @@
ElButton, ElButton,
ElMessage, ElMessage,
ElCascader, ElCascader,
ElIcon,
} from 'element-plus'; } from 'element-plus';
import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import type { FetchApiItem, VisualEditorModel } from '@/visual-editor/visual-editor.utils';
import { useModal } from '@/visual-editor/hooks/useModal';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { Delete, Edit } from '@element-plus/icons-vue';
import { useImportSwaggerJsonModal } from './utils';
import type { FetchApiItem, VisualEditorModel } from '@/visual-editor/visual-editor.utils';
import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import { useModal } from '@/visual-editor/hooks/useModal';
import { generateNanoid } from '@/visual-editor/utils/'; import { generateNanoid } from '@/visual-editor/utils/';
import { RequestEnum, ContentTypeEnum } from '@/enums/httpEnum'; import { RequestEnum, ContentTypeEnum } from '@/enums/httpEnum';
import { useImportSwaggerJsonModal } from './utils';
import { Delete, Edit } from '@element-plus/icons-vue';
interface IState { interface IState {
activeNames: string[]; activeNames: string[];
@ -246,6 +247,7 @@
.model-actions { .model-actions {
display: flex; display: flex;
i { i {
padding: 6px; padding: 6px;
margin: 0 2px; margin: 0 2px;

View File

@ -23,7 +23,7 @@
<div class="model-item-title"> <div class="model-item-title">
<span class="truncate w-160px">{{ item.name }}</span> <span class="truncate w-160px">{{ item.name }}</span>
<div class="model-actions"> <div class="model-actions">
<el-icon size="24" color="#2196f3" @click.stop="editModel(item)"> <el-icon :size="24" color="#2196f3" @click.stop="editModel(item)">
<Edit /> <Edit />
</el-icon> </el-icon>
<el-popconfirm <el-popconfirm
@ -32,7 +32,7 @@
@confirm="deleteModel(item.key)" @confirm="deleteModel(item.key)"
> >
<template #reference> <template #reference>
<el-icon size="24" color="#f44336"><Delete /></el-icon> <el-icon :size="24" color="#f44336"><Delete /></el-icon>
</template> </template>
</el-popconfirm> </el-popconfirm>
</div> </div>
@ -59,14 +59,15 @@
ElCard, ElCard,
ElButton, ElButton,
ElMessage, ElMessage,
ElIcon,
} from 'element-plus'; } from 'element-plus';
import { useVisualData, fieldTypes } from '@/visual-editor/hooks/useVisualData';
import type { VisualEditorModel } from '@/visual-editor/visual-editor.utils';
import { useModal } from '@/visual-editor/hooks/useModal';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { generateNanoid } from '@/visual-editor/utils/';
import { useImportSwaggerJsonModal } from './utils';
import { Delete, Edit } from '@element-plus/icons-vue'; import { Delete, Edit } from '@element-plus/icons-vue';
import { useImportSwaggerJsonModal } from './utils';
import type { VisualEditorModel } from '@/visual-editor/visual-editor.utils';
import { useVisualData, fieldTypes } from '@/visual-editor/hooks/useVisualData';
import { useModal } from '@/visual-editor/hooks/useModal';
import { generateNanoid } from '@/visual-editor/utils/';
interface IState { interface IState {
activeNames: string[]; activeNames: string[];
@ -261,6 +262,7 @@
.model-actions { .model-actions {
display: flex; display: flex;
i { i {
padding: 6px; padding: 6px;
margin: 0 2px; margin: 0 2px;

View File

@ -1,10 +1,10 @@
<!-- <!--
* @Author: 卜启缘 * @Author: 卜启缘
* @Date: 2021-06-24 18:36:03 * @Date: 2021-06-24 18:36:03
* @LastEditTime: 2021-07-07 14:12:15 * @LastEditTime: 2022-07-02 23:13:00
* @LastEditors: 卜启缘 * @LastEditors: 卜启缘
* @Description: 数据源管理 * @Description: 数据源管理
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\left-aside\components\data-source\index.vue * @FilePath: /vite-vue3-lowcode/src/visual-editor/components/left-aside/components/data-source/index.vue
--> -->
<template> <template>
<el-tabs type="border-card" stretch class="data-source"> <el-tabs type="border-card" stretch class="data-source">
@ -17,18 +17,16 @@
</el-tabs> </el-tabs>
</template> </template>
<script lang="tsx"> <script setup lang="tsx" name="基本组件">
import { DataBoard } from '@element-plus/icons-vue'; import { DataBoard } from '@element-plus/icons-vue';
import DataModel from './data-model.vue';
import DataFetch from './data-fetch.vue';
export default { defineOptions({
label: '数据源', label: '数据源',
order: 2, order: 2,
icon: DataBoard, icon: DataBoard,
}; });
</script>
<script setup lang="tsx" name="基本组件">
import DataModel from './data-model.vue';
import DataFetch from './data-fetch.vue';
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -1,19 +1,18 @@
/* /*
* @Author: * @Author:
* @Date: 2021-06-27 13:15:19 * @Date: 2021-06-27 13:15:19
* @LastEditTime: 2021-06-27 15:22:51 * @LastEditTime: 2022-07-02 23:12:37
* @LastEditors: * @LastEditors:
* @Description: * @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\left-aside\components\data-source\utils.tsx * @FilePath: /vite-vue3-lowcode/src/visual-editor/components/left-aside/components/data-source/utils.tsx
*/ */
import { ElMessage } from 'element-plus';
import type { FetchApiItem, VisualEditorModel } from '@/visual-editor/visual-editor.utils';
import { generateNanoid } from '@/visual-editor/utils'; import { generateNanoid } from '@/visual-editor/utils';
import type { FetchApiItem } from '@/visual-editor/visual-editor.utils';
import { RequestEnum } from '@/enums/httpEnum'; import { RequestEnum } from '@/enums/httpEnum';
import MonacoEditor from '@/visual-editor/components/common/monaco-editor/MonacoEditor'; import MonacoEditor from '@/visual-editor/components/common/monaco-editor/MonacoEditor';
import { useVisualData } from '@/visual-editor/hooks/useVisualData'; import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import type { VisualEditorModel } from '@/visual-editor/visual-editor.utils';
import { useModal } from '@/visual-editor/hooks/useModal'; import { useModal } from '@/visual-editor/hooks/useModal';
import { ElMessage } from 'element-plus';
/** /**
* @description , eg: 简单的解析代码 * @description , eg: 简单的解析代码
@ -51,7 +50,7 @@ export const importSwaggerJson = (swagger: any) => {
key: generateNanoid(), key: generateNanoid(),
name: apiUrlObj.summary, name: apiUrlObj.summary,
options: { options: {
url: url, // 请求的url url, // 请求的url
method: method.toLocaleUpperCase() as RequestEnum, // 请求的方法 method: method.toLocaleUpperCase() as RequestEnum, // 请求的方法
contentType: apiUrlObj.produces[0] || apiUrlObj.consumes[0], // 请求的内容类型 contentType: apiUrlObj.produces[0] || apiUrlObj.consumes[0], // 请求的内容类型
}, },

View File

@ -1,13 +1,13 @@
const modules = import.meta.globEager('./*/index.(tsx|vue)') const modules = import.meta.globEager('./*/index.(tsx|vue)');
const components = {} const components = {};
console.log(modules, '起航') console.log(modules, '起航');
for (const path in modules) { for (const path in modules) {
const comp = modules[path].default const comp = modules[path].default;
components[comp.name || path.split('/')[1]] = comp components[comp.name || path.split('/')[1]] = comp;
} }
console.log('left-aside components:', components) console.log('left-aside components:', components);
export default components export default components;

View File

@ -40,23 +40,21 @@
</el-tree> </el-tree>
</template> </template>
<script lang="tsx"> <script lang="tsx" setup>
export default { import { ref, computed } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { ElMessage, ElForm, ElFormItem, ElInput } from 'element-plus';
import { Tickets, Plus, MoreFilled, Edit, Delete, Link } from '@element-plus/icons-vue';
import type { VisualEditorPage } from '@/visual-editor/visual-editor.utils';
import { useModal } from '@/visual-editor/hooks/useModal';
import { useVisualData, createNewPage } from '@/visual-editor/hooks/useVisualData';
defineOptions({
name: 'PageTree', name: 'PageTree',
label: '页面', label: '页面',
order: 1, order: 1,
icon: Tickets, icon: Tickets,
}; });
</script>
<script lang="tsx" setup>
import { ref, computed } from 'vue';
import { useVisualData, createNewPage } from '@/visual-editor/hooks/useVisualData';
import { useRouter, useRoute } from 'vue-router';
import { ElMessage, ElForm, ElFormItem, ElInput } from 'element-plus';
import { useModal } from '@/visual-editor/hooks/useModal';
import type { VisualEditorPage } from '@/visual-editor/visual-editor.utils';
import { Tickets, Plus, MoreFilled, Edit, Delete, Link } from '@element-plus/icons-vue';
const rules = { const rules = {
title: [{ required: true, message: '请输入页面标题', trigger: 'blur' }], title: [{ required: true, message: '请输入页面标题', trigger: 'blur' }],

View File

@ -1,10 +1,10 @@
<!-- <!--
* @Author: 卜启缘 * @Author: 卜启缘
* @Date: 2021-06-24 00:35:17 * @Date: 2021-06-24 00:35:17
* @LastEditTime: 2021-07-07 14:02:29 * @LastEditTime: 2022-07-02 18:26:09
* @LastEditors: 卜启缘 * @LastEditors: 卜启缘
* @Description: 左侧边栏 * @Description: 左侧边栏
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\left-aside\index.vue * @FilePath: /vite-vue3-lowcode/src/visual-editor/components/left-aside/index.vue
--> -->
<template> <template>
<el-tabs v-model="activeName" tab-position="left" class="left-aside"> <el-tabs v-model="activeName" tab-position="left" class="left-aside">
@ -22,12 +22,6 @@
</el-tabs> </el-tabs>
</template> </template>
<script lang="ts">
export default {
name: 'LeftAside',
};
</script>
<script lang="ts" setup> <script lang="ts" setup>
/** /**
* @description 左侧边栏 * @description 左侧边栏
@ -35,6 +29,10 @@
import { ref } from 'vue'; import { ref } from 'vue';
import components from './components'; import components from './components';
defineOptions({
name: 'LeftAside',
});
const tabs = Object.keys(components) const tabs = Object.keys(components)
.map((name) => { .map((name) => {
const { label, icon, order } = components[name]; const { label, icon, order } = components[name];

View File

@ -8,13 +8,13 @@
*/ */
import { defineComponent, reactive, ref, watchEffect } from 'vue'; import { defineComponent, reactive, ref, watchEffect } from 'vue';
import { ElTabs, ElTabPane, ElRow, ElCol, ElButton, ElSwitch, ElAlert, ElIcon } from 'element-plus'; import { ElTabs, ElTabPane, ElRow, ElCol, ElButton, ElSwitch, ElAlert, ElIcon } from 'element-plus';
import { onClickOutside } from '@vueuse/core';
import { Plus, CaretRight } from '@element-plus/icons-vue';
import { animationTabs } from './animateConfig'; import { animationTabs } from './animateConfig';
import styles from './animate.module.scss'; import styles from './animate.module.scss';
import { onClickOutside } from '@vueuse/core';
import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import type { Animation } from '@/visual-editor/visual-editor.utils'; import type { Animation } from '@/visual-editor/visual-editor.utils';
import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import { useAnimate } from '@/hooks/useAnimate'; import { useAnimate } from '@/hooks/useAnimate';
import { Plus, CaretRight } from '@element-plus/icons-vue';
import 'element-plus/es/components/alert/style/css'; import 'element-plus/es/components/alert/style/css';
export const Animate = defineComponent({ export const Animate = defineComponent({

View File

@ -1,30 +1,30 @@
import type { Animation } from '@/visual-editor/visual-editor.utils' import type { Animation } from '@/visual-editor/visual-editor.utils';
export interface animationBoxTs { export interface animationBoxTs {
label: string label: string;
value: Animation[] value: Animation[];
} }
// 动画类型 // 动画类型
export interface animationTabsTs { export interface animationTabsTs {
in: animationBoxTs in: animationBoxTs;
out: animationBoxTs out: animationBoxTs;
other: animationBoxTs other: animationBoxTs;
} }
export const animationTabs: animationTabsTs = { export const animationTabs: animationTabsTs = {
in: { in: {
label: '进入', label: '进入',
value: [] value: [],
}, },
out: { out: {
label: '退出', label: '退出',
value: [] value: [],
}, },
other: { other: {
label: '其他', label: '其他',
value: [] value: [],
} },
} };
const opt = [ const opt = [
{ {
@ -33,41 +33,41 @@ const opt = [
children: [ children: [
{ {
label: '弹跳', label: '弹跳',
value: 'bounce' value: 'bounce',
}, },
{ {
label: '闪烁', label: '闪烁',
value: 'flash' value: 'flash',
}, },
{ {
label: '跳动', label: '跳动',
value: 'pulse' value: 'pulse',
}, },
{ {
label: '抖动', label: '抖动',
value: 'headShake' value: 'headShake',
}, },
{ {
label: '摇摆', label: '摇摆',
value: 'swing' value: 'swing',
}, },
{ {
label: '橡皮圈', label: '橡皮圈',
value: 'rubberBand' value: 'rubberBand',
}, },
{ {
label: '果冻', label: '果冻',
value: 'jello' value: 'jello',
}, },
{ {
label: '晃动', label: '晃动',
value: 'tada' value: 'tada',
}, },
{ {
label: '抖动', label: '抖动',
value: 'wobble' value: 'wobble',
} },
] ],
}, },
{ {
@ -76,25 +76,25 @@ const opt = [
children: [ children: [
{ {
label: '弹跳进入', label: '弹跳进入',
value: 'bounceIn' value: 'bounceIn',
}, },
{ {
label: '向下弹跳进入', label: '向下弹跳进入',
value: 'bounceInDown' value: 'bounceInDown',
}, },
{ {
label: '向右弹跳进入', label: '向右弹跳进入',
value: 'bounceInLeft' value: 'bounceInLeft',
}, },
{ {
label: '向左弹跳进入', label: '向左弹跳进入',
value: 'bounceInRight' value: 'bounceInRight',
}, },
{ {
label: '向上弹跳进入', label: '向上弹跳进入',
value: 'bounceInUp' value: 'bounceInUp',
} },
] ],
}, },
{ {
@ -103,25 +103,25 @@ const opt = [
children: [ children: [
{ {
label: '弹跳退出', label: '弹跳退出',
value: 'bounceOut' value: 'bounceOut',
}, },
{ {
label: '向下弹跳退出', label: '向下弹跳退出',
value: 'bounceOutDown' value: 'bounceOutDown',
}, },
{ {
label: '向左弹跳退出', label: '向左弹跳退出',
value: 'bounceOutLeft' value: 'bounceOutLeft',
}, },
{ {
label: '向右弹跳退出', label: '向右弹跳退出',
value: 'bounceOutRight' value: 'bounceOutRight',
}, },
{ {
label: '向上弹跳退出', label: '向上弹跳退出',
value: 'bounceOutUp' value: 'bounceOutUp',
} },
] ],
}, },
{ {
@ -130,41 +130,41 @@ const opt = [
children: [ children: [
{ {
label: '渐显进入', label: '渐显进入',
value: 'fadeIn' value: 'fadeIn',
}, },
{ {
label: '向下渐显进入', label: '向下渐显进入',
value: 'fadeInDown' value: 'fadeInDown',
}, },
{ {
label: '由屏幕外向下渐显进入', label: '由屏幕外向下渐显进入',
value: 'fadeInDownBig' value: 'fadeInDownBig',
}, },
{ {
label: '向右显进入', label: '向右显进入',
value: 'fadeInLeft' value: 'fadeInLeft',
}, },
{ {
label: '由屏幕外向右渐显进入', label: '由屏幕外向右渐显进入',
value: 'fadeInLeftBig' value: 'fadeInLeftBig',
}, },
{ {
label: '向左渐显进入', label: '向左渐显进入',
value: 'fadeInRight' value: 'fadeInRight',
}, },
{ {
label: '由屏幕外向左渐显进入', label: '由屏幕外向左渐显进入',
value: 'fadeInRightBig' value: 'fadeInRightBig',
}, },
{ {
label: '向上渐显进入', label: '向上渐显进入',
value: 'fadeInUp' value: 'fadeInUp',
}, },
{ {
label: '由屏幕外向上渐显进入', label: '由屏幕外向上渐显进入',
value: 'fadeInUpBig' value: 'fadeInUpBig',
} },
] ],
}, },
{ {
@ -173,41 +173,41 @@ const opt = [
children: [ children: [
{ {
label: '渐隐退出', label: '渐隐退出',
value: 'fadeOut' value: 'fadeOut',
}, },
{ {
label: '向下渐隐退出', label: '向下渐隐退出',
value: 'fadeOutDown' value: 'fadeOutDown',
}, },
{ {
label: '向下渐隐退出屏幕外', label: '向下渐隐退出屏幕外',
value: 'fadeOutDownBig' value: 'fadeOutDownBig',
}, },
{ {
label: '向左渐隐退出', label: '向左渐隐退出',
value: 'fadeOutLeft' value: 'fadeOutLeft',
}, },
{ {
label: '向左渐隐退出屏幕外', label: '向左渐隐退出屏幕外',
value: 'fadeOutLeftBig' value: 'fadeOutLeftBig',
}, },
{ {
label: '向右渐隐退出', label: '向右渐隐退出',
value: 'fadeOutRight' value: 'fadeOutRight',
}, },
{ {
label: '向右渐隐退出屏幕外', label: '向右渐隐退出屏幕外',
value: 'fadeOutRightBig' value: 'fadeOutRightBig',
}, },
{ {
label: '向上渐隐退出', label: '向上渐隐退出',
value: 'fadeOutUp' value: 'fadeOutUp',
}, },
{ {
label: '向上渐隐退出屏幕外', label: '向上渐隐退出屏幕外',
value: 'fadeOutUpBig' value: 'fadeOutUpBig',
} },
] ],
}, },
{ {
@ -216,25 +216,25 @@ const opt = [
children: [ children: [
{ {
label: '翻动', label: '翻动',
value: 'flip' value: 'flip',
}, },
{ {
label: '纵向翻动', label: '纵向翻动',
value: 'flipInX' value: 'flipInX',
}, },
{ {
label: '横向翻动', label: '横向翻动',
value: 'flipInY' value: 'flipInY',
}, },
{ {
label: '立体纵向翻动', label: '立体纵向翻动',
value: 'flipOutX' value: 'flipOutX',
}, },
{ {
label: '立体横向翻动', label: '立体横向翻动',
value: 'flipOutY' value: 'flipOutY',
} },
] ],
}, },
{ {
@ -243,21 +243,21 @@ const opt = [
children: [ children: [
{ {
label: '向左加速进入', label: '向左加速进入',
value: 'lightSpeedInRight' value: 'lightSpeedInRight',
}, },
{ {
label: '向右加速进入', label: '向右加速进入',
value: 'lightSpeedInLeft' value: 'lightSpeedInLeft',
}, },
{ {
label: '向右加速退出', label: '向右加速退出',
value: 'lightSpeedOutRight' value: 'lightSpeedOutRight',
}, },
{ {
label: '向左加速退出', label: '向左加速退出',
value: 'lightSpeedOutLeft' value: 'lightSpeedOutLeft',
} },
] ],
}, },
{ {
@ -266,25 +266,25 @@ const opt = [
children: [ children: [
{ {
label: '旋转渐显', label: '旋转渐显',
value: 'rotateIn' value: 'rotateIn',
}, },
{ {
label: '左下角旋转渐显', label: '左下角旋转渐显',
value: 'rotateInDownLeft' value: 'rotateInDownLeft',
}, },
{ {
label: '右下角旋转渐显', label: '右下角旋转渐显',
value: 'rotateInDownRight' value: 'rotateInDownRight',
}, },
{ {
label: '左上角旋转渐显', label: '左上角旋转渐显',
value: 'rotateInUpLeft' value: 'rotateInUpLeft',
}, },
{ {
label: '右上角旋转渐显', label: '右上角旋转渐显',
value: 'rotateInUpRight' value: 'rotateInUpRight',
} },
] ],
}, },
{ {
@ -293,25 +293,25 @@ const opt = [
children: [ children: [
{ {
label: '旋转渐隐', label: '旋转渐隐',
value: 'rotateOut' value: 'rotateOut',
}, },
{ {
label: '左下角旋转渐隐', label: '左下角旋转渐隐',
value: 'rotateOutDownLeft' value: 'rotateOutDownLeft',
}, },
{ {
label: '左下角旋转渐隐', label: '左下角旋转渐隐',
value: 'rotateOutDownRight' value: 'rotateOutDownRight',
}, },
{ {
label: '左上角旋转渐隐', label: '左上角旋转渐隐',
value: 'rotateOutUpLeft' value: 'rotateOutUpLeft',
}, },
{ {
label: '右上角旋转渐隐', label: '右上角旋转渐隐',
value: 'rotateOutUpRight' value: 'rotateOutUpRight',
} },
] ],
}, },
{ {
@ -320,21 +320,21 @@ const opt = [
children: [ children: [
{ {
label: '向上平移进入', label: '向上平移进入',
value: 'slideInUp' value: 'slideInUp',
}, },
{ {
label: '向下平移进入', label: '向下平移进入',
value: 'slideInDown' value: 'slideInDown',
}, },
{ {
label: '向右平移进入', label: '向右平移进入',
value: 'slideInLeft' value: 'slideInLeft',
}, },
{ {
label: '向左平移进入', label: '向左平移进入',
value: 'slideInRight' value: 'slideInRight',
} },
] ],
}, },
{ {
label: '平移退出', label: '平移退出',
@ -342,21 +342,21 @@ const opt = [
children: [ children: [
{ {
label: '向上平移退出', label: '向上平移退出',
value: 'slideOutUp' value: 'slideOutUp',
}, },
{ {
label: '向下平移退出', label: '向下平移退出',
value: 'slideOutDown' value: 'slideOutDown',
}, },
{ {
label: '向左平移退出', label: '向左平移退出',
value: 'slideOutLeft' value: 'slideOutLeft',
}, },
{ {
label: '向右平移退出', label: '向右平移退出',
value: 'slideOutRight' value: 'slideOutRight',
} },
] ],
}, },
{ {
@ -365,25 +365,25 @@ const opt = [
children: [ children: [
{ {
label: '放大进入', label: '放大进入',
value: 'zoomIn' value: 'zoomIn',
}, },
{ {
label: '向下放大进入', label: '向下放大进入',
value: 'zoomInDown' value: 'zoomInDown',
}, },
{ {
label: '向右放大进入', label: '向右放大进入',
value: 'zoomInLeft' value: 'zoomInLeft',
}, },
{ {
label: '向左放大进入', label: '向左放大进入',
value: 'zoomInRight' value: 'zoomInRight',
}, },
{ {
label: '向上放大进入', label: '向上放大进入',
value: 'zoomInUp' value: 'zoomInUp',
} },
] ],
}, },
{ {
@ -392,25 +392,25 @@ const opt = [
children: [ children: [
{ {
label: '缩小退出', label: '缩小退出',
value: 'zoomOut' value: 'zoomOut',
}, },
{ {
label: '向下缩小退出', label: '向下缩小退出',
value: 'zoomOutDown' value: 'zoomOutDown',
}, },
{ {
label: '向左缩小退出', label: '向左缩小退出',
value: 'zoomOutLeft' value: 'zoomOutLeft',
}, },
{ {
label: '向右缩小退出', label: '向右缩小退出',
value: 'zoomOutRight' value: 'zoomOutRight',
}, },
{ {
label: '向上缩小退出', label: '向上缩小退出',
value: 'zoomOutUp' value: 'zoomOutUp',
} },
] ],
}, },
{ {
@ -419,51 +419,51 @@ const opt = [
children: [ children: [
{ {
label: '悬挂', label: '悬挂',
value: 'hinge' value: 'hinge',
}, },
{ {
label: '滚动进入', label: '滚动进入',
value: 'rollIn' value: 'rollIn',
}, },
{ {
label: '滚动退出', label: '滚动退出',
value: 'rollOut' value: 'rollOut',
} },
] ],
} },
] ];
/** /**
* @return {Object} { animationValue: animatonLabel } * @return {Object} { animationValue: animatonLabel }
*/ */
const inReg = /进|渐显/ const inReg = /进|渐显/;
const outReg = /退|渐隐/ const outReg = /退|渐隐/;
const defaultOption = { const defaultOption = {
delay: 0, delay: 0,
count: 1, count: 1,
duration: 1, duration: 1,
infinite: false infinite: false,
} };
for (let index = 0; index < opt.length; index++) { for (let index = 0; index < opt.length; index++) {
const items = opt[index].children const items = opt[index].children;
items.forEach((item) => { items.forEach((item) => {
if (inReg.test(item.label)) { if (inReg.test(item.label)) {
animationTabs.in.value.push({ animationTabs.in.value.push({
...item, ...item,
...defaultOption ...defaultOption,
}) });
} else if (outReg.test(item.label)) { } else if (outReg.test(item.label)) {
animationTabs.out.value.push({ animationTabs.out.value.push({
...item, ...item,
...defaultOption ...defaultOption,
}) });
} else { } else {
animationTabs.other.value.push({ animationTabs.other.value.push({
...item, ...item,
...defaultOption ...defaultOption,
}) });
} }
}) });
} }
export default animationTabs export default animationTabs;

View File

@ -21,12 +21,12 @@ import {
ElIcon, ElIcon,
} from 'element-plus'; } from 'element-plus';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { isObject } from '@/visual-editor/utils/is';
import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import { PropConfig } from '../prop-config';
import { VisualEditorBlockData, VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { Rank, CirclePlus, Remove } from '@element-plus/icons-vue'; import { Rank, CirclePlus, Remove } from '@element-plus/icons-vue';
import { PropConfig } from '../prop-config';
import { isObject } from '@/visual-editor/utils/is';
import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import { VisualEditorBlockData, VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
interface OptionItem extends LabelValue { interface OptionItem extends LabelValue {
component?: VisualEditorComponent; component?: VisualEditorComponent;

View File

@ -7,5 +7,5 @@
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\attr-editor\components\index.ts * @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\attr-editor\components\index.ts
*/ */
export { CrossSortableOptionsEditor } from './cross-sortable-options-editor/cross-sortable-options-editor' export { CrossSortableOptionsEditor } from './cross-sortable-options-editor/cross-sortable-options-editor';
export { TablePropEditor } from './table-prop-editor/table-prop-editor' export { TablePropEditor } from './table-prop-editor/table-prop-editor';

View File

@ -1,10 +1,10 @@
/* /*
* @Author: * @Author:
* @Date: 2021-07-11 17:53:54 * @Date: 2021-07-11 17:53:54
* @LastEditTime: 2021-07-11 18:36:17 * @LastEditTime: 2022-07-02 22:58:55
* @LastEditors: * @LastEditors:
* @Description: * @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\attr-editor\components\prop-config\index.tsx * @FilePath: /vite-vue3-lowcode/src/visual-editor/components/right-attribute-panel/components/attr-editor/components/prop-config/index.tsx
*/ */
import { computed, defineComponent, PropType } from 'vue'; import { computed, defineComponent, PropType } from 'vue';
@ -19,14 +19,15 @@ import {
ElFormItem, ElFormItem,
ElTooltip, ElTooltip,
ElIcon, ElIcon,
ExpandTrigger,
} from 'element-plus'; } from 'element-plus';
import { cloneDeep } from 'lodash-es';
import { Warning } from '@element-plus/icons-vue';
import { TablePropEditor, CrossSortableOptionsEditor } from '../../components';
import { useDotProp } from '@/visual-editor/hooks/useDotProp'; import { useDotProp } from '@/visual-editor/hooks/useDotProp';
import { VisualEditorProps, VisualEditorPropsType } from '@/visual-editor/visual-editor.props'; import { VisualEditorProps, VisualEditorPropsType } from '@/visual-editor/visual-editor.props';
import { TablePropEditor, CrossSortableOptionsEditor } from '../../components';
import { cloneDeep } from 'lodash-es';
import { useVisualData } from '@/visual-editor/hooks/useVisualData'; import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import { VisualEditorBlockData, VisualEditorComponent } from '@/visual-editor/visual-editor.utils'; import { VisualEditorBlockData, VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { Warning } from '@element-plus/icons-vue';
export const PropConfig = defineComponent({ export const PropConfig = defineComponent({
props: { props: {
@ -92,11 +93,11 @@ export const PropConfig = defineComponent({
children: 'entitys', children: 'entitys',
label: 'name', label: 'name',
value: 'key', value: 'key',
expandTrigger: 'hover', expandTrigger: ExpandTrigger.HOVER,
}} }}
placeholder="请选择绑定的请求数据" placeholder="请选择绑定的请求数据"
v-model={propObj[prop]} v-model={propObj[prop]}
options={models.value} options={[...models.value]}
></ElCascader> ></ElCascader>
), ),
}[propConfig.type](); }[propConfig.type]();
@ -126,9 +127,11 @@ export const PropConfig = defineComponent({
popper-class="max-w-200px" popper-class="max-w-200px"
content={propConfig.tips} content={propConfig.tips}
> >
<div>
<ElIcon> <ElIcon>
<Warning /> <Warning />
</ElIcon> </ElIcon>
</div>
</ElTooltip> </ElTooltip>
)} )}
{propConfig.label} {propConfig.label}

View File

@ -1,8 +1,8 @@
import { VisualEditorProps } from '@/visual-editor/visual-editor.props';
import { defineComponent, getCurrentInstance, onMounted, PropType, reactive, createApp } from 'vue'; import { defineComponent, getCurrentInstance, onMounted, PropType, reactive, createApp } from 'vue';
import { defer } from '@/visual-editor/utils/defer';
import { ElButton, ElDialog, ElTable, ElTableColumn, ElInput } from 'element-plus'; import { ElButton, ElDialog, ElTable, ElTableColumn, ElInput } from 'element-plus';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { defer } from '@/visual-editor/utils/defer';
import { VisualEditorProps } from '@/visual-editor/visual-editor.props';
export interface TablePropEditorServiceOption { export interface TablePropEditorServiceOption {
data: any[]; data: any[];
@ -66,7 +66,7 @@ const ServiceComponent = defineComponent({
return () => ( return () => (
<> <>
<ElDialog v-model={state.showFlag}> <ElDialog modelValue={state.showFlag}>
{{ {{
default: () => ( default: () => (
<div> <div>

View File

@ -1,25 +1,25 @@
import { defineComponent, PropType, SetupContext } from 'vue' import { defineComponent, PropType, SetupContext } from 'vue';
import { VisualEditorProps } from '@/visual-editor/visual-editor.props' import { ElButton, ElTag } from 'element-plus';
import { ElButton, ElTag } from 'element-plus' import { useVModel } from '@vueuse/core';
import { $$tablePropEditor } from './table-prop-edit.service' import { $$tablePropEditor } from './table-prop-edit.service';
import { useVModel } from '@vueuse/core' import { VisualEditorProps } from '@/visual-editor/visual-editor.props';
export const TablePropEditor = defineComponent({ export const TablePropEditor = defineComponent({
props: { props: {
modelValue: { type: Array as PropType<any[]> }, modelValue: { type: Array as PropType<any[]> },
propConfig: { type: Object as PropType<VisualEditorProps>, required: true } propConfig: { type: Object as PropType<VisualEditorProps>, required: true },
}, },
emits: ['update:modelValue'], emits: ['update:modelValue'],
setup(props, { emit }: SetupContext) { setup(props, { emit }: SetupContext) {
const model = useVModel(props, 'modelValue', emit) const model = useVModel(props, 'modelValue', emit);
const onClick = async () => { const onClick = async () => {
const data = await $$tablePropEditor({ const data = await $$tablePropEditor({
config: props.propConfig, config: props.propConfig,
data: props.modelValue || [] data: props.modelValue || [],
}) });
model.value = data model.value = data;
} };
return () => ( return () => (
<div> <div>
@ -30,6 +30,6 @@ export const TablePropEditor = defineComponent({
<ElTag {...({ onClick } as any)}>{item[props.propConfig.table!.showKey]}</ElTag> <ElTag {...({ onClick } as any)}>{item[props.propConfig.table!.showKey]}</ElTag>
))} ))}
</div> </div>
) );
} },
}) });

View File

@ -8,10 +8,10 @@
*/ */
import { defineComponent, computed, watch } from 'vue'; import { defineComponent, computed, watch } from 'vue';
import { ElForm, ElFormItem, ElPopover, ElRadioGroup, ElRadioButton, ElIcon } from 'element-plus'; import { ElForm, ElFormItem, ElPopover, ElRadioGroup, ElRadioButton, ElIcon } from 'element-plus';
import { Warning } from '@element-plus/icons-vue';
import { PropConfig } from './components/prop-config';
import { useVisualData } from '@/visual-editor/hooks/useVisualData'; import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import { FormatInputNumber } from '@/visual-editor/components/common/format-input-number'; import { FormatInputNumber } from '@/visual-editor/components/common/format-input-number';
import { PropConfig } from './components/prop-config';
import { Warning } from '@element-plus/icons-vue';
export const AttrEditor = defineComponent({ export const AttrEditor = defineComponent({
setup() { setup() {
@ -75,8 +75,8 @@ export const AttrEditor = defineComponent({
</ElFormItem> </ElFormItem>
</>, </>,
); );
if (!!component) { if (component) {
if (!!component.props) { if (component.props) {
content.push(<PropConfig component={component} block={currentBlock.value} />); content.push(<PropConfig component={component} block={currentBlock.value} />);
{ {
currentBlock.value.showStyleConfig && currentBlock.value.showStyleConfig &&

View File

@ -1,13 +1,12 @@
/* /*
* @Author: * @Author:
* @Date: 2021-06-24 11:01:45 * @Date: 2021-06-24 11:01:45
* @LastEditTime: 2021-07-08 09:53:27 * @LastEditTime: 2022-07-02 18:29:25
* @LastEditors: * @LastEditors:
* @Description: - * @Description: -
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\event-action\index.tsx * @FilePath: /vite-vue3-lowcode/src/visual-editor/components/right-attribute-panel/components/event-action/index.tsx
*/ */
import { computed, ref, defineComponent, reactive } from 'vue'; import { computed, ref, defineComponent, reactive } from 'vue';
import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import { import {
ElForm, ElForm,
ElFormItem, ElFormItem,
@ -21,10 +20,11 @@ import {
ElCollapseItem, ElCollapseItem,
ElPopconfirm, ElPopconfirm,
} from 'element-plus'; } from 'element-plus';
import { cloneDeep } from 'lodash-es';
import type { Action } from '@/visual-editor/visual-editor.utils'; import type { Action } from '@/visual-editor/visual-editor.utils';
import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import { generateNanoid } from '@/visual-editor/utils/'; import { generateNanoid } from '@/visual-editor/utils/';
import { useModal } from '@/visual-editor/hooks/useModal'; import { useModal } from '@/visual-editor/hooks/useModal';
import { cloneDeep } from 'lodash-es';
interface IState { interface IState {
activeNames: string[]; activeNames: string[];
@ -92,7 +92,6 @@ export const EventAction = defineComponent({
.filter((item) => item.actions?.length) .filter((item) => item.actions?.length)
.map((item) => { .map((item) => {
item.value = item._vid; item.value = item._vid;
item.label = item.label;
item.children = (item.actions || []).map((item: any) => { item.children = (item.actions || []).map((item: any) => {
item.label = item.name; item.label = item.name;
item.value = item.key; item.value = item.key;

View File

@ -1,10 +1,10 @@
/* /*
* @Author: * @Author:
* @Date: 2021-07-05 10:51:09 * @Date: 2021-07-05 10:51:09
* @LastEditTime: 2021-07-08 23:20:17 * @LastEditTime: 2022-07-02 22:46:59
* @LastEditors: * @LastEditors:
* @Description: * @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\form-rule\index.tsx * @FilePath: /vite-vue3-lowcode/src/visual-editor/components/right-attribute-panel/components/form-rule/index.tsx
*/ */
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { ElCard, ElTooltip } from 'element-plus'; import { ElCard, ElTooltip } from 'element-plus';
@ -29,7 +29,7 @@ export const FormRule = defineComponent({
default: () => <div></div>, default: () => <div></div>,
}} }}
</ElCard> </ElCard>
<ElCard shadow={'always'} bodyStyle={{ padding: 1 ? '0' : '20px' }} class={'mb-20px'}> <ElCard shadow={'always'} bodyStyle={{ padding: '0' }} class={'mb-20px'}>
{{ {{
header: () => ( header: () => (
<div class="flex justify-between"> <div class="flex justify-between">

View File

@ -7,8 +7,8 @@
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\index.ts * @FilePath: \vite-vue3-lowcode\src\visual-editor\components\right-attribute-panel\components\index.ts
*/ */
export { AttrEditor } from './attr-editor' export { AttrEditor } from './attr-editor';
export { Animate } from './animate/Animate' export { Animate } from './animate/Animate';
export { PageSetting } from './page-setting/pageSetting' export { PageSetting } from './page-setting/pageSetting';
export { EventAction } from './event-action/' export { EventAction } from './event-action/';
export { FormRule } from './form-rule/' export { FormRule } from './form-rule/';

View File

@ -8,9 +8,9 @@
*/ */
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { ElForm, ElFormItem, ElInput, ElUpload, ElColorPicker, ElSwitch } from 'element-plus'; import { ElForm, ElFormItem, ElInput, ElUpload, ElColorPicker, ElSwitch } from 'element-plus';
import { Plus } from '@element-plus/icons-vue';
import styles from './styles.module.scss'; import styles from './styles.module.scss';
import { useVisualData } from '@/visual-editor/hooks/useVisualData'; import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import { Plus } from '@element-plus/icons-vue';
export const PageSetting = defineComponent({ export const PageSetting = defineComponent({
setup() { setup() {

View File

@ -9,11 +9,11 @@
*/ */
import { defineComponent, reactive, watch } from 'vue'; import { defineComponent, reactive, watch } from 'vue';
import styles from './index.module.scss';
import { ElTabPane, ElTabs } from 'element-plus'; import { ElTabPane, ElTabs } from 'element-plus';
import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import { AttrEditor, Animate, PageSetting, EventAction, FormRule } from './components';
import { DArrowLeft, DArrowRight } from '@element-plus/icons-vue'; import { DArrowLeft, DArrowRight } from '@element-plus/icons-vue';
import styles from './index.module.scss';
import { AttrEditor, Animate, PageSetting, EventAction, FormRule } from './components';
import { useVisualData } from '@/visual-editor/hooks/useVisualData';
export default defineComponent({ export default defineComponent({
name: 'RightAttributePanel', name: 'RightAttributePanel',

View File

@ -6,17 +6,17 @@
* @Description: * @Description:
* @FilePath: \vite-vue3-lowcode\src\visual-editor\components\simulator-editor\comp-render.tsx * @FilePath: \vite-vue3-lowcode\src\visual-editor\components\simulator-editor\comp-render.tsx
*/ */
import { defineComponent, PropType } from 'vue' import { defineComponent, PropType } from 'vue';
import type { VisualEditorBlockData } from '@/visual-editor/visual-editor.utils' import type { VisualEditorBlockData } from '@/visual-editor/visual-editor.utils';
import { visualConfig } from '@/visual.config' import { visualConfig } from '@/visual.config';
export default defineComponent({ export default defineComponent({
name: 'CompRender', name: 'CompRender',
props: { props: {
element: { element: {
type: Object as PropType<VisualEditorBlockData>, type: Object as PropType<VisualEditorBlockData>,
default: () => ({}) default: () => ({}),
} },
}, },
setup(props) { setup(props) {
return visualConfig.componentMap[props.element.componentKey].render({ return visualConfig.componentMap[props.element.componentKey].render({
@ -24,7 +24,7 @@ export default defineComponent({
props: props.element.props || {}, props: props.element.props || {},
model: {}, model: {},
block: props.element, block: props.element,
custom: {} custom: {},
}) });
} },
}) });

View File

@ -3,11 +3,10 @@
v-model="list" v-model="list"
class="dragArea list-group" class="dragArea list-group"
:class="{ isDrag }" :class="{ isDrag }"
tag="transition-group"
:component-data="{ :component-data="{
tag: 'div', tag: 'ul',
type: 'transition-group', type: 'transition-group',
name: !isDrag ? 'flip-list' : null name: !isDrag ? 'flip-list' : null,
}" }"
:group="group" :group="group"
v-bind="{ ...dragOptions, ...$attrs }" v-bind="{ ...dragOptions, ...$attrs }"
@ -24,78 +23,78 @@
</template> </template>
<script lang="ts"> <script lang="ts">
/** /**
* @name: draggable-transition-group * @name: draggable-transition-group
* @author:卜启缘 * @author:卜启缘
* @date: 2021/5/1 23:15 * @date: 2021/5/1 23:15
* @descriptiondraggable-transition-group * @descriptiondraggable-transition-group
* @update: 2021/5/1 23:15 * @update: 2021/5/1 23:15
*/ */
import { computed, defineComponent, reactive, toRefs, SetupContext } from 'vue' import { computed, defineComponent, reactive, toRefs, SetupContext } from 'vue';
import draggable from 'vuedraggable' import draggable from 'vuedraggable';
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core';
export default defineComponent({ export default defineComponent({
name: 'DraggableTransitionGroup', name: 'DraggableTransitionGroup',
components: { draggable }, components: { draggable },
props: { props: {
moduleValue: { moduleValue: {
type: Array, type: Array,
default: () => [] default: () => [],
}, },
drag: { drag: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
itemKey: { itemKey: {
type: String, type: String,
default: '_vid' default: '_vid',
}, },
group: { group: {
type: Object, type: Object,
default: () => ({ name: 'components' }) default: () => ({ name: 'components' }),
}, },
fallbackClass: String fallbackClass: String,
}, },
emits: ['update:moduleValue', 'update:drag'], emits: ['update:moduleValue', 'update:drag'],
setup(props, { emit }: SetupContext) { setup(props, { emit }: SetupContext) {
const state = reactive({ const state = reactive({
list: useVModel(props, 'moduleValue', emit), list: useVModel(props, 'moduleValue', emit),
isDrag: useVModel(props, 'drag', emit) isDrag: useVModel(props, 'drag', emit),
}) });
const dragOptions = computed(() => ({ const dragOptions = computed(() => ({
animation: 200, animation: 200,
disabled: false, disabled: false,
scroll: true, scroll: true,
ghostClass: 'ghost' ghostClass: 'ghost',
})) }));
return { return {
...toRefs(state), ...toRefs(state),
dragOptions dragOptions,
} };
} },
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import './func.scss'; @import './func.scss';
.flip-list-move { .flip-list-move {
transition: transform 0.5s; transition: transform 0.5s;
} }
.no-move { .no-move {
transition: transform 0s; transition: transform 0s;
} }
.ghost { .ghost {
background: #c8ebfb; background: #c8ebfb;
opacity: 0.5; opacity: 0.5;
} }
.list-group { .list-group {
height: 100%; height: 100%;
min-height: 40px; min-height: 40px;
@ -106,5 +105,5 @@ export default defineComponent({
&.isDrag:not(.no-child) :deep(.list-group-item.has-slot) { &.isDrag:not(.no-child) :deep(.list-group-item.has-slot) {
@include showContainerBorder; @include showContainerBorder;
} }
} }
</style> </style>

View File

@ -55,13 +55,13 @@
<script lang="tsx"> <script lang="tsx">
import { defineComponent, reactive, watchEffect, toRefs } from 'vue'; import { defineComponent, reactive, watchEffect, toRefs } from 'vue';
import type { VisualEditorBlockData } from '@/visual-editor/visual-editor.utils'; import { cloneDeep } from 'lodash-es';
import DraggableTransitionGroup from './draggable-transition-group.vue'; import DraggableTransitionGroup from './draggable-transition-group.vue';
import { $$dropdown, DropdownOption } from '@/visual-editor/utils/dropdown-service';
import CompRender from './comp-render'; import CompRender from './comp-render';
import SlotItem from './slot-item.vue'; import SlotItem from './slot-item.vue';
import type { VisualEditorBlockData } from '@/visual-editor/visual-editor.utils';
import { $$dropdown, DropdownOption } from '@/visual-editor/utils/dropdown-service';
import MonacoEditor from '@/visual-editor/components/common/monaco-editor/MonacoEditor'; import MonacoEditor from '@/visual-editor/components/common/monaco-editor/MonacoEditor';
import { cloneDeep } from 'lodash-es';
import { useGlobalProperties } from '@/hooks/useGlobalProperties'; import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { useVisualData } from '@/visual-editor/hooks/useVisualData'; import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import { useModal } from '@/visual-editor/hooks/useModal'; import { useModal } from '@/visual-editor/hooks/useModal';

View File

@ -13,7 +13,7 @@
:data-label="innerElement.label" :data-label="innerElement.label"
:class="{ :class="{
focus: innerElement.focus, focus: innerElement.focus,
focusWithChild: innerElement.focusWithChild focusWithChild: innerElement.focusWithChild,
}" }"
@contextmenu.stop.prevent="onContextmenuBlock($event, innerElement, slotChildren)" @contextmenu.stop.prevent="onContextmenuBlock($event, innerElement, slotChildren)"
@mousedown.stop="selectComp(innerElement)" @mousedown.stop="selectComp(innerElement)"
@ -21,7 +21,7 @@
<comp-render <comp-render
:element="innerElement" :element="innerElement"
:style="{ :style="{
pointerEvents: Object.keys(innerElement.props?.slots || {}).length ? 'auto' : 'none' pointerEvents: Object.keys(innerElement.props?.slots || {}).length ? 'auto' : 'none',
}" }"
> >
<template v-for="(value, key) in innerElement.props?.slots" :key="key" #[key]> <template v-for="(value, key) in innerElement.props?.slots" :key="key" #[key]>
@ -40,7 +40,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
/** /**
* @name: slot-item * @name: slot-item
* @author:卜启缘 * @author:卜启缘
* @date: 2021/5/2 22:36 * @date: 2021/5/2 22:36
@ -48,64 +48,64 @@
* @update: 2021/5/2 22:36 * @update: 2021/5/2 22:36
*/ */
import { defineComponent, PropType } from 'vue' import { defineComponent, PropType } from 'vue';
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core';
import DraggableTransitionGroup from './draggable-transition-group.vue' import DraggableTransitionGroup from './draggable-transition-group.vue';
import CompRender from './comp-render' import CompRender from './comp-render';
import type { VisualEditorBlockData } from '@/visual-editor/visual-editor.utils' import type { VisualEditorBlockData } from '@/visual-editor/visual-editor.utils';
export default defineComponent({ export default defineComponent({
name: 'SlotItem', name: 'SlotItem',
components: { CompRender, DraggableTransitionGroup }, components: { CompRender, DraggableTransitionGroup },
props: { props: {
slotKey: { slotKey: {
type: String as PropType<string>, type: String as PropType<string | number>,
default: '' default: '',
}, },
drag: { drag: {
type: Boolean as PropType<boolean>, type: Boolean as PropType<boolean>,
default: false default: false,
}, },
children: { children: {
type: Array as PropType<VisualEditorBlockData[]>, type: Array as PropType<VisualEditorBlockData[]>,
default: () => [] default: () => [],
}, },
selectComp: { selectComp: {
type: Function as PropType<(comp: VisualEditorBlockData) => void>, type: Function as PropType<(comp: VisualEditorBlockData) => void>,
required: true required: true,
}, },
onContextmenuBlock: { onContextmenuBlock: {
type: Function as PropType< type: Function as PropType<
( (
e: MouseEvent, e: MouseEvent,
block: VisualEditorBlockData, block: VisualEditorBlockData,
parentBlocks?: VisualEditorBlockData[] parentBlocks?: VisualEditorBlockData[],
) => void ) => void
>, >,
required: true required: true,
} },
}, },
emits: ['update:children', 'on-selected', 'update:drag'], emits: ['update:children', 'on-selected', 'update:drag'],
setup(props, { emit }) { setup(props, { emit }) {
// //
props.children.some((item) => item.focus && !void props.selectComp(item)) props.children.some((item) => item.focus && props.selectComp(item));
return { return {
isDrag: useVModel(props, 'drag', emit), isDrag: useVModel(props, 'drag', emit),
slotChildren: useVModel(props, 'children', emit) slotChildren: useVModel(props, 'children', emit),
} };
} },
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import './func.scss'; @import './func.scss';
.inner-draggable { .inner-draggable {
position: relative; position: relative;
} }
.inner-draggable.slot::after { .inner-draggable.slot::after {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
@ -123,9 +123,9 @@ export default defineComponent({
outline-offset: -1px; outline-offset: -1px;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
} }
.list-group-item { .list-group-item {
position: relative; position: relative;
padding: 3px; padding: 3px;
cursor: move; cursor: move;
@ -148,5 +148,5 @@ export default defineComponent({
opacity: 1; opacity: 1;
} }
} }
} }
</style> </style>

View File

@ -6,13 +6,13 @@
* @update: 2021/5/2 19:54 * @update: 2021/5/2 19:54
*/ */
export const useDotProp = (originObj, propName) => { export const useDotProp = (originObj, propName) => {
const props: string[] = propName.split('.') const props: string[] = propName.split('.');
const isDotProp = props.length > 1 const isDotProp = props.length > 1;
const prop = props.pop()! const prop = props.pop()!;
const propObj = props.reduce((prev, curr) => (prev[curr] ??= {}), originObj) const propObj = props.reduce((prev, curr) => (prev[curr] ??= {}), originObj);
return { return {
prop, prop,
propObj, propObj,
isDotProp isDotProp,
} };
} };

View File

@ -12,58 +12,58 @@ import {
PropType, PropType,
getCurrentInstance, getCurrentInstance,
ComponentInternalInstance, ComponentInternalInstance,
isVNode isVNode,
} from 'vue' } from 'vue';
import { ElButton, ElDialog } from 'element-plus' import { ElButton, ElDialog } from 'element-plus';
import { isFunction } from '@/visual-editor/utils/is' import { isFunction } from '@/visual-editor/utils/is';
interface ModalOptions { interface ModalOptions {
title?: string title?: string;
footer?: null | (() => JSX.Element) footer?: null | (() => JSX.Element);
content: ComponentInternalInstance | (() => JSX.Element) content: ComponentInternalInstance | (() => JSX.Element);
onConfirm?: () => void onConfirm?: () => void;
onCancel?: () => void onCancel?: () => void;
props?: { props?: {
[propName: string]: any [propName: string]: any;
} };
} }
const Modal = defineComponent({ const Modal = defineComponent({
props: { props: {
options: { options: {
type: Object as PropType<ModalOptions>, type: Object as PropType<ModalOptions>,
default: () => ({}) default: () => ({}),
} },
}, },
setup(props) { setup(props) {
const instance = getCurrentInstance()! const instance = getCurrentInstance()!;
const state = reactive({ const state = reactive({
options: props.options, options: props.options,
visible: true visible: true,
}) });
const methods = { const methods = {
service: (options: ModalOptions) => { service: (options: ModalOptions) => {
state.options = options state.options = options;
methods.show() methods.show();
}, },
show: () => (state.visible = true), show: () => (state.visible = true),
hide: () => (state.visible = false) hide: () => (state.visible = false),
} };
const handler = { const handler = {
onConfirm: async () => { onConfirm: async () => {
await state.options.onConfirm?.() await state.options.onConfirm?.();
methods.hide() methods.hide();
}, },
onCancel: () => { onCancel: () => {
state.options.onCancel?.() state.options.onCancel?.();
methods.hide() methods.hide();
} },
} };
Object.assign(instance.proxy, methods) Object.assign(instance.proxy!, methods);
return () => ( return () => (
<ElDialog <ElDialog
@ -88,24 +88,24 @@ const Modal = defineComponent({
</ElButton> </ElButton>
</div> </div>
) ),
}} }}
</ElDialog> </ElDialog>
) );
} },
}) });
export const useModal = (() => { export const useModal = (() => {
let instance: any let instance: any;
return (options: ModalOptions) => { return (options: ModalOptions) => {
if (instance) { if (instance) {
instance.service(options) instance.service(options);
return instance return instance;
} }
const div = document.createElement('div') const div = document.createElement('div');
document.body.appendChild(div) document.body.appendChild(div);
const app = createApp(Modal, { options }) const app = createApp(Modal, { options });
instance = app.mount(div) instance = app.mount(div);
return instance return instance;
} };
})() })();

View File

@ -1,42 +1,42 @@
import { ref, watch, defineComponent } from 'vue' import { ref, watch, defineComponent } from 'vue';
export function useModel<T>(getter: () => T, emitter: (val: T) => void) { export function useModel<T>(getter: () => T, emitter: (val: T) => void) {
const state = ref(getter()) as { value: T } const state = ref(getter()) as { value: T };
watch(getter, (val) => { watch(getter, (val) => {
if (val !== state.value) { if (val !== state.value) {
state.value = val state.value = val;
} }
}) });
return { return {
get value() { get value() {
return state.value return state.value;
}, },
set value(val: T) { set value(val: T) {
if (state.value !== val) { if (state.value !== val) {
state.value = val state.value = val;
emitter(val) emitter(val);
}
}
} }
},
};
} }
export const TestUseModel = defineComponent({ export const TestUseModel = defineComponent({
props: { props: {
modelValue: { type: String } modelValue: { type: String },
}, },
emits: ['update:modelValue'], emits: ['update:modelValue'],
setup(props, ctx) { setup(props, ctx) {
const model = useModel( const model = useModel(
() => props.modelValue, () => props.modelValue,
(val) => ctx.emit('update:modelValue', val) (val) => ctx.emit('update:modelValue', val),
) );
return () => ( return () => (
<div> <div>
<input type="text" v-model={model.value} /> <input type="text" v-model={model.value} />
</div> </div>
) );
} },
}) });

Some files were not shown because too many files have changed in this diff Show More