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
.local
/bin
/src/mock
Dockerfile
commitlint.config.js
components.d.ts
auto-imports.d.ts

View File

@ -1,6 +1,4 @@
// @ts-check
const { defineConfig } = require('eslint-define-config');
module.exports = defineConfig({
module.exports = {
root: true,
env: {
browser: true,
@ -15,28 +13,50 @@ module.exports = defineConfig({
jsxPragma: 'React',
ecmaFeatures: {
jsx: true,
tsx: true,
},
},
plugins: ['@typescript-eslint', 'prettier', 'import'],
extends: [
'plugin:vue/vue3-recommended',
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-recommended',
'prettier',
'plugin:prettier/recommended',
],
overrides: [
{
files: ['*.ts', '*.tsx', '*.vue'],
rules: {
'no-undef': 'off',
},
},
],
rules: {
'vue/script-setup-uses-vars': 'error',
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'vue/custom-event-name-casing': 'off',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'off',
// js/ts
// 'no-console': ['warn', { allow: ['error'] }],
'no-restricted-syntax': ['error', 'LabeledStatement', 'WithStatement'],
camelcase: ['error', { properties: 'never' }],
'no-var': 'error',
'no-empty': ['error', { allowEmptyCatch: true }],
'no-void': 'error',
'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-types': 'off',
'@typescript-eslint/no-non-null-assertion': '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': [
'error',
{
@ -51,29 +71,43 @@ module.exports = defineConfig({
varsIgnorePattern: '^_',
},
],
'space-before-function-paren': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': '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
'vue/no-v-html': 'off',
'vue/require-default-prop': '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',
{
html: {
void: 'always',
normal: 'never',
component: 'always',
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
pathGroups: [
{
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:
push:
branches: [main]
jobs:
repo-sync:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Checkout
uses: actions/checkout@v3
# 设置服务器时区为东八区
- name: Set time zone
run: sudo timedatectl set-timezone 'Asia/Shanghai'
- name: Setup node
uses: actions/setup-node@v3
with:
# Full git history is needed to get a proper list of changed files within `super-linter`
fetch-depth: 0
- name: Setup Node.js v14.x
uses: actions/setup-node@v2
with:
node-version: '14.x'
node-version: '16'
registry-url: https://registry.npmjs.com/
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: latest
- name: Install
run: pnpm install
- name: Cache ~/.pnpm-store
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
run: pnpm build
env:
FORCE_COLOR: 2
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
@ -60,6 +78,6 @@ jobs:
# 注意替换为你的 Gitee 仓库,仓库名严格区分大小写,请准确填写,否则会出错
gitee-repo: buqiyuan/vite-vue3-lowcode
# 是否强制使用 HTTPS
https: false
https: true
# 要部署的分支,默认是 master若是其他分支则需要指定指定的分支必须存在
branch: gh-pages

4
.gitignore vendored
View File

@ -51,3 +51,7 @@ testem.log
# System Files
.DS_Store
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": [
"johnsoncodehk.volar",
"vue.volar",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint",
"esbenp.prettier-vscode",

47
.vscode/settings.json vendored
View File

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

9
auto-imports.d.ts vendored
View File

@ -1,17 +1,19 @@
// Generated by 'unplugin-auto-import'
// We suggest you to commit this file into source control
export {};
declare global {
const EffectScope: typeof import('vue')['EffectScope'];
const computed: typeof import('vue')['computed'];
const createApp: typeof import('vue')['createApp'];
const customRef: typeof import('vue')['customRef'];
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'];
const defineComponent: typeof import('vue')['defineComponent'];
const effectScope: typeof import('vue')['effectScope'];
const EffectScope: typeof import('vue')['EffectScope'];
const getCurrentInstance: typeof import('vue')['getCurrentInstance'];
const getCurrentScope: typeof import('vue')['getCurrentScope'];
const h: typeof import('vue')['h'];
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 isRef: typeof import('vue')['isRef'];
const markRaw: typeof import('vue')['markRaw'];
@ -50,5 +52,6 @@ declare global {
const useSlots: typeof import('vue')['useSlots'];
const watch: typeof import('vue')['watch'];
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
// 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 {
ElAside: typeof import('element-plus/es')['ElAside'];
ElButton: typeof import('element-plus/es')['ElButton'];
@ -26,8 +29,11 @@ declare module 'vue' {
ElTag: typeof import('element-plus/es')['ElTag'];
ElTooltip: typeof import('element-plus/es')['ElTooltip'];
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",
"serve": "npm run dev",
"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-tsc": "vue-tsc --noEmit && vite build",
"preview": "npm run build && vite preview",
@ -15,77 +15,89 @@
"log": "conventional-changelog -p angular -i CHANGELOG.md -s",
"clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
"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: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:lint-staged": "lint-staged",
"deploy": "npm run build && npx gh-pages -d dist",
"test:gzip": "npx http-server dist --cors --gzip -c-1",
"test:br": "npx http-server dist --cors --brotli -c-1",
"prepare": "husky install",
"postversion": "git push && git push origin --tags",
"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": {
"@element-plus/icons-vue": "^0.2.4",
"@element-plus/icons-vue": "^2.0.6",
"@vant/touch-emulator": "^1.3.2",
"@vueuse/core": "^7.5.3",
"@vueuse/integrations": "^7.5.3",
"@vueuse/core": "^8.7.5",
"@vueuse/integrations": "^8.7.5",
"animate.css": "^4.1.1",
"axios": "^0.24.0",
"dayjs": "^1.10.7",
"dexie": "^3.2.0",
"element-plus": "1.3.0-beta.5",
"axios": "^0.27.2",
"dayjs": "^1.11.3",
"dexie": "^3.2.2",
"element-plus": "2.2.8",
"lodash-es": "^4.17.21",
"monaco-editor": "^0.31.1",
"nanoid": "^3.1.32",
"monaco-editor": "^0.33.0",
"nanoid": "^4.0.0",
"normalize.css": "^8.0.1",
"nprogress": "^1.0.0-1",
"pinia": "^2.0.9",
"pinia": "^2.0.14",
"qrcode": "^1.5.0",
"qs": "^6.10.3",
"vant": "3.4.1",
"vue": "3.2.26",
"vue-router": "^4.0.12",
"qs": "^6.11.0",
"vant": "3.5.2",
"vue": "3.2.37",
"vue-router": "^4.0.16",
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@commitlint/cli": "^16.0.2",
"@commitlint/config-conventional": "^16.0.0",
"@types/lodash-es": "^4.17.5",
"@types/node": "^17.0.8",
"@typescript-eslint/eslint-plugin": "^5.9.1",
"@typescript-eslint/parser": "^5.9.1",
"@vitejs/plugin-legacy": "^1.6.4",
"@vitejs/plugin-vue": "^2.0.1",
"@vitejs/plugin-vue-jsx": "^1.3.3",
"@vue/compiler-sfc": "3.2.26",
"@commitlint/cli": "^17.0.3",
"@commitlint/config-conventional": "^17.0.3",
"@types/lodash-es": "^4.17.6",
"@types/node": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.30.3",
"@typescript-eslint/parser": "^5.30.3",
"@vitejs/plugin-legacy": "^1.8.2",
"@vitejs/plugin-vue": "^2.3.3",
"@vitejs/plugin-vue-jsx": "^1.3.10",
"commitizen": "^4.2.4",
"conventional-changelog-cli": "^2.2.2",
"cross-env": "^7.0.3",
"eslint": "^8.7.0",
"eslint-config-prettier": "^8.3.0",
"eslint-define-config": "^1.2.2",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.3.0",
"husky": "^7.0.4",
"lint-staged": "^12.1.7",
"postcss-html": "^1.3.0",
"prettier": "^2.5.1",
"sass": "1.48.0",
"stylelint": "^14.2.0",
"eslint": "^8.19.0",
"eslint-config-prettier": "^8.5.0",
"eslint-define-config": "^1.5.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.1.1",
"husky": "^8.0.1",
"lint-staged": "^13.0.3",
"postcss": "^8.4.14",
"postcss-html": "^1.4.1",
"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-prettier": "^9.0.3",
"stylelint-config-recommended": "^6.0.0",
"stylelint-config-standard": "^24.0.0",
"stylelint-config-recommended": "^8.0.0",
"stylelint-config-standard": "^26.0.0",
"stylelint-order": "^5.0.0",
"stylelint-scss": "^4.1.0",
"typescript": "^4.5.4",
"unplugin-auto-import": "^0.5.11",
"unplugin-vue-components": "^0.17.11",
"vite": "2.7.12",
"vite-plugin-windicss": "^1.6.2",
"vue-eslint-parser": "^8.0.1",
"windicss": "^3.4.2"
"stylelint-scss": "^4.2.0",
"typescript": "^4.7.4",
"unplugin-auto-import": "^0.9.2",
"unplugin-vue-components": "^0.21.0",
"unplugin-vue-define-options": "^0.6.1",
"vite": "2.9.13",
"vite-plugin-checker": "^0.4.7",
"vite-plugin-windicss": "^1.8.6",
"vue-eslint-parser": "^9.0.3",
"vue-tsc": "^0.38.2",
"windicss": "^3.5.6"
},
"repository": {
"type": "git",
@ -104,17 +116,14 @@
},
"homepage": "https://github.com/buqiyuan/vite-vue3-lowcode#readme",
"engines": {
"node": "^12 || >=14"
"node": ">=14"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [
"prettier --write--parser json"
],
"package.json": [
"*.json": [
"prettier --write"
],
"*.vue": [
@ -129,5 +138,6 @@
"*.md": [
"prettier --write"
]
}
},
"packageManager": "^pnpm@6.32.4"
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,21 +1,21 @@
import { createApp } from 'vue'
import App from './App.vue'
import { createApp } from '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插件
setupVant(app)
setupVant(app);
app.config.globalProperties.$$refs = {}
app.config.globalProperties.$$refs = {};
// 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:
* @FilePath: \vite-vue3-lowcode\preview\router.ts
*/
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import type { VisualEditorModelValue } from '@/visual-editor/visual-editor.utils'
import { CacheEnum } from '@/enums'
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
import type { VisualEditorModelValue } from '@/visual-editor/visual-editor.utils';
import { CacheEnum } from '@/enums';
const routes: Array<RouteRecordRaw> = [
{
path: '/:pathMatch(.*)*',
component: () => import('./views/preview.vue')
}
]
component: () => import('./views/preview.vue'),
},
];
const router = createRouter({
history: createWebHashHistory(),
routes
})
routes,
});
// 获取本地缓存的页面数据
const jsonData: VisualEditorModelValue = JSON.parse(
localStorage.getItem(CacheEnum.PAGE_DATA_KEY) as string
)
localStorage.getItem(CacheEnum.PAGE_DATA_KEY) as string,
);
router.beforeEach((to) => {
document.title = jsonData?.pages?.[to.path]?.title ?? document.title
return true
})
document.title = jsonData?.pages?.[to.path]?.title ?? document.title;
return true;
});
export default router
export default router;

View File

@ -5,7 +5,7 @@ export enum ResultEnum {
SUCCESS = 0,
ERROR = -1,
TIMEOUT = 10042,
TYPE = 'success'
TYPE = 'success',
}
/**
@ -16,7 +16,7 @@ export enum RequestEnum {
POST = 'POST',
PATCH = 'PATCH',
PUT = 'PUT',
DELETE = 'DELETE'
DELETE = 'DELETE',
}
/**
@ -30,5 +30,5 @@ export enum ContentTypeEnum {
// form-data 一般配合qs
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
// 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 store from '@/store'
import { Toast } from 'vant';
import router from '@/router';
import { ContentTypeEnum } from './httpEnum';
import router from '@/router';
// create an axios instance
const service = axios.create({
@ -85,7 +85,7 @@ service.interceptors.response.use(
if (error.message?.includes('timeout')) {
Toast('请求超时!');
}
console.log('err' + error); // for debug
console.log(`err${error}`); // for debug
return Promise.reject(error);
},
);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@ export enum ResultEnum {
SUCCESS = 0,
ERROR = -1,
TIMEOUT = 10042,
TYPE = 'success'
TYPE = 'success',
}
/**
@ -16,7 +16,7 @@ export enum RequestEnum {
POST = 'POST',
PATCH = 'PATCH',
PUT = 'PUT',
DELETE = 'DELETE'
DELETE = 'DELETE',
}
/**
@ -30,5 +30,5 @@ export enum ContentTypeEnum {
// form-data 一般配合qs
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
// 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
*/
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
*/
import type { Animation } from '@/visual-editor/visual-editor.utils'
import type { Animation } from '@/visual-editor/visual-editor.utils';
export const useAnimate = async (
animateEl: HTMLElement,
animations: Animation | Animation[],
prefixCls = 'animate__'
prefixCls = 'animate__',
) => {
animations = Array.isArray(animations) ? animations : [animations]
animations = Array.isArray(animations) ? animations : [animations];
const play = (animate: Animation) =>
new Promise((resolve) => {
if (animateEl) {
const animationName = `${prefixCls}${animate.value}`
const animationName = `${prefixCls}${animate.value}`;
// 过滤可能残留的animate.css动画类名
animateEl.classList.value = animateEl.classList.value
.split(' ')
.filter((item) => !item.includes(prefixCls))
.join(' ')
.join(' ');
// 设置动画属性
const setAnimate = () => {
animateEl.style.setProperty('--animate-duration', `${animate.duration}s`)
animateEl.style.setProperty('animation-delay', `${animate.delay}s`)
animateEl.style.setProperty('--animate-duration', `${animate.duration}s`);
animateEl.style.setProperty('animation-delay', `${animate.delay}s`);
animateEl.style.setProperty(
'animation-iteration-count',
`${animate.infinite ? 'infinite' : animate.count}`
)
animateEl?.classList.add(`${prefixCls}animated`, animationName)
}
`${animate.infinite ? 'infinite' : animate.count}`,
);
animateEl?.classList.add(`${prefixCls}animated`, animationName);
};
// 动画结束时,删除类名
const handleAnimationEnd = (event?: AnimationEvent) => {
event?.stopPropagation()
animateEl.classList.remove(`${prefixCls}animated`, animationName)
animateEl.removeEventListener('animationend', handleAnimationEnd)
resolve('animation end')
}
event?.stopPropagation();
animateEl.classList.remove(`${prefixCls}animated`, animationName);
animateEl.removeEventListener('animationend', handleAnimationEnd);
resolve('animation end');
};
setAnimate()
setAnimate();
animateEl?.addEventListener('animationend', handleAnimationEnd, { once: true })
animateEl?.addEventListener('animationend', handleAnimationEnd, { once: true });
// animateEl?.addEventListener('animationcancel', handleAnimationEnd, { once: true })
} else {
resolve('动画执行失败!执行动画元素不存在!')
resolve('动画执行失败!执行动画元素不存在!');
}
})
});
for (const item of animations) {
await play(item)
await play(item);
}
}
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,14 +7,14 @@
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\image\index.tsx
*/
import { Image } from 'vant';
import { Picture } from '@element-plus/icons-vue';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import {
createEditorInputProp,
createEditorSelectProp,
createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { Picture } from '@element-plus/icons-vue';
export default {
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) => {
const name = key.replace(/\.\/(.*)\/index\.(tsx|vue)/, '$1')
components[name] = modules[key]?.default || modules[key]
})
const name = key.replace(/\.\/(.*)\/index\.(tsx|vue)/, '$1');
components[name] = modules[key]?.default || modules[key];
});
console.log(components, 'base-widgets')
export default components
console.log(components, 'base-widgets');
export default components;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,8 @@
import {
createEditorInputProp,
createEditorSelectProp,
createEditorSwitchProp
} from '@/visual-editor/visual-editor.props'
createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props';
/**
* @name: createFieldProps
@ -19,21 +19,21 @@ export const createFieldProps = () => ({
options: [
{
label: '左对齐',
value: 'left'
value: 'left',
},
{
label: '居中',
value: 'center'
value: 'center',
},
{
label: '右对齐',
value: 'right'
}
value: 'right',
},
],
defaultValue: 'left'
defaultValue: 'left',
}),
border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }),
readonly: 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
*/
import { Field, Rate } from 'vant';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { createFieldProps } from './createFieldProps';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import {
createEditorInputNumberProp,

View File

@ -1,8 +1,8 @@
import {
createEditorInputProp,
createEditorSelectProp,
createEditorSwitchProp
} from '@/visual-editor/visual-editor.props'
createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props';
/**
* @name: createFieldProps
@ -19,21 +19,21 @@ export const createFieldProps = () => ({
options: [
{
label: '左对齐',
value: 'left'
value: 'left',
},
{
label: '居中',
value: 'center'
value: 'center',
},
{
label: '右对齐',
value: 'right'
}
value: 'right',
},
],
defaultValue: 'left'
defaultValue: 'left',
}),
border: createEditorSwitchProp({ label: '是否显示内边框', defaultValue: true }),
readonly: 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
*/
import { Field, Slider } from 'vant';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { omit } from 'lodash-es';
import { createFieldProps } from './createFieldProps';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import {
createEditorInputNumberProp,
@ -16,7 +17,6 @@ import {
createEditorModelBindProp,
createEditorSwitchProp,
} from '@/visual-editor/visual-editor.props';
import { omit } from 'lodash-es';
export default {
key: 'slider',

View File

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

View File

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

View File

@ -9,14 +9,17 @@
import {
createEditorInputProp,
createEditorSwitchProp,
createEditorCrossSortableProp
} from '@/visual-editor/visual-editor.props'
createEditorCrossSortableProp,
} from '@/visual-editor/visual-editor.props';
export const createFieldProps = () => ({
images: createEditorCrossSortableProp({
label: '图片列表',
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' }),
height: createEditorInputProp({ label: '滑块高度,单位为 px', defaultValue: '200' }),
@ -29,5 +32,5 @@ export const createFieldProps = () => ({
showIndicators: createEditorSwitchProp({ label: '是否显示指示器', defaultValue: true }),
stopPropagation: 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:
* @FilePath: \vite-vue3-lowcode\src\packages\base-widgets\swipe\index.tsx
*/
import { Swipe, SwipeItem } from 'vant'
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils'
import { createFieldProps } from './createFieldProps'
import { useGlobalProperties } from '@/hooks/useGlobalProperties'
import { Swipe, SwipeItem } from 'vant';
import { createFieldProps } from './createFieldProps';
import type { VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { useGlobalProperties } from '@/hooks/useGlobalProperties';
const swipeItemStyle = `color: #fff;
font-size: 20px;
line-height: 150px;
text-align: center;
background-color: #39a9ed;`
background-color: #39a9ed;`;
export default {
key: 'swipe',
@ -30,7 +30,7 @@ export default {
</Swipe>
),
render: ({ block, props }) => {
const { registerRef } = useGlobalProperties()
const { registerRef } = useGlobalProperties();
return () => (
<div>
@ -48,15 +48,15 @@ export default {
))}
</Swipe>
</div>
)
);
},
props: createFieldProps(),
events: [{ label: '每一页轮播结束后触发', value: 'change' }],
showStyleConfig: false,
resize: {
width: true
width: true,
},
model: {
default: '绑定字段'
}
} as VisualEditorComponent
default: '绑定字段',
},
} as VisualEditorComponent;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,8 @@
import type { App } from 'vue'
import '@vant/touch-emulator'
import 'vant/lib/index.css'
import { Lazyload } from 'vant'
import { Lazyload } from 'vant';
import type { App } from 'vue';
import '@vant/touch-emulator';
import 'vant/lib/index.css';
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 NProgress from 'nprogress' // progress bar
import 'nprogress/css/nprogress.css' // 进度条样式
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
import NProgress from 'nprogress'; // progress bar
import 'nprogress/css/nprogress.css'; // 进度条样式
NProgress.configure({ showSpinner: false }) // NProgress Configuration
NProgress.configure({ showSpinner: false }); // NProgress Configuration
const routes: Array<RouteRecordRaw> = [
{
path: '/:pathMatch(.*)*',
component: () => import('@/visual-editor/index.vue')
}
]
component: () => import('@/visual-editor/index.vue'),
},
];
const router = createRouter({
history: createWebHashHistory(),
routes
})
routes,
});
router.beforeEach(() => {
NProgress.start() // start progress bar
return true
})
NProgress.start(); // start progress bar
return true;
});
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 type { App } from 'vue';
const store = createPinia();

View File

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

View File

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

View File

@ -1,24 +1,24 @@
// 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 jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
import cssWorker from 'monaco-editor/esm/vs/language/css/css.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 htmlWorker from 'monaco-editor/esm/vs/language/html/html.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 = {
getWorker(_: string, label: string) {
if (label === 'json') return new jsonWorker()
if (label === 'css' || label === 'scss' || label === 'less') return new cssWorker()
if (label === 'json') return new jsonWorker();
if (label === 'css' || label === 'scss' || label === 'less') return new cssWorker();
// if (label === 'html' || label === 'handlebars' || label === 'razor') return new htmlWorker()
// if (label === 'typescript' || label === 'javascript') return new tsWorker()
// 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>
<script lang="ts" setup>
import { VideoPlay } from '@element-plus/icons-vue';
import Preview from './preview.vue';
import { useTools } from './useTools';
import { useVisualData, localKey } from '@/visual-editor/hooks/useVisualData';
import { BASE_URL } from '@/visual-editor/utils';
import { useTools } from './useTools';
import { VideoPlay } from '@element-plus/icons-vue';
const isShowH5Preview = ref(false);

View File

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

View File

@ -7,11 +7,8 @@
*/
import { reactive } from 'vue';
import { ElMessage, ElRadio, ElRadioGroup } from 'element-plus';
import { useQRCode } from '@vueuse/integrations';
import { useQRCode } from '@vueuse/integrations/useQRCode';
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 {
DocumentCopy,
Cellphone,
@ -23,6 +20,9 @@ import {
Download,
Upload,
} 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';
export const useTools = () => {
@ -95,7 +95,7 @@ export const useTools = () => {
title: '真机预览',
icon: Cellphone,
onClick: () => {
const qrcode = useQRCode(location.origin + '/preview');
const qrcode = useQRCode(`${location.origin}/preview`);
useModal({
title: '预览二维码(暂不可用)',
props: {

View File

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

View File

@ -8,12 +8,12 @@
*/
import { defineComponent } from 'vue';
import { cloneDeep } from 'lodash-es';
import { visualConfig } from '@/visual.config';
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 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({
name: 'ContainerComponent',

View File

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

View File

@ -23,7 +23,7 @@
<div class="model-item-title">
<span class="truncate w-160px">{{ item.name }}</span>
<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 />
</el-icon>
<el-popconfirm
@ -32,7 +32,7 @@
@confirm="deleteModel(item.key)"
>
<template #reference>
<el-icon size="24" color="#f44336"><Delete /></el-icon>
<el-icon :size="24" color="#f44336"><Delete /></el-icon>
</template>
</el-popconfirm>
</div>
@ -59,14 +59,15 @@
ElCard,
ElButton,
ElMessage,
ElIcon,
} 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 { generateNanoid } from '@/visual-editor/utils/';
import { useImportSwaggerJsonModal } from './utils';
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 {
activeNames: string[];
@ -261,6 +262,7 @@
.model-actions {
display: flex;
i {
padding: 6px;
margin: 0 2px;

View File

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

View File

@ -1,19 +1,18 @@
/*
* @Author:
* @Date: 2021-06-27 13:15:19
* @LastEditTime: 2021-06-27 15:22:51
* @LastEditTime: 2022-07-02 23:12:37
* @LastEditors:
* @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 type { FetchApiItem } from '@/visual-editor/visual-editor.utils';
import { RequestEnum } from '@/enums/httpEnum';
import MonacoEditor from '@/visual-editor/components/common/monaco-editor/MonacoEditor';
import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import type { VisualEditorModel } from '@/visual-editor/visual-editor.utils';
import { useModal } from '@/visual-editor/hooks/useModal';
import { ElMessage } from 'element-plus';
/**
* @description , eg: 简单的解析代码
@ -51,7 +50,7 @@ export const importSwaggerJson = (swagger: any) => {
key: generateNanoid(),
name: apiUrlObj.summary,
options: {
url: url, // 请求的url
url, // 请求的url
method: method.toLocaleUpperCase() as RequestEnum, // 请求的方法
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) {
const comp = modules[path].default
components[comp.name || path.split('/')[1]] = comp
const comp = modules[path].default;
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>
</template>
<script lang="tsx">
export default {
<script lang="tsx" setup>
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',
label: '页面',
order: 1,
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 = {
title: [{ required: true, message: '请输入页面标题', trigger: 'blur' }],

View File

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

View File

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

View File

@ -21,12 +21,12 @@ import {
ElIcon,
} from 'element-plus';
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 { 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 {
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
*/
export { CrossSortableOptionsEditor } from './cross-sortable-options-editor/cross-sortable-options-editor'
export { TablePropEditor } from './table-prop-editor/table-prop-editor'
export { CrossSortableOptionsEditor } from './cross-sortable-options-editor/cross-sortable-options-editor';
export { TablePropEditor } from './table-prop-editor/table-prop-editor';

View File

@ -1,10 +1,10 @@
/*
* @Author:
* @Date: 2021-07-11 17:53:54
* @LastEditTime: 2021-07-11 18:36:17
* @LastEditTime: 2022-07-02 22:58:55
* @LastEditors:
* @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';
@ -19,14 +19,15 @@ import {
ElFormItem,
ElTooltip,
ElIcon,
ExpandTrigger,
} 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 { 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 { VisualEditorBlockData, VisualEditorComponent } from '@/visual-editor/visual-editor.utils';
import { Warning } from '@element-plus/icons-vue';
export const PropConfig = defineComponent({
props: {
@ -92,11 +93,11 @@ export const PropConfig = defineComponent({
children: 'entitys',
label: 'name',
value: 'key',
expandTrigger: 'hover',
expandTrigger: ExpandTrigger.HOVER,
}}
placeholder="请选择绑定的请求数据"
v-model={propObj[prop]}
options={models.value}
options={[...models.value]}
></ElCascader>
),
}[propConfig.type]();
@ -126,9 +127,11 @@ export const PropConfig = defineComponent({
popper-class="max-w-200px"
content={propConfig.tips}
>
<div>
<ElIcon>
<Warning />
</ElIcon>
</div>
</ElTooltip>
)}
{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 { defer } from '@/visual-editor/utils/defer';
import { ElButton, ElDialog, ElTable, ElTableColumn, ElInput } from 'element-plus';
import { cloneDeep } from 'lodash-es';
import { defer } from '@/visual-editor/utils/defer';
import { VisualEditorProps } from '@/visual-editor/visual-editor.props';
export interface TablePropEditorServiceOption {
data: any[];
@ -66,7 +66,7 @@ const ServiceComponent = defineComponent({
return () => (
<>
<ElDialog v-model={state.showFlag}>
<ElDialog modelValue={state.showFlag}>
{{
default: () => (
<div>

View File

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

View File

@ -8,10 +8,10 @@
*/
import { defineComponent, computed, watch } from 'vue';
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 { 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({
setup() {
@ -75,8 +75,8 @@ export const AttrEditor = defineComponent({
</ElFormItem>
</>,
);
if (!!component) {
if (!!component.props) {
if (component) {
if (component.props) {
content.push(<PropConfig component={component} block={currentBlock.value} />);
{
currentBlock.value.showStyleConfig &&

View File

@ -1,13 +1,12 @@
/*
* @Author:
* @Date: 2021-06-24 11:01:45
* @LastEditTime: 2021-07-08 09:53:27
* @LastEditTime: 2022-07-02 18:29:25
* @LastEditors:
* @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 { useVisualData } from '@/visual-editor/hooks/useVisualData';
import {
ElForm,
ElFormItem,
@ -21,10 +20,11 @@ import {
ElCollapseItem,
ElPopconfirm,
} from 'element-plus';
import { cloneDeep } from 'lodash-es';
import type { Action } from '@/visual-editor/visual-editor.utils';
import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import { generateNanoid } from '@/visual-editor/utils/';
import { useModal } from '@/visual-editor/hooks/useModal';
import { cloneDeep } from 'lodash-es';
interface IState {
activeNames: string[];
@ -92,7 +92,6 @@ export const EventAction = defineComponent({
.filter((item) => item.actions?.length)
.map((item) => {
item.value = item._vid;
item.label = item.label;
item.children = (item.actions || []).map((item: any) => {
item.label = item.name;
item.value = item.key;

View File

@ -1,10 +1,10 @@
/*
* @Author:
* @Date: 2021-07-05 10:51:09
* @LastEditTime: 2021-07-08 23:20:17
* @LastEditTime: 2022-07-02 22:46:59
* @LastEditors:
* @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 { ElCard, ElTooltip } from 'element-plus';
@ -29,7 +29,7 @@ export const FormRule = defineComponent({
default: () => <div></div>,
}}
</ElCard>
<ElCard shadow={'always'} bodyStyle={{ padding: 1 ? '0' : '20px' }} class={'mb-20px'}>
<ElCard shadow={'always'} bodyStyle={{ padding: '0' }} class={'mb-20px'}>
{{
header: () => (
<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
*/
export { AttrEditor } from './attr-editor'
export { Animate } from './animate/Animate'
export { PageSetting } from './page-setting/pageSetting'
export { EventAction } from './event-action/'
export { FormRule } from './form-rule/'
export { AttrEditor } from './attr-editor';
export { Animate } from './animate/Animate';
export { PageSetting } from './page-setting/pageSetting';
export { EventAction } from './event-action/';
export { FormRule } from './form-rule/';

View File

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

View File

@ -9,11 +9,11 @@
*/
import { defineComponent, reactive, watch } from 'vue';
import styles from './index.module.scss';
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 styles from './index.module.scss';
import { AttrEditor, Animate, PageSetting, EventAction, FormRule } from './components';
import { useVisualData } from '@/visual-editor/hooks/useVisualData';
export default defineComponent({
name: 'RightAttributePanel',

View File

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

View File

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

View File

@ -55,13 +55,13 @@
<script lang="tsx">
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 { $$dropdown, DropdownOption } from '@/visual-editor/utils/dropdown-service';
import CompRender from './comp-render';
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 { cloneDeep } from 'lodash-es';
import { useGlobalProperties } from '@/hooks/useGlobalProperties';
import { useVisualData } from '@/visual-editor/hooks/useVisualData';
import { useModal } from '@/visual-editor/hooks/useModal';

View File

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

View File

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

View File

@ -12,58 +12,58 @@ import {
PropType,
getCurrentInstance,
ComponentInternalInstance,
isVNode
} from 'vue'
import { ElButton, ElDialog } from 'element-plus'
import { isFunction } from '@/visual-editor/utils/is'
isVNode,
} from 'vue';
import { ElButton, ElDialog } from 'element-plus';
import { isFunction } from '@/visual-editor/utils/is';
interface ModalOptions {
title?: string
footer?: null | (() => JSX.Element)
content: ComponentInternalInstance | (() => JSX.Element)
onConfirm?: () => void
onCancel?: () => void
title?: string;
footer?: null | (() => JSX.Element);
content: ComponentInternalInstance | (() => JSX.Element);
onConfirm?: () => void;
onCancel?: () => void;
props?: {
[propName: string]: any
}
[propName: string]: any;
};
}
const Modal = defineComponent({
props: {
options: {
type: Object as PropType<ModalOptions>,
default: () => ({})
}
default: () => ({}),
},
},
setup(props) {
const instance = getCurrentInstance()!
const instance = getCurrentInstance()!;
const state = reactive({
options: props.options,
visible: true
})
visible: true,
});
const methods = {
service: (options: ModalOptions) => {
state.options = options
methods.show()
state.options = options;
methods.show();
},
show: () => (state.visible = true),
hide: () => (state.visible = false)
}
hide: () => (state.visible = false),
};
const handler = {
onConfirm: async () => {
await state.options.onConfirm?.()
methods.hide()
await state.options.onConfirm?.();
methods.hide();
},
onCancel: () => {
state.options.onCancel?.()
methods.hide()
}
}
state.options.onCancel?.();
methods.hide();
},
};
Object.assign(instance.proxy, methods)
Object.assign(instance.proxy!, methods);
return () => (
<ElDialog
@ -88,24 +88,24 @@ const Modal = defineComponent({
</ElButton>
</div>
)
),
}}
</ElDialog>
)
}
})
);
},
});
export const useModal = (() => {
let instance: any
let instance: any;
return (options: ModalOptions) => {
if (instance) {
instance.service(options)
return instance
instance.service(options);
return instance;
}
const div = document.createElement('div')
document.body.appendChild(div)
const app = createApp(Modal, { options })
instance = app.mount(div)
return instance
}
})()
const div = document.createElement('div');
document.body.appendChild(div);
const app = createApp(Modal, { options });
instance = app.mount(div);
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) {
const state = ref(getter()) as { value: T }
const state = ref(getter()) as { value: T };
watch(getter, (val) => {
if (val !== state.value) {
state.value = val
state.value = val;
}
})
});
return {
get value() {
return state.value
return state.value;
},
set value(val: T) {
if (state.value !== val) {
state.value = val
emitter(val)
}
}
state.value = val;
emitter(val);
}
},
};
}
export const TestUseModel = defineComponent({
props: {
modelValue: { type: String }
modelValue: { type: String },
},
emits: ['update:modelValue'],
setup(props, ctx) {
const model = useModel(
() => props.modelValue,
(val) => ctx.emit('update:modelValue', val)
)
(val) => ctx.emit('update:modelValue', val),
);
return () => (
<div>
<input type="text" v-model={model.value} />
</div>
)
}
})
);
},
});

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