no message
|
|
@ -0,0 +1,6 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
tab_width = 2
|
||||
indent_style = space
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# 网站标题
|
||||
VITE_APP_TITLE='OpenDataV'
|
||||
|
||||
# 端口
|
||||
VITE_APP_PORT=8890
|
||||
|
||||
#VITE_APP_PROXY 和 VITE_APP_BASE_URL 的关系
|
||||
#如果 VITE_APP_BASE_URL 配置为空,所有请求就通过 VITE_APP_PROXY 代理转发
|
||||
#如果 VITE_APP_BASE_URL 配置不为空,VITE_APP_PROXY 的配置就不生效
|
||||
# 代理配置,必须在一行中
|
||||
VITE_APP_PROXY=[["/api","http://localhost:9900"]]
|
||||
|
||||
# 后端地址
|
||||
VITE_APP_BASE_URL=''
|
||||
|
||||
# 路由模式:hash 或者 history
|
||||
VITE_ROUTER_MODE='hash'
|
||||
|
||||
# Token 键
|
||||
VITE_AUTH_TOKEN='Token'
|
||||
|
||||
# Token 的存储:TokenStorage
|
||||
VITE_TOKEN_STORAGE='localStorage'
|
||||
|
||||
# 报警通知延时,单位:毫秒
|
||||
VITE_ALERT_DELAY=300
|
||||
|
||||
VITE_MOCK=true
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# 网站标题
|
||||
VITE_APP_TITLE='OpenDataV'
|
||||
|
||||
# 端口
|
||||
VITE_APP_PORT=8890
|
||||
|
||||
# 代理配置(开发使用),必须在一行中
|
||||
VITE_APP_PROXY=[["/api","http://localhost:8000"]]
|
||||
|
||||
# 后端地址
|
||||
VITE_APP_BASE_URL='/'
|
||||
|
||||
# 路由模式:hash 或者 history
|
||||
VITE_ROUTER_MODE='hash'
|
||||
|
||||
# Token 键
|
||||
VITE_AUTH_TOKEN='Token'
|
||||
|
||||
# Token 的存储:TokenStorage
|
||||
VITE_TOKEN_STORAGE='localStorage'
|
||||
|
||||
# 网站是否不需要权限认证: NoAuth Auth
|
||||
VITE_APP_NO_AUTH='NoAuth'
|
||||
|
||||
VITE_BACKIMAGE=''
|
||||
|
||||
VITE_MOCK=true
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# 网站标题
|
||||
VITE_APP_TITLE='OpenDataV'
|
||||
|
||||
# 端口
|
||||
VITE_APP_PORT=8890
|
||||
|
||||
# 代理配置,必须在一行中
|
||||
VITE_APP_PROXY=[["/api","http://localhost:9900"]]
|
||||
|
||||
# 后端地址
|
||||
VITE_APP_BASE_URL='/'
|
||||
|
||||
# 路由模式:hash 或者 history
|
||||
VITE_ROUTER_MODE='hash'
|
||||
|
||||
# Token 键
|
||||
VITE_AUTH_TOKEN='Token'
|
||||
|
||||
# Token 的存储:TokenStorage
|
||||
VITE_TOKEN_STORAGE='localStorage'
|
||||
|
||||
# 网站是否不需要权限认证: NoAuth Auth
|
||||
VITE_APP_NO_AUTH='NoAuth'
|
||||
|
||||
VITE_BACKGROUND=''
|
||||
|
||||
VITE_MOCK=true
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/src/assets/*
|
||||
**/node_modules/**
|
||||
.vscode
|
||||
.idea
|
||||
public
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
const { defineConfig } = require('eslint-define-config')
|
||||
|
||||
module.exports = defineConfig?.({
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
'vue/setup-compiler-macros': true
|
||||
},
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser',
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
jsxPragma: 'React',
|
||||
ecmaFeatures: {
|
||||
jsx: true
|
||||
}
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/vue3-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
'plugin:prettier/recommended'
|
||||
// 'plugin:jest/recommended'
|
||||
],
|
||||
plugins: ['prettier', 'import', '@typescript-eslint', 'spellcheck', 'simple-import-sort'],
|
||||
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',
|
||||
'vue/no-v-html': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@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-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_'
|
||||
}
|
||||
],
|
||||
'no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_'
|
||||
}
|
||||
],
|
||||
'space-before-function-paren': '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/require-default-prop': 'off',
|
||||
'vue/require-explicit-emits': 'off',
|
||||
'vue/html-self-closing': [
|
||||
'error',
|
||||
{
|
||||
html: {
|
||||
void: 'always',
|
||||
normal: 'never',
|
||||
component: 'always'
|
||||
},
|
||||
svg: 'always',
|
||||
math: 'always'
|
||||
}
|
||||
],
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'sonarjs/no-duplicate-string': 'off',
|
||||
'sonarjs/cognitive-complexity': 'off',
|
||||
'sonarjs/no-identical-functions': 'off',
|
||||
'@typescript-eslint/no-this-alias': [
|
||||
'error',
|
||||
{
|
||||
allowDestructuring: false, // Disallow `const { props, state } = this`; true by default
|
||||
allowedNames: ['self'] // Allow `const self = this`; `[]` by default
|
||||
}
|
||||
],
|
||||
/**
|
||||
* 属性排序
|
||||
*
|
||||
* @see https://eslint.vuejs.org/rules/attributes-order.html#rule-details
|
||||
*/
|
||||
'vue/attributes-order': 1,
|
||||
/**
|
||||
* 仅仅导入/导出声明
|
||||
*
|
||||
* @see https://jkchao.github.io/typescript-book-chinese/new/typescript-3.8.html#%E4%BB%85%E4%BB%85%E5%AF%BC%E5%85%A5-%E5%AF%BC%E5%87%BA%E5%A3%B0%E6%98%8E
|
||||
*/
|
||||
'@typescript-eslint/consistent-type-imports': 2,
|
||||
|
||||
/**
|
||||
* 强制使用一致的换行符风格
|
||||
*
|
||||
* @see https://cn.eslint.org/docs/rules/linebreak-style
|
||||
*/
|
||||
'linebreak-style': [2, 'unix'],
|
||||
'simple-import-sort/imports': 'error',
|
||||
'simple-import-sort/exports': 'error',
|
||||
'import/first': 'error',
|
||||
'import/newline-after-import': 'error',
|
||||
'import/no-duplicates': 'error',
|
||||
'vue/no-setup-props-destructure': 'off'
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
node_modules/
|
||||
dist/
|
||||
.idea/
|
||||
node_modules
|
||||
package-lock.json
|
||||
components.d.ts
|
||||
.DS_Store
|
||||
dist
|
||||
.idea
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
# Format and submit code according to lintstagedrc.js configuration
|
||||
pnpm lint
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"stylelint.vscode-stylelint",
|
||||
"johnsoncodehk.volar"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
// 使用 IntelliSense 了解相关属性。
|
||||
// 悬停以查看现有属性的描述。
|
||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"type": "pwa-chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against localhost",
|
||||
"url": "http://localhost:8080",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"css.validate": false,
|
||||
"less.validate": false,
|
||||
"sass.validate": false,
|
||||
"editor.tabSize": 2,
|
||||
"javascript.validate.enable": false,
|
||||
"tailwindCSS.includeLanguages": {
|
||||
"wxml": "html"
|
||||
},
|
||||
"prettier.trailingComma": "none",
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "stylelint.vscode-stylelint"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/*.log": true,
|
||||
"**/*.log*": true,
|
||||
"**/dist": true,
|
||||
"**/.git": true,
|
||||
"**/.gitignore": true,
|
||||
"**/.svn": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/.idea": true,
|
||||
"**/.vscode": false,
|
||||
"**/yarn.lock": true,
|
||||
"**/tmp": true,
|
||||
"out": true,
|
||||
"dist": true,
|
||||
"node_modules": true,
|
||||
"yarn-error.log": true,
|
||||
"**/.yarn": true
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2022 AnsGoo
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
@ -0,0 +1,131 @@
|
|||

|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## 简介
|
||||
🎃OpenDataV 是一个纯前端的`拖拽式`、`可视化`、`低代码`数据可视化🌈开发平台,你可以用它自由的拼接成各种✨炫酷的大屏,同时支持用户方便的开发自己的组件并接入平台。
|
||||
|
||||
## 体验
|
||||
🧙国外:[http://datav.byteportrait.com/](http://datav.byteportrait.com/)
|
||||
|
||||
🧙国内:[http://small_bud_star.gitee.io/opendatav](http://small_bud_star.gitee.io/opendatav)
|
||||
|
||||
🧙国内:[http://opendatav.xingxingzaixian.fun/](http://opendatav.xingxingzaixian.fun/) (已对接后端)
|
||||
|
||||
🧙文档: [https://datav.byteportrait.com/#/docs/quick-satrt/quick-start](https://datav.byteportrait.com/#/docs/quick-satrt/quick-start)
|
||||
|
||||
|
||||
## 源码地址:
|
||||
|
||||
🍨github: [https://github.com/AnsGoo/openDataV](https://github.com/AnsGoo/openDataV)
|
||||
|
||||
🍨gitee: [https://gitee.com/small_bud_star/OpenDataV](https://gitee.com/small_bud_star/OpenDataV)
|
||||
|
||||
🍨后端: [https://gitee.com/small_bud_star/open-data-backend](https://gitee.com/small_bud_star/open-data-backend)
|
||||
|
||||
|
||||
目前该项目在不断的完善中,🎉欢迎 issuer,🌹欢迎 start, 🎨欢迎 commit, 🚀欢迎 use...,💪欢迎一切技术交流活动
|
||||
|
||||
## 👁️预览
|
||||
|
||||
### 🤿 页面编辑
|
||||
|
||||
1. 组件添加
|
||||

|
||||
|
||||
2. 组件操作
|
||||

|
||||
|
||||
3. 组件数据配置
|
||||

|
||||
|
||||
### 🖇️ 接口管理
|
||||
|
||||

|
||||
|
||||
|
||||
## 💒功能
|
||||
- 🎊 编辑器页面基本功能完成,包括编辑、预览、导入、导出、保存
|
||||
- 🪄 图层的置顶、置底、上下移动、显示、隐藏、复制、剪切、粘贴
|
||||
- 🖼️ 组件的缩放、旋转、拖动、复制、粘贴、组合、拆分、移除、自动对齐
|
||||
- 🔮 支持用户操作记录的恢复、撤销功能
|
||||
- 🧶 支持用户自定义组件
|
||||
- 📔 支持组件的用户自定组件配置项
|
||||
- 🏪 支持明暗主题切换
|
||||
- 🧬 使用Monorepo模式进行组件和依赖管理
|
||||
- 🧶 组件数据自定义接入
|
||||
- 🔌 数据动态处理(JS已完成)
|
||||
- 🖇️ 接口管理(示例数据、静态数据、HTTP接口数据已完成适配)
|
||||
|
||||
## 🎢技术点
|
||||
本项目采用`Vue3` + `vite` + `TypeScript`开发,界面库使用`NaiveUI`,使用面向对象方式封装了路由、请求、存储,组件采用自动扫描注册、异步加载,提升渲染速度;使用IndexDB存储快照数据,减少快照数据内存占用,加快访问速度;组件独立依赖,解耦了组件和基础框架的依赖库,方便后续独立开发组件。
|
||||
|
||||
目前仅开发了部分组件,后续还会继续完善。
|
||||
|
||||
## ⌛计划功能
|
||||
- [ ] 组件管理
|
||||
- [ ] 脚本管理(Python待支持)
|
||||
- [ ] GraphQL、GRPC、WebScoket、MQTT、SocketIO多种数据接口适配
|
||||
- [ ] 代码生成
|
||||
- [ ] 动态文档
|
||||
- [ ] 全局数据接入
|
||||
|
||||
## 💂开发
|
||||
|
||||
### 🧊开发环境
|
||||
| 名称 | 版本 |
|
||||
| ---- | ------- |
|
||||
| node | 16.14.x |
|
||||
| pnpm | 7.9.3 |
|
||||
| vue | 3.2.37 |
|
||||
|
||||
🚥目前仅在 `Chrome` 和 `Microsoft Edge` 最新版浏览器测试过,其他浏览器未测试
|
||||
|
||||
### 🎮启动项目
|
||||
|
||||
```Bash
|
||||
|
||||
# 安装依赖
|
||||
pnpm install
|
||||
或
|
||||
pnpm bootstrap
|
||||
|
||||
# 运行项目
|
||||
pnpm dev
|
||||
|
||||
# 打包项目
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## 🧑💻代码提交
|
||||
|
||||
git commit 信息请按照如下规范进行书写
|
||||
- feat: 新功能
|
||||
- fix: 修复 Bug
|
||||
- docs: 文档修改
|
||||
- perf: 性能优化
|
||||
- revert: 版本回退
|
||||
- ci: CICD 集成相关
|
||||
- test: 添加测试代码
|
||||
- refactor: 代码重构
|
||||
- build: 影响项目构建或依赖修改
|
||||
- style: 不影响程序逻辑的代码修改
|
||||
- other: 不属于以上类型的其他类型(日常事务)
|
||||
|
||||
## ☎️联系方式
|
||||
|
||||
**技术交流,请加微信群**
|
||||
|
||||
<img src="./public/wechat.png" style="width:430px">
|
||||
|
||||
**更新动态请关注公众号**
|
||||
|
||||

|
||||
|
||||
**如果群链接失效请加微信,回复:OpenDataV 拉群**
|
||||
|
||||

|
||||
|
|
@ -0,0 +1,128 @@
|
|||
// Process @[toc](|Title)
|
||||
|
||||
'use strict'
|
||||
|
||||
module.exports = function (md) {
|
||||
const TOC_REGEXP = /\[toc\](?:\((?:\s+)?([^\)]+)(?:\s+)?\)?)?(?:\s+?)?$/im
|
||||
let gstate
|
||||
|
||||
function toc(state, silent) {
|
||||
while (state.src.indexOf('\n') >= 0 && state.src.indexOf('\n') < state.src.indexOf('[toc]')) {
|
||||
if (state.tokens.slice(-1)[0].type === 'softbreak') {
|
||||
state.src = state.src.split('\n').slice(1).join('\n')
|
||||
state.pos = 0
|
||||
}
|
||||
}
|
||||
let token
|
||||
let match = TOC_REGEXP.exec(state.src)
|
||||
if (!match) {
|
||||
return false
|
||||
}
|
||||
match = match.filter(function (m) {
|
||||
return m
|
||||
})
|
||||
if (match.length < 1) {
|
||||
return false
|
||||
}
|
||||
if (silent) {
|
||||
// don't run any pairs in validation mode
|
||||
return false
|
||||
}
|
||||
|
||||
token = state.push('toc_open', 'toc', 1)
|
||||
token.markup = '[toc]'
|
||||
|
||||
token = state.push('toc_body', '', 0)
|
||||
|
||||
token = state.push('toc_close', 'toc', -1)
|
||||
|
||||
let offset = 0
|
||||
let newline = state.src.indexOf('\n')
|
||||
if (newline !== -1) {
|
||||
offset = state.pos + newline
|
||||
} else {
|
||||
offset = state.pos + state.posMax + 1
|
||||
}
|
||||
state.pos = offset
|
||||
|
||||
return true
|
||||
}
|
||||
const makeSafe = function (label) {
|
||||
return label
|
||||
.replace(/[^\w\s]/gi, '')
|
||||
.split(' ')
|
||||
.join('_')
|
||||
}
|
||||
|
||||
md.renderer.rules.heading_open = function (tokens, index) {
|
||||
const level = tokens[index].tag
|
||||
const label = tokens[index + 1]
|
||||
if (label.type === 'inline') {
|
||||
const anchor = makeSafe(label.content) + '_' + label.map[0]
|
||||
return `<${level}><a id="${anchor}"></a>`
|
||||
} else {
|
||||
return '</h1>'
|
||||
}
|
||||
}
|
||||
|
||||
md.renderer.rules.toc_open = function (_tokens, _index) {
|
||||
return ''
|
||||
}
|
||||
|
||||
md.renderer.rules.toc_close = function (_tokens, _index) {
|
||||
return ''
|
||||
}
|
||||
|
||||
md.renderer.rules.toc_body = function (_tokens, _index) {
|
||||
// Wanted to avoid linear search through tokens here,
|
||||
// but this seems the only reliable way to identify headings
|
||||
const headings = []
|
||||
const gtokens = gstate.tokens
|
||||
const size = gtokens.length
|
||||
for (let i = 0; i < size; i++) {
|
||||
if (gtokens[i].type !== 'heading_close') {
|
||||
continue
|
||||
}
|
||||
const token = gtokens[i]
|
||||
const heading = gtokens[i - 1]
|
||||
if (heading.type === 'inline') {
|
||||
headings.push({
|
||||
level: +token.tag.substr(1, 1),
|
||||
anchor: makeSafe(heading.content) + '_' + heading.map[0],
|
||||
content: heading.content
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let indent = 0
|
||||
const list = headings.map(function (heading) {
|
||||
let res = []
|
||||
if (heading.level > indent) {
|
||||
const ldiff = heading.level - indent
|
||||
for (let i = 0; i < ldiff; i++) {
|
||||
res.push('<ul>')
|
||||
indent++
|
||||
}
|
||||
} else if (heading.level < indent) {
|
||||
const ldiff = indent - heading.level
|
||||
for (let i = 0; i < ldiff; i++) {
|
||||
res.push('</ul>')
|
||||
indent--
|
||||
}
|
||||
}
|
||||
|
||||
res.push(
|
||||
`<li><a href="javascript:document.querySelector('#${heading.anchor}').scrollIntoView({behavior: 'smooth'})">${heading.content}</a></li>`
|
||||
)
|
||||
return res.join('')
|
||||
})
|
||||
|
||||
return `<div class="toc-box">${list.join('') + new Array(indent + 1).join('</ul>')}</div>`
|
||||
}
|
||||
|
||||
md.core.ruler.push('grab_state', function (state) {
|
||||
gstate = state
|
||||
})
|
||||
|
||||
md.inline.ruler.after('emphasis', 'toc', toc)
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<template>
|
||||
<n-layout-sider
|
||||
class="right"
|
||||
:width="180"
|
||||
:native-scrollbar="false"
|
||||
bordered
|
||||
collapse-mode="width"
|
||||
>
|
||||
<SiderContent :menus="componentMenus" />
|
||||
</n-layout-sider>
|
||||
<n-layout-content class="content">
|
||||
<n-scrollbar x-scrollable>
|
||||
<RenderMD>
|
||||
<ConfigProvider>
|
||||
<RouterView />
|
||||
</ConfigProvider>
|
||||
</RenderMD>
|
||||
</n-scrollbar>
|
||||
</n-layout-content>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="DocContent">
|
||||
import { NLayoutContent, NLayoutSider, NScrollbar } from 'naive-ui'
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
import ConfigProvider from '@/components/provider/ConfigProvider.vue'
|
||||
import docsRouters from '@/router/modules/docs'
|
||||
import type { AppRouteRecordRaw } from '@/router/types'
|
||||
|
||||
import SiderContent from '../modules/components/SiderContent'
|
||||
import type { MenuItem } from '../modules/components/SiderContent/type'
|
||||
import RenderMD from '../RenderMD.vue'
|
||||
import { getMenus } from './menus'
|
||||
|
||||
const route = useRoute()
|
||||
const componentMenus = computed<Array<MenuItem>>(() => {
|
||||
const matcheds = route.matched
|
||||
const name = matcheds[1].name
|
||||
const routers = docsRouters[0].children.filter((el) => el.name === name)
|
||||
if (routers.length > 0 && routers[0].children && routers[0].children.length > 0) {
|
||||
return getMenus(routers[0].children as Array<AppRouteRecordRaw>)
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<n-result
|
||||
status="404"
|
||||
title="文档正在编写中..."
|
||||
description="star一下,来为OpenDataV加速吧"
|
||||
style="height: 100%"
|
||||
>
|
||||
<template #icon>
|
||||
<img src="/errors/404.png" />
|
||||
</template>
|
||||
</n-result>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { NResult } from 'naive-ui'
|
||||
</script>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import Content from './Content.vue'
|
||||
|
||||
export default Content
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import type { AppRouteRecordRaw } from '@/router/types'
|
||||
|
||||
import type { MenuItem } from '../modules/components/SiderContent'
|
||||
|
||||
export const getMenus = (routers: AppRouteRecordRaw[]): Array<MenuItem> => {
|
||||
return routers.map((el) => {
|
||||
const item = {
|
||||
label: el.meta.title,
|
||||
icon: el.meta.icon!,
|
||||
key: el.name,
|
||||
children: [] as Array<MenuItem>
|
||||
}
|
||||
if (el.children && el.children?.length > 0) {
|
||||
item.children = getMenus(el.children || [])
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
<template>
|
||||
<n-card v-if="mode === 'debug'" :title="title" style="margin-bottom: 16px; height: 100%">
|
||||
<n-tabs type="line" animated>
|
||||
<n-tab-pane name="oasis" tab="效果" display-directive="show">
|
||||
<Render />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="attr" tab="属性" display-directive="show">
|
||||
<OCodeEditor
|
||||
v-model:value="form.propValue"
|
||||
class="content"
|
||||
mode="debug"
|
||||
height="300px"
|
||||
@update:value="propValueChange"
|
||||
/>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="style" tab="样式" display-directive="show">
|
||||
<OCodeEditor
|
||||
v-model:value="form.style"
|
||||
class="content"
|
||||
mode="debug"
|
||||
height="300px"
|
||||
@update:value="styleChange"
|
||||
/>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
<n-card v-else :title="title" style="margin-bottom: 16px; height: 100%">
|
||||
<Render />
|
||||
</n-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { NCard, NTabPane, NTabs } from 'naive-ui'
|
||||
import type { CustomComponent } from 'open-data-v/base'
|
||||
import { getComponentStyle, uuid } from 'open-data-v/designer/utils'
|
||||
import type { ComponentOptions, ConcreteComponent } from 'vue'
|
||||
import { computed, h, reactive } from 'vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
config: new (id?: string, name?: string) => CustomComponent
|
||||
component: ConcreteComponent
|
||||
propValue: Record<string, any>
|
||||
style: Record<string, any>
|
||||
title: string
|
||||
mode?: 'view' | 'debug' | 'use'
|
||||
}>(),
|
||||
{
|
||||
mode: 'debug'
|
||||
}
|
||||
)
|
||||
const form = reactive<{
|
||||
propValue: string
|
||||
style: string
|
||||
}>({
|
||||
propValue: JSON.stringify(props.propValue, null, '\t'),
|
||||
style: JSON.stringify(props.style, null, '\t')
|
||||
})
|
||||
|
||||
const propValueChange = (value: string) => {
|
||||
form.propValue = value
|
||||
}
|
||||
const styleChange = (style: string) => {
|
||||
form.style = style
|
||||
}
|
||||
|
||||
const Render = computed<ComponentOptions>(() => {
|
||||
const componentInstance = new props.config(uuid())
|
||||
const style = JSON.parse(form.style)
|
||||
const propValue = JSON.parse(form.propValue)
|
||||
componentInstance.setStyleValue({ style: style })
|
||||
componentInstance.setPropValue({ propValue: propValue })
|
||||
return h(props.component, {
|
||||
id: componentInstance.id,
|
||||
key: componentInstance.id,
|
||||
class: 'component',
|
||||
style: getComponentStyle(componentInstance),
|
||||
component: componentInstance
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<template>
|
||||
<n-card>
|
||||
<slot></slot>
|
||||
</n-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import '@/css/markdown.css'
|
||||
|
||||
import { NCard } from 'naive-ui'
|
||||
import type { ComputedRef } from 'vue'
|
||||
import { computed, inject } from 'vue'
|
||||
|
||||
const darkTheme = inject<ComputedRef<boolean>>(
|
||||
'DarkTheme',
|
||||
computed(() => true)
|
||||
)
|
||||
const backgroundColor = computed<string>(() => {
|
||||
return darkTheme.value ? '#101014' : '#ffffff'
|
||||
})
|
||||
const textColor = computed<string>(() => {
|
||||
return darkTheme.value ? '#ffffffd1' : '#333639'
|
||||
})
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.markdown-body) {
|
||||
// max-width: max(80%, 70vw);
|
||||
margin-right: 200px;
|
||||
border-right: 1px solid;
|
||||
.toc-box {
|
||||
position: fixed;
|
||||
right: 0px;
|
||||
width: max(15%, 200px);
|
||||
min-width: 200px;
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
}
|
||||
body {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
ul {
|
||||
li {
|
||||
list-style-type: disc;
|
||||
}
|
||||
}
|
||||
ol {
|
||||
li {
|
||||
list-style: decimal;
|
||||
}
|
||||
}
|
||||
padding: 10px 30px;
|
||||
background-color: v-bind(backgroundColor) !important;
|
||||
img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
background-color: v-bind(backgroundColor);
|
||||
}
|
||||
table {
|
||||
tr {
|
||||
background-color: v-bind(backgroundColor) !important;
|
||||
color: v-bind(textColor);
|
||||
}
|
||||
}
|
||||
* {
|
||||
color: v-bind(textColor);
|
||||
}
|
||||
code {
|
||||
color: #ffffffd1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<template>
|
||||
<n-layout position="absolute">
|
||||
<n-layout-header style="height: 60px"><ToolBar /></n-layout-header>
|
||||
<n-layout has-sider sider-placement="left" style="height: calc(99vh - 60px); overflow: hidden">
|
||||
<RouterView />
|
||||
<n-layout-footer position="absolute"
|
||||
><div class="copyleft">
|
||||
<div>OpenDataV · Made by AnsGoo</div>
|
||||
</div></n-layout-footer
|
||||
>
|
||||
</n-layout>
|
||||
</n-layout>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import '@/css/markdown.css'
|
||||
|
||||
import { NLayout, NLayoutFooter, NLayoutHeader } from 'naive-ui'
|
||||
|
||||
import ToolBar from './modules/ToolBar'
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
:deep(.n-layout .n-layout-scroll-container) {
|
||||
overflow-x: initial;
|
||||
}
|
||||
.copyleft {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<ToolBar :bars="toolBars" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import type { ToolBarItemType } from '@/components/ToolBar'
|
||||
import { ToolBar } from '@/components/ToolBar'
|
||||
import docsRouters from '@/router/modules/docs'
|
||||
|
||||
import ActionDetail from './modules/ActionDetail.vue'
|
||||
import { toggleTheme } from './modules/actions'
|
||||
import Logo from './modules/Logo.vue'
|
||||
import ThemeIcon from './modules/themeSwitch/ThemeIcon.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const emits = defineEmits<{
|
||||
(e: 'change', value: string): void
|
||||
}>()
|
||||
|
||||
const leftBars: ToolBarItemType[] = docsRouters[0].children.map((el) => {
|
||||
return {
|
||||
label: el.meta.title,
|
||||
action: async () => {
|
||||
emits('change', 'help')
|
||||
await router.push({
|
||||
name: el.name
|
||||
})
|
||||
},
|
||||
icon: () =>
|
||||
h(ActionDetail, {
|
||||
icon: el.meta.icon,
|
||||
label: el.meta.title
|
||||
}),
|
||||
location: 'left'
|
||||
}
|
||||
})
|
||||
const toolBars: ToolBarItemType[] = [
|
||||
{
|
||||
label: '首页',
|
||||
action: async (_e: MouseEvent) => {
|
||||
await router.push({
|
||||
name: 'Pages'
|
||||
})
|
||||
},
|
||||
icon: () => h(Logo),
|
||||
divider: true,
|
||||
location: 'left'
|
||||
},
|
||||
...leftBars,
|
||||
{
|
||||
label: 'GitHub',
|
||||
action: () => {
|
||||
window.open('https://github.com/AnsGoo/openDataV', '_blank')
|
||||
},
|
||||
icon: 'github',
|
||||
location: 'right'
|
||||
},
|
||||
{
|
||||
label: '主题',
|
||||
action: toggleTheme,
|
||||
icon: () => h(ThemeIcon),
|
||||
location: 'right'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.tool-bar-title {
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import ToolBar from './Toolbar.vue'
|
||||
|
||||
export default ToolBar
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<x-icon :name="icon" :size="24" /><span>{{ label }}</span>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
defineProps<{
|
||||
icon: string
|
||||
label: string
|
||||
}>()
|
||||
</script>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<template><LogoView width="65px" /><span class="title">OpenDataV</span></template>
|
||||
<script lang="ts" setup>
|
||||
import LogoView from '@/components/LogoView'
|
||||
</script>
|
||||
<style scoped>
|
||||
.title {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.75rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { useProjectSettingStoreWithOut } from '@/store/modules/projectSetting'
|
||||
|
||||
// 状态管理
|
||||
const projectStore = useProjectSettingStoreWithOut()
|
||||
|
||||
const toggleTheme = () => {
|
||||
projectStore.setNavTheme(!projectStore.darkTheme ? 'light' : 'dark')
|
||||
projectStore.setDarkTheme(!projectStore.darkTheme)
|
||||
}
|
||||
|
||||
export { toggleTheme }
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<x-icon :name="themeIcon" :size="24" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { ComputedRef } from 'vue'
|
||||
import { computed, inject } from 'vue'
|
||||
|
||||
const darkTheme = inject<ComputedRef<boolean>>(
|
||||
'DarkTheme',
|
||||
computed(() => true)
|
||||
)
|
||||
const themeIcon = computed<string>(() => (darkTheme.value ? 'sun' : 'moon'))
|
||||
</script>
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<template>
|
||||
<div v-memo="menus" class="components">
|
||||
<n-ul v-for="item in menus" :key="item.key">
|
||||
<div class="group-label group-item" @click="goTo(item.key)">
|
||||
<x-icon :name="item.icon" :size="20" />
|
||||
<span> {{ item.label }}</span>
|
||||
</div>
|
||||
<n-li v-for="child in item.children || []" :key="child.key" @click="goTo(child.key)">
|
||||
<div class="sub-group-item group-item">{{ child.label }}</div>
|
||||
</n-li>
|
||||
</n-ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { NLi, NUl } from 'naive-ui'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import type { MenuItem } from './type'
|
||||
|
||||
const router = useRouter()
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
menus: MenuItem[]
|
||||
}>(),
|
||||
{
|
||||
menus: () => []
|
||||
}
|
||||
)
|
||||
|
||||
const goTo = async (key: string) => {
|
||||
await router.push({
|
||||
name: key
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.group-label {
|
||||
font-size: 18px;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.sub-group-item {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.group-item {
|
||||
&:hover {
|
||||
transform: scale(1.01);
|
||||
color: #2d8cf0;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import Content from './Content.vue'
|
||||
import type { MenuItem } from './type'
|
||||
|
||||
export default Content
|
||||
export { MenuItem }
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
export interface MenuItem {
|
||||
label: string
|
||||
icon: string
|
||||
key: string
|
||||
children: Array<{
|
||||
label: string
|
||||
key: string
|
||||
}>
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
[toc]
|
||||
|
||||
|
||||
# 为什么我不能保存我的页面?
|
||||
|
||||
openDataV 是一款基于Vue3的纯前端拖拽式低代码平台,默认所有的接口都是Mock接口,因此你不能进行任何写操作,因此保存更新之类的操作是无效果的;因为时间和精力不足,我们开发了一个简易的后台服务[https://gitee.com/small_bud_star/open-data-backend](https://gitee.com/small_bud_star/open-data-backend),便于大家使用,但是并不适合在生产环境中使用
|
||||
|
||||
我们也打算开发一个高性能的后台服务,计划采用Spring套装或者Go语言,感兴趣的大佬可以加入我们一起开发
|
||||
|
||||
# 为什么没有XXXX组件?
|
||||
|
||||
我们对OpenDataV的定位更多是一个平台性质或者框架性质,所以自带的组件更多是示例功能。
|
||||
|
||||
我们在实际应用中发现,组件的通用性和复杂度成反比,如果一个组件通用性很强,那么它的复杂度就很高,用户的的学习成本就会很大,这样就失去了低代码的意义;既然做不到通用性强,我们的工作重点也不会放在组件开发上;当然我们也会去开发一些有较大通用性的组件,但是站在个人经验来说,组件的开发本身应该是结合业务,根据具体业务有侧重点的开发,既能满足业务需求的变化,又简单易上手。
|
||||
|
||||
后期不排除我们会针对某个行业去开发一成套的组件,同时如果各位老板有定制组件的开发需求,我们也不排除去接一些定制组件开发的兼职。
|
||||
|
||||
# 怎么去开发一个新组件?
|
||||
|
||||
1. 学习开发文档
|
||||
2. 学习官方组件例子
|
||||
3. 关注公众号,查看往期文章
|
||||
4. 和社区同伴交流
|
||||
|
||||
同时希望大家向本项目贡献自己的组件,被官方收录的组件请保证组件有较大的通用性、完善的示例文档,且和本项目保持一致的开源许可证, 如果未上传开源许可证,默认采用本项目的许可证。
|
||||
|
||||
|
||||
# 我的组件依赖和其他组件公用一个`NPM`包,但是版本不一样怎么办?
|
||||
|
||||
整个OpenDataV是采用`Monorepo`方式管理组件以及依赖,严格意义上来说,每个组件都是独立的,每个组件有自己的`类型文件`、`依赖包`和`许可证`,删除任何一个组件不影响其他组件的使用。所以对于同一个包的不同版本,只要在各自的依赖中添加对应的版本即可,也可以像官方组件一样,多个组件共享同一个依赖。
|
||||
|
||||
同时对于官方组件你也可以按需引入,对于自己用不到的可以删除,不会有任何问题
|
||||
|
||||
# 想自己开发后台,如何知道本项目的接口?
|
||||
|
||||
可以查看项目 `src\api`接口文件和类型文件,当然你可以随意更改这些接口定义
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
[toc]
|
||||
|
||||
平台集成了`通用数据`管理功能,目前已完成`静态数据`和`Rest数据`功能开发
|
||||
|
||||
|
||||
# 静态数据
|
||||
|
||||
|
||||
静态数据是指平台提供了一个统一接口用来读取或者存储的数据
|
||||
|
||||

|
||||
|
||||
用户可以在静态页面对平台存储的静态数据进行`修改`、`删除`和`新增`
|
||||
|
||||
## 新增与修改
|
||||
|
||||
点击静态数据的`原始数据`页签,在代码编辑器里面可以对静态数据进行`修改`和`新增`,点击<x-icon name="save"/>按钮,即可将在`处理数据`页签看到,此静态数据被脚本处理完成之后的效果,点击右上角的保存按钮,即可将此静态书进行更新和保存
|
||||
|
||||
## 删除
|
||||
|
||||
在右侧静态数据列表中,鼠标右击即可删除对应的静态数据
|
||||
|
||||
## 设计思路
|
||||
|
||||
|
||||
> 很多低代码平台的静态数据保存在页面中配置文件中,但是我们无法预测用户配置的静态数据大小,如果将静态数据存储在页面配置文件中,将会导致页面配置文件数据体积增大,进而影响页面渲染速度。
|
||||
|
||||
> 静态数据保存在平台后台的话又会带来另一个问题,静态数据在多个组件中被复用时,在某个组件中更改了静态数据内容时所带来的影响是不是可预见的,答案是否定的。
|
||||
|
||||
> 我们不建议在组件使用过程中更改静态数据内容,我们可以通过在每个组件中写不同的脚本来适配同一数据源在不同组件复用时数据差异。
|
||||
|
||||
> 静态数据的修改应该考虑数据被复用时,对使用的组件效果影响,因此我们将静态数据设计为存储在平台后端,在使用组件使用过程中不可更改,但是在静态数据页面可修改!
|
||||
|
||||
|
||||
# Rest数据
|
||||
|
||||
`Rest 数据`是通过`HTTP`接口获取数据,应该是平台最主要的获取数据的方式,一个类似`PostMan`的接口调试
|
||||
|
||||

|
||||
|
||||
|
||||
## 跨域拦截
|
||||
|
||||
因为浏览器的安全策略,一旦你的接口服务未配置跨域相关安全策略,那么是无法在本平台进行接口调试的,可以跟接口服务开发人员协调,支持跨域访问
|
||||
|
||||
|
||||
## 设计思路
|
||||
|
||||
> `Rest数据`页面设计是类似于`PostMan`的作用,但是很遗憾,我们尚未达到`Postman`的效果,目前仅支持`application/json`数据的请求和响应,但是对于`FormData`、`Blob`和`ArrayBuffer`等数据尚未支持
|
||||
|
||||
> `Rest数据`的定义数据是可控的,主要是 `url`、`method`、`headers`、`params`、`data`,因此整个 `Rest数据`定义我们是保存在页面配置文件中的,我们支持在`Rest数据`页面增加或者修改接口定义,同时我们也支持在单个组件中添加接口,也就是说你修改任意一接口也不会影响已经在其他组件中被使用的该接口
|
||||
|
||||
|
||||
# 脚本
|
||||
|
||||
|
||||
为了提高组件的`复用性`和接口的`兼容性`,我们提供了脚本功能。
|
||||
|
||||

|
||||
|
||||
|
||||
通过脚本你可以将`接口返回的数据`处理成`组件所需要的数据`,这样就你就无需担心你的组件和接口之间的无法兼容的问题。
|
||||
|
||||
目前我们的脚本只支持`javascript`,后续将支持`python`,因为`Python`在数据处理方面的先天优势十分的吸引我们,我们希望可以将`Python`的引入,可以和平台碰撞出耀眼的火光。
|
||||
|
||||
|
||||
## Javascript脚本
|
||||
|
||||
`Javascript脚本` 我们使用的是js的 `new Function()`特性,因此`Javascript脚本`,你只需要书写函数体本身即可
|
||||
|
||||
例如
|
||||
|
||||
```javascript
|
||||
const filterFunc = (resp,options) => {
|
||||
return resp.filter(el => el.value > 30)
|
||||
}
|
||||
|
||||
```
|
||||
你只需要写
|
||||
|
||||
```javascript
|
||||
return resp.filter(el => el.value > 30)
|
||||
```
|
||||
|
||||
`Javascript脚本` 生成函数默认有两个位置参数`resp`和`options`
|
||||
|
||||
|
||||
- `resp`: 需要处理的数据
|
||||
|
||||
- `options`: 在组件中使用,默认返回的组件的`propValue`属性
|
||||
|
||||
|
||||
## Python脚本
|
||||
|
||||
待实现
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,536 @@
|
|||
[toc]
|
||||
|
||||
# 基础知识
|
||||
|
||||
## 入门须知
|
||||
|
||||
1. 本项目是基于`Vue3`开发,所有的组件都符合`Vue3`知识,对于`Vue3`知识不熟悉的同学建议,先学习`Vue3`相关知识
|
||||
|
||||
2. 本项目是采用`TypeScript`开发,若对于`TypeScript`知识欠缺的,可先熟悉一下`TypeScript`
|
||||
|
||||
3. 本项目中所有的组件只能在本项目中使用,无法保证在别的平台同样适用
|
||||
|
||||
# 编辑器
|
||||
|
||||
编辑器承载与渲染组件的容器,编辑提供了组件`拉伸`、`旋转`、`置顶`、`置底`、`组合`、`拆分`的能力,编辑器如下图:
|
||||
|
||||

|
||||
|
||||
编辑器主要由以下功能分区组成:
|
||||
|
||||
> - 顶部的`工具栏`: 常用工具按钮
|
||||
> - 左侧的`资源栏`: 组件和图层
|
||||
> - 右侧的`属性栏`: 属性分为组件属性和画布属性
|
||||
> - 底部的`状态栏`: 创建页面的状态
|
||||
> - 中间的`画布`: 承载组件的最顶层容器
|
||||
|
||||
# 组件
|
||||
|
||||
一个完整的组件又以下属性构成
|
||||
|
||||
> - `componentName`:组件名
|
||||
> - `component`: 组件对象渲染模板
|
||||
> - `config`:组件配置项对象
|
||||
> - `docs`:组件说明文档
|
||||
|
||||
## 组件名
|
||||
|
||||
组件名必须保证全局唯一,因为组件会被挂载到`Vue3`实例对象上
|
||||
|
||||
## 组件配置项对象
|
||||
|
||||
配置项对象是继承了 `CustomComponent` 抽象类的子类,在这里我们以静态文本为例
|
||||
|
||||
```typescript
|
||||
|
||||
/**
|
||||
* {component}: 组件名
|
||||
* {group}: 组件分类
|
||||
* {name}: 组件label
|
||||
* {id}: 组件ID
|
||||
* {width}: 组件初始化长度
|
||||
* {height}: 组件初始化高度
|
||||
* {icon? }: 组件图标
|
||||
* {DataMode? }: 数据接入模式
|
||||
*/
|
||||
|
||||
class StaticTextComponent extends CustomComponent {
|
||||
constructor(id?: string, name?: string, icon?: string) {
|
||||
super({
|
||||
component: componentName,
|
||||
group: ComponentGroup.TEXT,
|
||||
name: name ? name : '静态文本',
|
||||
id,
|
||||
width: 150,
|
||||
height: 20,
|
||||
icon,
|
||||
dataMode: DataMode.SELF
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
各个属性含义
|
||||
|
||||
> - `component`: 组件名
|
||||
> - `group`: 组件分类
|
||||
> - `name`: 组件label
|
||||
> - `id`: 组件ID
|
||||
> - `width`: 组件初始化长度
|
||||
> - `height`: 组件初始化高度
|
||||
> - `icon? `: 组件图标
|
||||
> - `DataMode? `: 数据接入模式
|
||||
|
||||
|
||||
除过需要继承`CustomComponent` 抽象类外,还需要重新定义`_prop`和`_style`属性,
|
||||
|
||||
> - `_prop属性`:定义了组件可以更改的属性
|
||||
|
||||
> - `_style`: 定义了组件的外在的CSS样式
|
||||
|
||||
## 组件渲染模板对象
|
||||
|
||||
组件渲染模板对象是一个特殊的`Vue3`组件对象, 这个组件对象有一个`component`配置项对象,这里我展示Vue3 `setup script` 写法
|
||||
|
||||
```typescript
|
||||
|
||||
const props = defineProps<{
|
||||
component: StaticTextComponent
|
||||
}>()
|
||||
|
||||
```
|
||||
|
||||
## 组件分类
|
||||
|
||||
目前组件分为 16类,分别是
|
||||
|
||||
- `BASIC`: 基础组件
|
||||
- `BORDER`: 边框组件
|
||||
- `DECORATION`: 装饰组件
|
||||
- `LINE`: 线状图组件
|
||||
- `BAR`: 柱状图组件
|
||||
- `PIE`: 饼状图组件
|
||||
- `MAP`: 地图组件
|
||||
- `GAUGE`: 仪表盘组件
|
||||
- `CUSTOM`: 自定义组件
|
||||
- `TEXT`: 文本组件
|
||||
- `TABLE`: 表格组件
|
||||
- `NAVIFATION`: 导航组件
|
||||
- `PROGERSS`: 进度条组件
|
||||
- `THERMOMETER`: 温度计组件
|
||||
- `OTHER`: 其他组件
|
||||
|
||||
用户按照组件特性选择对应的组件类型即可
|
||||
|
||||
## 组件属性
|
||||
|
||||
组件属性是定义了组件在编辑器右侧`属性栏`中的`属性`的可配置项
|
||||
|
||||
组件属性的由属性配置项对象`MetaContainerItem`和组件属性值对象构成
|
||||
|
||||
|
||||
```typescript
|
||||
|
||||
[
|
||||
{
|
||||
label: '基础配置',
|
||||
prop: 'base',
|
||||
children: [
|
||||
{
|
||||
prop: 'type',
|
||||
label: '文本类型',
|
||||
type: FormType.SELECT,
|
||||
props: {
|
||||
defaultValue: 'text',
|
||||
options: [
|
||||
{ value: 'text', label: '文本' },
|
||||
{ value: 'symbol', label: '符号' }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'text',
|
||||
label: '自定义文本',
|
||||
type: FormType.TEXT,
|
||||
props: {
|
||||
defaultValue: 'OpenDataV'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
该配置项对应的组件属性分别是
|
||||
|
||||
```typescript
|
||||
|
||||
StaticTextType {
|
||||
base: {
|
||||
text: string
|
||||
type: 'text' | 'symbol'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
属性配置项对象`MetaContainerItem`
|
||||
- `label`: 分类标签
|
||||
- `prop`: 分类值
|
||||
- `children`: `AttrType`子属性配置项集合
|
||||
|
||||
`AttrType`子属性配置项
|
||||
|
||||
- `label`: 值标签
|
||||
- `prop`: 属性值
|
||||
- `type`: 属性值的Form类型
|
||||
- `showLabel`: 是否显示标签
|
||||
- `props`: Form组件配置
|
||||
- `help`: Form帮助信息
|
||||
|
||||
|
||||
## 组件样式
|
||||
|
||||
### 公共样式
|
||||
所有的组件都有`位置大小`这个公共样式,公共样式主要是组件在画布中的`坐标`、`尺寸`和`旋转度`三个类属性
|
||||
|
||||
### 其他样式
|
||||
|
||||
其他样式组件属性的定义类似,只不过组件属性,需要你在组件中根据业务将属性值渲染出来,但是组件样式是里面的子属性值要求必须是CSS属性,本平台已经实现了常见的CSS样式渲染
|
||||
|
||||
## 组件可用Form类型
|
||||
|
||||
本平台已经实现了常用的Form类型,例如:
|
||||
|
||||
> - `TEXT`: Input框
|
||||
> - `TEXTAREA`: 文本域
|
||||
> - `NUMBER`: 数字输入框
|
||||
> - `SELECT`: 选择器
|
||||
> - `COLOR`: 色盘
|
||||
> - `RADIO`: 单选框
|
||||
> - `SWITCH`: 开关
|
||||
|
||||
以及平台定制的Form类型, 例如:
|
||||
|
||||
> - `FONT_STYLE`: 字体选择
|
||||
> - `FONT_WEIGHT`: 字重选择
|
||||
> - `ARRAY`: 动态列表Form
|
||||
> - `BACKGROUND`: 背景选择
|
||||
> - `LINEAR_GRADIENT`: 渐变色
|
||||
|
||||
还有支持用户自定义`Form`
|
||||
|
||||
> -`CUSTOM`: 自定义Form
|
||||
|
||||
**Form公共属性**
|
||||
|
||||
所有的Form 都有如下属性,其次每一种Form类型可能拥有自己独有的属性
|
||||
|
||||
|属性名|含义|说明|
|
||||
|----|---|---|
|
||||
|editable|是否可编辑| bool型|
|
||||
|disabled|是否禁用| bool型|
|
||||
|required|是否必须| bool型|
|
||||
|defaultValue|默认值| 任意类型|
|
||||
|options|自有配置项| 任意类型|
|
||||
|
||||
1. TEXT Form属性
|
||||
|
||||
无专有属性
|
||||
|
||||
2. NUMBER Form属性
|
||||
|
||||
|属性名|含义|说明|
|
||||
|----|---|---|
|
||||
|min|最小值| 数字型|
|
||||
|max|最大值| 数字型|
|
||||
|step|步长| 数字型|
|
||||
|
||||
3. SELECT Form属性
|
||||
|
||||
|属性名|含义|说明|
|
||||
|----|---|---|
|
||||
|options|选项列表| `Array<{value:any, label:string}>`|
|
||||
|
||||
4. SWITCH Form属性
|
||||
|
||||
|属性名|含义|说明|
|
||||
|----|---|---|
|
||||
|options|选项列表| `Array<{value:any, label:string}>`|
|
||||
|
||||
5. RADIO Form属性
|
||||
|
||||
|属性名|含义|说明|
|
||||
|----|---|---|
|
||||
|options|选项列表| `Array<{value:any, label:string}>`|
|
||||
|
||||
6. ARRAY Form 属性
|
||||
|
||||
|属性名|含义|说明|
|
||||
|----|---|---|
|
||||
|count|数量| 数字型|
|
||||
|type|类型| `static`长度不可变,`dynamic`长度可变|
|
||||
|maxItem|最大数量| 数字型|
|
||||
|minItem|最小数量| 数字型|
|
||||
|
||||
|
||||
7. CUSTOM Form 属性
|
||||
|
||||
|属性名|含义|说明|
|
||||
|----|---|---|
|
||||
|componentType|组件| Form组件|
|
||||
|args|组件参数|任何类型|
|
||||
|
||||
8. 自定义Form类型
|
||||
|
||||
我们自定义Form类型,自定义Form组件需要实现属性`value`和`update:value`的`emit`方法,具体详见本平台`FONT_STYLE`或者`BACKGROUND`等平台定制Form组件
|
||||
|
||||
|
||||
```typescript
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
value?: number
|
||||
}>(),
|
||||
{
|
||||
value: 400
|
||||
}
|
||||
)
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'update:value', weight: number): void
|
||||
(e: 'change', weight: number): void
|
||||
}>()
|
||||
```
|
||||
|
||||
## 监听组件属性
|
||||
|
||||
当用户通过`属性栏`的`属性`更改组件属性时,画布中的组件需要根据用户更改来,对组件进行重新渲染。
|
||||
|
||||
监听组件属性变化有三种方式
|
||||
|
||||
### watch 观测
|
||||
|
||||
可以通过深度监听,监听组件 `component`属性对象的`propValue`属性或者子属性来监听组件属性更改
|
||||
|
||||
```typescript
|
||||
|
||||
const customeText = ref<string>(props.propValue.base.text)
|
||||
|
||||
watch(
|
||||
() => props.propValue.base.text,
|
||||
(value: string) => {
|
||||
customeText.value = value
|
||||
}
|
||||
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
### computed 计算属性
|
||||
|
||||
也可以通过computed计算组件 `component`属性对象的`propValue`属性或者子属性来监听组件属性更改
|
||||
|
||||
```typescript
|
||||
|
||||
const customeText = computed<string>(() => {
|
||||
return props.propValue.base.text
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
### hooks
|
||||
|
||||
还可以通过平台提供的`useProp`hook来监听属性变化
|
||||
|
||||
```typescript
|
||||
const customeText = ref<string>(props.propValue.base.text)
|
||||
const propValueChange = (type:string, key:string, value:any) {
|
||||
if(type === 'base' && key === 'text'){
|
||||
customeText.value = value
|
||||
}
|
||||
}
|
||||
const { propValue } = useProp<StaticTextType>(props.component, propValueChange)
|
||||
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
需要注意的事,只有在编辑模式下才需要监听组件属性变化,在预览模式下不需要监听组件属性变化,因此可以在组件中判断编辑器模式(`什么是编辑器模式,详见编辑器模式`),来决定是不是要对属性进行监听依次可以提升组件性能
|
||||
|
||||
|
||||
## 数据
|
||||
|
||||
组件配置项对象在有一个`DataMode` 数据接入模式的属性,他定义了组件可以从那里接入数据
|
||||
|
||||
接入模式分为三类:
|
||||
|
||||
> - `SELF`: 组件自己内部自行接入数据
|
||||
> - `UNIVERSAL`: 组件采用通用的方式接入数据
|
||||
> - `GLOBAL`: 组件从订阅全局数据
|
||||
|
||||
### SELF
|
||||
|
||||
组件自己在内部通过不管通过`HTTP`或者`WebSocket` 自己处理数据的请求和响应,这时候组件的`属性栏`没有`数据`配置项
|
||||
|
||||
```typescript
|
||||
|
||||
const activeCount = ref<string>(props.propValue.base.count)
|
||||
|
||||
const getData = async () => {
|
||||
const resp = await http.get({url: '/getdata'})
|
||||
activeCount.value = resp.data
|
||||
}
|
||||
const intervalId:number = 0
|
||||
onMounted( () => {
|
||||
clearInterval(intervalId)
|
||||
intervalId = setInterval( getData, 30000)
|
||||
})
|
||||
|
||||
onUnmounted( () => {
|
||||
clearInterval(intervalId)
|
||||
})
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
### UNIVERSAL
|
||||
|
||||
组件采用`useData`hook来统一处理数据,通用数据处理方式目前提供了三种数据接入方式,分别是`示例数据`、`静态数据`、`Rest数据`
|
||||
|
||||
> - `示例数据`: 示例数据无法更改,主要 用来组件的展示,不建议在生产环境下使用
|
||||
> - `静态数据`: 静态数据从后台数据库中存储的静态数据中加载
|
||||
> - `Rest数据`: 根据用户提供的`REST`接口,发起HTTP请求,获取数据
|
||||
|
||||
|
||||
```typescript
|
||||
|
||||
import { useData } from 'open-data-v/base/hooks'
|
||||
let chartData:
|
||||
| Array<{ label: string; value: number }>
|
||||
| RequestResponse<Array<{ label: string; value: number }>>['afterData'] = []
|
||||
const dataChange = (resp: any, _: DataType) => {
|
||||
if (resp.status >= 0) {
|
||||
chartData = resp.afterData
|
||||
doSomething(chartData)
|
||||
}
|
||||
}
|
||||
|
||||
useData(props.component, dataChange)
|
||||
|
||||
```
|
||||
|
||||
`useData`钩子的第二个参数是一个数据处理回调,入参是获取到的数据,用户可以在回调中根据数据处理组件的渲染
|
||||
|
||||
### GLOBAL
|
||||
|
||||
待实现
|
||||
|
||||
|
||||
# 监听组件尺寸
|
||||
|
||||
不管是在`画布`中`拉伸`组件还是通过`属性栏`的`样式`里面的`位置大小`属性进行配置,都会引起组件大小变动,可以通过`v-resize`指令来监听组件大小的更改,具体详见`常见指令`章节
|
||||
|
||||
|
||||
|
||||
# 编辑器模式
|
||||
|
||||
1. 模式分类
|
||||
|
||||
编辑器分为`编辑模式(EDIT)`、`预览模式(PREVIEW)`、`视图模式(VIEW)`
|
||||
|
||||
|
||||
2. 模式作用
|
||||
|
||||
在组件中可以根据编辑器的模式来切换不同的表现形式,例如:
|
||||
|
||||
>
|
||||
> - 在`编辑模式`下我们需要监听组件属性或者样式的变化,但是在`预览模式`和`视图模式`下我们不需要监听组件属性的变化,这时候我们可以获取编辑器模式,来在不同模式下采用不同的逻辑。
|
||||
|
||||
>
|
||||
> - 在`编辑模式`下我们采用示例数据来渲染组件,但是在`预览模式`和`视图模式`下我们采用生产数据渲染组件。
|
||||
|
||||
|
||||
3. 获取组件模式
|
||||
|
||||
我们可以通过`canvasState`来获取编辑器模式,例如
|
||||
|
||||
```typescript
|
||||
import useCanvasState from 'open-data-v/designer/state/canvas'
|
||||
|
||||
const canvasState = useCanvasState()
|
||||
// editoMode 即为编辑器模式
|
||||
canvasState.editMode
|
||||
|
||||
// isEditMode 可以判断编辑器是否处于编辑模式
|
||||
canvasState.isEditMode
|
||||
|
||||
```
|
||||
|
||||
# 常用指令
|
||||
|
||||
`v-resize` 组件缩放
|
||||
|
||||
组件编辑器中经常需要被拉伸进行放大或者缩小,可以采用我们封装的`v-resize`指令,来监听组件的大小变化,以便做出对应的响应
|
||||
|
||||
```html
|
||||
<div v-size="resizeHander">我是组件</div>
|
||||
```
|
||||
|
||||
```TypeScript
|
||||
const resizeHandler = (entry: ResizeObserverEntry) => {
|
||||
const {width, height}: DOMRectReadOnly = entry.contentRect
|
||||
doSomething()
|
||||
}
|
||||
```
|
||||
|
||||
# 组件文档
|
||||
|
||||
本项目主体文档采用`Markdown`编写,支持在Markdown中渲染Vue组件,为了方便文档书写,我们提供了一个工具组件`RenderComponent`,支持渲染任意组件,并提供`palyground`
|
||||
|
||||
```vue
|
||||
<RenderComponent
|
||||
:config="StaticTextComponent"
|
||||
:component="StaticText"
|
||||
:prop-value="{
|
||||
base: {
|
||||
text: '我们一起建设OpenDataV吧',
|
||||
type: 'text'
|
||||
}
|
||||
}"
|
||||
:style="{
|
||||
color: '#d03050',
|
||||
fontSize: 40,
|
||||
fontWeight: 800,
|
||||
width: 550,
|
||||
height: 100
|
||||
}
|
||||
"
|
||||
title="静态文本"
|
||||
mode="debug"
|
||||
/>
|
||||
```
|
||||
|
||||
> - `mode`: 模式,可选项`debug`|`view`, 在`debug`下提供了`playground`功能
|
||||
> - `component`: 组件模板
|
||||
> - `config`:组件配置项类
|
||||
> - `propValue`:组件属性初始化配置项
|
||||
> - `style`:组件样式初始化配置项
|
||||
|
||||
|
||||
大家在撰写组件文档时需要有如下内容
|
||||
|
||||
**1. 属性或者样式的解释**
|
||||
|
||||
**2. 效果示例**
|
||||
|
||||
**3. 可交互的`playground`**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
[toc]
|
||||
|
||||

|
||||
|
||||
<div class="badge">
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
## 简介
|
||||
|
||||
🎃OpenDataV 是一个纯前端的`拖拽式`、`可视化`、`低代码`数据可视化 🌈 开发平台,你可以用它自由的拼接成各种 ✨ 炫酷的大屏,同时支持用户方便的开发自己的组件并接入平台。
|
||||
|
||||
## 体验
|
||||
|
||||
🧙国外:[http://datav.byteportrait.com/](http://datav.byteportrait.com/)
|
||||
|
||||
🧙国内:[http://small_bud_star.gitee.io/opendatav](http://small_bud_star.gitee.io/opendatav)
|
||||
|
||||
🧙国内:[http://opendatav.xingxingzaixian.fun/](http://opendatav.xingxingzaixian.fun/) (已对接后端)
|
||||
|
||||
|
||||
## 源码地址:
|
||||
|
||||
🍨github:[https://github.com/AnsGoo/openDataV](https://github.com/AnsGoo/openDataV)
|
||||
|
||||
🍨gitee:[https://gitee.com/small_bud_star/DataV](https://gitee.com/small_bud_star/DataV)
|
||||
|
||||
gitee 仅做代码同步,issues 或者 PR 请在 github 提交。
|
||||
|
||||
目前该项目在不断的完善中,🎉 欢迎 issuer,🌹 欢迎 start, 🎨 欢迎 commit, 🚀 欢迎 use...,💪 欢迎一切技术交流活动
|
||||
|
||||
## 👁️ 预览
|
||||
|
||||
### 🤿 页面编辑
|
||||
|
||||
|
||||
1. 组件添加
|
||||

|
||||
|
||||
2. 组件操作
|
||||

|
||||
|
||||
3. 组件数据配置
|
||||

|
||||
|
||||
### 🖇️ 接口管理
|
||||
|
||||

|
||||
|
||||
## 💒 功能
|
||||
|
||||
- 🎊 编辑器页面基本功能完成,包括编辑、预览、导入、导出、保存
|
||||
- 🪄 图层的置顶、置底、上下移动、显示、隐藏、复制、剪切、粘贴
|
||||
- 🖼️ 组件的缩放、旋转、拖动、复制、粘贴、组合、拆分、移除、自动对齐
|
||||
- 🔮 支持用户操作记录的恢复、撤销功能
|
||||
- 🧶 支持用户自定义组件
|
||||
- 📔 支持组件的用户自定组件配置项
|
||||
- 🏪 支持明暗主题切换
|
||||
- 🧬 使用 Monorepo 模式进行组件和依赖管理
|
||||
- 🧶 组件数据自定义接入
|
||||
- 🔌 数据动态处理(JS 已完成)
|
||||
- 🖇️ 接口管理(示例数据、静态数据、HTTP 接口数据已完成适配)
|
||||
|
||||
## 🎢 技术点
|
||||
|
||||
本项目采用`Vue3` + `vite` + `TypeScript`开发,界面库使用`NaiveUI`,使用面向对象方式封装了路由、请求、存储,组件采用自动扫描注册、异步加载,提升渲染速度;使用 IndexDB 存储快照数据,减少快照数据内存占用,加快访问速度;组件独立依赖,解耦了组件和基础框架的依赖库,方便后续独立开发组件。
|
||||
|
||||
目前仅开发了部分组件,后续还会继续完善。
|
||||
|
||||
## ⌛ 计划功能
|
||||
|
||||
- [ ] 组件管理
|
||||
- [ ] 脚本管理(Python 待支持)
|
||||
- [ ] GraphQL、GRPC、WebScoket、MQTT、SocketIO 多种数据接口适配
|
||||
- [ ] 代码生成
|
||||
- [ ] 动态文档
|
||||
- [ ] 全局数据接入
|
||||
|
||||
## 💂 开发
|
||||
|
||||
### 🧊 开发环境
|
||||
|
||||
| 名称 | 版本 |
|
||||
| ---- | ------- |
|
||||
| node | 16.14.x |
|
||||
| pnpm | 7.9.3 |
|
||||
| vue | 3.2.37 |
|
||||
|
||||
🚥 目前仅在`Chrome`和`Microsoft Edge`最新版浏览器测试过,其他浏览器未测试
|
||||
|
||||
### 🎮 启动项目
|
||||
|
||||
```Bash
|
||||
|
||||
# 安装依赖
|
||||
pnpm install
|
||||
或
|
||||
pnpm bootstrap
|
||||
|
||||
# 运行项目
|
||||
pnpm dev
|
||||
|
||||
# 打包项目
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## 🧑💻 代码提交
|
||||
|
||||
git commit 信息请按照如下规范进行书写
|
||||
|
||||
- feat: 新功能
|
||||
- fix: 修复 Bug
|
||||
- docs: 文档修改
|
||||
- perf: 性能优化
|
||||
- revert: 版本回退
|
||||
- ci: CICD 集成相关
|
||||
- test: 添加测试代码
|
||||
- refactor: 代码重构
|
||||
- build: 影响项目构建或依赖修改
|
||||
- style: 不影响程序逻辑的代码修改
|
||||
- other: 不属于以上类型的其他类型(日常事务)
|
||||
|
||||
## ☎️联系方式
|
||||
|
||||
**技术交流,请加微信群**
|
||||
|
||||
<img src="/wechat.png" style="width:430px">
|
||||
|
||||
**更新动态请关注公众号**
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
<script setup >
|
||||
</script>
|
||||
<style scoped>
|
||||
.badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
|
||||
|
||||
[toc]
|
||||
|
||||
## 开源合作
|
||||
|
||||
|
||||
本项目使用`Apache-2.0`开源协议,二次开发及其商用请遵守开源协议即可,若想采用商用授权请联系`OpenDataV`的作者,可邮件 **haiven_123@163.com**
|
||||
|
||||
## 免责声明
|
||||
|
||||
任何企业或者个人使用`OpenDataV`原始项目或者二次开发,对自己或者他人造成的任何形式的损失或者危害,`OpenDataV`开发者不承担任何法律风险
|
||||
|
||||
|
||||
## 权益声明
|
||||
|
||||
本项目使用`Apache-2.0`开源协议,二次开发及其商用请遵守开源协议即可,如若擅自违反开源协议产生的法律纠纷,`OpenDataV`将会追究到底
|
||||
|
||||
|
||||
|
||||
```license
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2022 AnsGoo
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
```
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
[toc](目录)
|
||||
|
||||
|
||||
快速创建一个属于自己的页面并发布
|
||||
|
||||
# 快速开始
|
||||
|
||||
接下来我们将带领大家创建一个页面,来熟悉该低代码平台的使用
|
||||
|
||||
## 安装
|
||||
|
||||
```Bash
|
||||
git clone https://github.com/AnsGoo/openDataV.git
|
||||
|
||||
npm install pnpm -g
|
||||
|
||||
pnpm install
|
||||
```
|
||||
|
||||
## 启动
|
||||
|
||||
```Bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
## 编译
|
||||
|
||||
```Bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
|
||||
# 访问布局管理
|
||||
|
||||
```Bash
|
||||
/Pages
|
||||
```
|
||||
|
||||

|
||||
|
||||
当前会展示出项目已有的页面,你可以点击页面卡片进行查看,也可以对已有页面进行二次编辑,还可以自己新创建一个页面
|
||||
|
||||
|
||||
## 打开创建页面
|
||||
|
||||
|
||||
点击`新建按钮`号会自动进入`创建`页面
|
||||
|
||||

|
||||
|
||||
整个页面被分为:
|
||||
|
||||
- 顶部的`工具栏`: 常用工具按钮
|
||||
- 左侧的`资源栏`: 组件和图层
|
||||
- 右侧的`属性栏`: 属性分为组件属性和画布属性
|
||||
- 底部的`状态栏`: 创建页面的状态
|
||||
- 中间的`画布`: 承载组件的最顶层容器
|
||||
|
||||
接下来我们将开始创建一个页面
|
||||
|
||||
|
||||
# 添加第一组件
|
||||
|
||||
我们设计一个`HelloWord`文本页面
|
||||
|
||||
## 拖动组件
|
||||
|
||||
我们在左侧`资源栏`的组件中找到`柱状图`->`基础柱状图`组件,并将其拖动到画布中央,并单击鼠标左键选中组件
|
||||
|
||||

|
||||
|
||||
## 修改组件属性
|
||||
|
||||
|
||||
修改组件下列属性
|
||||
- 数据配置的大值为`70`
|
||||
- 坐标轴配置的X网格线`不显示`
|
||||
- 坐标轴配置的轴线颜色为 `#18A058`
|
||||
|
||||

|
||||
|
||||
同时我们可以对组件进行`拖动`、`旋转`、`拉伸`
|
||||
|
||||
<video muted autoplay="true" loop="true">
|
||||
<source src="./img/010.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
## 配置组件数据
|
||||
|
||||
将组件的示例数据改为静态数据,切换组件数据类型,这里我们选择静态数据,并选择具体的静态数据
|
||||
|
||||
<video muted autoplay="true" loop="true">
|
||||
<source src="./img/011.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
|
||||
## 修改组件样式
|
||||
|
||||
所有的组件都有`位置大小`的公共样式,主要是用来设置组件的`坐标`、`尺寸`和`旋转`情况,你除过可以通过`拉伸`、`拖动`、`旋转`组件本身来改变这些值以外,还可以通过公共样式`位置大小`来精确更改。
|
||||
这里我让将组件旋转状态改为0
|
||||
|
||||
<video muted autoplay="true" loop="true">
|
||||
<source src="./img/012.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
# 组件间的操作
|
||||
|
||||
我们向画布中追加一个边框组件
|
||||
|
||||

|
||||
|
||||
## 选中
|
||||
|
||||
这里我们通过框选,选中`边框`和`柱状图`
|
||||
|
||||
- 框选
|
||||
|
||||
<video muted autoplay="true" loop="true">
|
||||
<source src="./img/020.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
- ctr + '鼠标单击'
|
||||
|
||||
<video muted autoplay="true" loop="true">
|
||||
<source src="./img/021.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
- 通过图层选中
|
||||
|
||||
<video muted autoplay="true" loop="true">
|
||||
<source src="./img/022.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
## 组合
|
||||
|
||||
在选中框上右击组合进行组件的组合,这时候两个或者多个组件就被变成一个`分组`组件
|
||||
|
||||
<video muted autoplay="true" loop="true">
|
||||
<source src="./img/023.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
|
||||
|
||||
## 拆分
|
||||
|
||||
我们也可以通过选中分组组件,然后鼠标右击对`分组`组件进行`拆分`
|
||||
|
||||
<video muted autoplay="true" loop="true">
|
||||
<source src="./img/024.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
|
||||
|
||||
# 图层编辑
|
||||
|
||||
图层是组件的层级关系,越是靠近顶层的组件图层级别越高,组件图层级别高的可以遮盖图层级别低的组件,图层级别高低关系着组件的渲染顺序,从而影响用户页面的`视觉效果`(比如一张图片覆盖了另一张图片)和`交互效果`(点击效果无法触发)等;点击左侧`资源栏`一列的图层即可看到我们画图中所有组件之间的图层关系,默认组件的名称就是图层的名称。
|
||||
|
||||
## 更名
|
||||
|
||||
选中组件之后,每一个组件的都有属性一栏都有一个`公共属性`,可以通过更改`公共属性`的`名称`一栏来修改图层名称
|
||||
|
||||
|
||||
## 移动
|
||||
|
||||
可以通过选中组件来,然后鼠标右击菜单栏里面的`置顶`、`上移一层`、`置底`、`下移一层`来更改组件的图层顺序
|
||||
|
||||
<video muted autoplay="true" loop="true">
|
||||
<source src="./img/025.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
|
||||
也可以通过`图层`一栏,鼠标右击的菜单栏里面的`置顶`、`上移一层`、`置底`、`下移一层`来更改组件的图层顺序
|
||||
|
||||
<video muted autoplay="true" loop="true">
|
||||
<source src="./img/026.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
|
||||
还可以通过拖动组件到对应的位置来修改图层顺序
|
||||
|
||||
<video muted autoplay="true" loop="true">
|
||||
<source src="./img/027.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
## 隐藏、显示
|
||||
|
||||
|
||||
在`图层`一栏,通过在图层上鼠标右击菜单栏里面的`显示`\\`隐藏`来显示或者隐藏组件,需要注意的是显示或者隐藏组件只是在画图中组件较多时,通过隐藏组件来减少画布中组件数量,便于用户编辑组件,在非编辑模式下无任何效果,只对编辑模式有效
|
||||
|
||||
# 修改画布
|
||||
|
||||
## 分辨率
|
||||
|
||||
我们支持针对不同的分辨率的设备,当设备分辨率比差异较大的情况下,可以通过指定分辨率使设计出来的页面达到最好的效果
|
||||
|
||||
## 页面背景
|
||||
|
||||
默认页面使背景色,我们支持页面修改为`背景色`、`渐变色`、`背景图`
|
||||
|
||||
<video muted autoplay="true" loop="true">
|
||||
<source src="./img/028.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
# 预览并发布
|
||||
|
||||
当我们完成页面的设计,我们可以通过`工具栏`里面的预览按钮去预览一下页面效果效果,也可以通过`保存`按钮来保存我们的页面,还可以通过`导出`按钮将我们页面设计在本地进行保存。
|
||||
|
||||
<video muted autoplay="true" loop="true">
|
||||
<source src="./img/029.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
|
||||
至此一个简单的页面就设计完成了!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
|
@ -0,0 +1,44 @@
|
|||
<script setup lang="ts">
|
||||
// This starter template is using Vue 3 <script setup> SFCs
|
||||
// Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup
|
||||
import { NGlobalStyle, NLoadingBarProvider } from 'naive-ui'
|
||||
import { useCanvasState } from 'open-data-v/designer'
|
||||
import { computed, provide, readonly, ref } from 'vue'
|
||||
import { RouterView, useRouter } from 'vue-router'
|
||||
|
||||
import ConfigProvider from '@/components/provider/ConfigProvider.vue'
|
||||
import { useProjectSettingStoreWithOut } from '@/store/modules/projectSetting'
|
||||
|
||||
const canvasState = useCanvasState()
|
||||
const projectStore = useProjectSettingStoreWithOut()
|
||||
provide('DarkTheme', readonly(computed(() => projectStore.darkTheme)))
|
||||
const overflow = ref<string>(
|
||||
(() => {
|
||||
if (!canvasState.isEditMode) {
|
||||
return 'hidden'
|
||||
} else {
|
||||
return 'auto'
|
||||
}
|
||||
})()
|
||||
)
|
||||
const { currentRoute } = useRouter()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ConfigProvider>
|
||||
<n-loading-bar-provider>
|
||||
<RouterView :key="currentRoute.path" :style="{ overflow }" />
|
||||
<n-global-style />
|
||||
</n-loading-bar-provider>
|
||||
</ConfigProvider>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
// overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
import { apiHttp as http } from '@/utils/http'
|
||||
|
||||
import type { AfterScriptDetail } from './type'
|
||||
|
||||
/**
|
||||
* 根据id 加载后置脚本
|
||||
* @param id 脚本ID
|
||||
*/
|
||||
export const getAfterScriptApi = async (id: string): Promise<AxiosResponse<AfterScriptDetail>> => {
|
||||
return http.get<AfterScriptDetail>({
|
||||
url: `/dataset/script/${id}/`
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后置脚本列表
|
||||
*/
|
||||
export const getAfterScriptListApi = async (): Promise<AxiosResponse<AfterScriptDetail[]>> => {
|
||||
return http.get<AfterScriptDetail[]>({
|
||||
url: '/dataset/script/'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新后置脚本
|
||||
* @param id 后置脚本id
|
||||
* @param data 后置脚本
|
||||
*/
|
||||
export const updateAfterScriptApi = async (
|
||||
id: string,
|
||||
data: Partial<Pick<AfterScriptDetail, 'name' | 'code'>>
|
||||
): Promise<AxiosResponse<AfterScriptDetail>> => {
|
||||
return http.put<AfterScriptDetail>({
|
||||
url: `/dataset/script/${id}/`,
|
||||
data: { ...data, id }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建后置脚本
|
||||
* @param data 后置脚本
|
||||
*/
|
||||
export const createAfterScriptApi = async (
|
||||
data: Pick<AfterScriptDetail, 'name' | 'code'>
|
||||
): Promise<AxiosResponse<AfterScriptDetail>> => {
|
||||
return http.post<AfterScriptDetail>({
|
||||
url: '/dataset/script/',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除后置脚本
|
||||
* @param id 脚本ID
|
||||
*/
|
||||
export const deleteAfterScriptApi = async (
|
||||
id: string
|
||||
): Promise<AxiosResponse<AfterScriptDetail>> => {
|
||||
return http.post<AfterScriptDetail>({
|
||||
url: `/dataset/script/${id}/`
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import {
|
||||
createRestDataApi,
|
||||
deleteRestDataApi,
|
||||
getRestDataApi,
|
||||
getRestDataListApi,
|
||||
updateRestDataApi
|
||||
} from './restData'
|
||||
import {
|
||||
createStaticDataApi,
|
||||
deleteStaticDataApi,
|
||||
getStaticDataApi,
|
||||
getStaticDataListApi,
|
||||
updateStaticDataApi
|
||||
} from './staticData'
|
||||
import type { StaticDataDetail } from './type'
|
||||
|
||||
export {
|
||||
createRestDataApi,
|
||||
createStaticDataApi,
|
||||
deleteRestDataApi,
|
||||
deleteStaticDataApi,
|
||||
getRestDataApi,
|
||||
getRestDataListApi,
|
||||
getStaticDataApi,
|
||||
getStaticDataListApi,
|
||||
updateRestDataApi,
|
||||
updateStaticDataApi
|
||||
}
|
||||
export type { StaticDataDetail }
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
import { apiHttp as http } from '@/utils/http'
|
||||
|
||||
import type { RestDataDetail } from './type'
|
||||
|
||||
/**
|
||||
* 根据id 加载Rest数据
|
||||
* @param id Rest接口ID
|
||||
*/
|
||||
export const getRestDataApi = async (id: string): Promise<AxiosResponse<RestDataDetail>> => {
|
||||
return http.get<RestDataDetail>({
|
||||
url: `/dataset/rest/${id}/`
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Rest数据列表
|
||||
*/
|
||||
export const getRestDataListApi = async (): Promise<AxiosResponse<RestDataDetail[]>> => {
|
||||
return http.get<RestDataDetail[]>({
|
||||
url: '/dataset/rest'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新Rest数据
|
||||
* @param id Rest数据id
|
||||
* @param data Rest数据
|
||||
*/
|
||||
export const updateRestDataApi = async (
|
||||
id: string,
|
||||
data: Partial<Omit<RestDataDetail, 'id' | 'author' | 'createDate' | 'updateDate'>>
|
||||
): Promise<AxiosResponse<RestDataDetail>> => {
|
||||
return http.put<RestDataDetail>({
|
||||
url: `/dataset/rest/${id}/`,
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Rest数据
|
||||
* @param data Rest数据
|
||||
*/
|
||||
export const createRestDataApi = async (
|
||||
data: Omit<RestDataDetail, 'id' | 'author' | 'createDate' | 'updateDate'>
|
||||
): Promise<AxiosResponse<RestDataDetail>> => {
|
||||
return http.post<RestDataDetail>({
|
||||
url: '/dataset/rest/',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Rest数据
|
||||
* @param id Rest接口ID
|
||||
*/
|
||||
export const deleteRestDataApi = async (id: string): Promise<AxiosResponse<RestDataDetail>> => {
|
||||
return http.delete<RestDataDetail>({
|
||||
url: `/dataset/rest/${id}/`
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
import { apiHttp as http } from '@/utils/http'
|
||||
|
||||
import type { StaticDataDetail } from './type'
|
||||
|
||||
/**
|
||||
* 根据id 加载静态数据
|
||||
* @param id 静态数据ID
|
||||
*/
|
||||
export const getStaticDataApi = async (id: string): Promise<AxiosResponse<StaticDataDetail>> => {
|
||||
return http.get<StaticDataDetail>({
|
||||
url: `/dataset/static/${id}/`
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取静态数据列表
|
||||
*/
|
||||
export const getStaticDataListApi = async (): Promise<AxiosResponse<StaticDataDetail[]>> => {
|
||||
return http.get<StaticDataDetail[]>({
|
||||
url: '/dataset/static/'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新静态数据
|
||||
* @param id 静态数据id
|
||||
* @param data 静态数据
|
||||
*/
|
||||
export const updateStaticDataApi = async (
|
||||
id: string,
|
||||
data: Partial<Pick<StaticDataDetail, 'name' | 'data'>>
|
||||
): Promise<AxiosResponse<StaticDataDetail>> => {
|
||||
return http.put<StaticDataDetail>({
|
||||
url: `/dataset/static/${id}/`,
|
||||
data: { ...data }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建静态数据
|
||||
* @param data 静态数据
|
||||
*/
|
||||
export const createStaticDataApi = async (
|
||||
data: Pick<StaticDataDetail, 'name' | 'data'>
|
||||
): Promise<AxiosResponse<StaticDataDetail>> => {
|
||||
return http.post<StaticDataDetail>({
|
||||
url: '/dataset/static/',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除静态数据
|
||||
* @param id 静态数据 id
|
||||
*/
|
||||
export const deleteStaticDataApi = async (id: string): Promise<AxiosResponse<StaticDataDetail>> => {
|
||||
return http.post<StaticDataDetail>({
|
||||
url: `/dataset/static/${id}/`
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import type { Method } from 'axios'
|
||||
|
||||
export interface AfterScript {
|
||||
code: string
|
||||
}
|
||||
|
||||
export interface StaticDataDetail {
|
||||
readonly id: string
|
||||
name: string
|
||||
readonly author: string
|
||||
readonly createDate: string
|
||||
readonly updateDate: string
|
||||
data: any
|
||||
}
|
||||
|
||||
export interface RestDataDetail {
|
||||
method: Method
|
||||
url: string
|
||||
headers: Record<string, any>
|
||||
params: Record<string, any>
|
||||
data: Record<string, any>
|
||||
afterScript?: AfterScript
|
||||
readonly id: string
|
||||
name: string
|
||||
readonly author: string
|
||||
readonly createDate: string
|
||||
readonly updateDate: string
|
||||
}
|
||||
|
||||
export interface AfterScriptDetail extends AfterScript {
|
||||
readonly id: string
|
||||
name: string
|
||||
readonly author?: string
|
||||
readonly createDate?: string
|
||||
readonly updateDate?: string
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
import { apiHttp as http } from '@/utils/http'
|
||||
|
||||
import type { ImageFile } from './type'
|
||||
|
||||
// 获取页面数据
|
||||
export const getImageListApi = async (): Promise<AxiosResponse<ImageFile[]>> => {
|
||||
return http.get<ImageFile[]>({
|
||||
url: '/image/image/'
|
||||
})
|
||||
}
|
||||
|
||||
export const uploadImageFileApi = async (
|
||||
file: FormData
|
||||
): Promise<AxiosResponse<{ url: string }>> => {
|
||||
return http.post<{ url: string }>({
|
||||
url: '/image/uploadImage/',
|
||||
data: file,
|
||||
headers: {
|
||||
CONTENT_TYPE: 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { getImageListApi, uploadImageFileApi } from './image'
|
||||
import type { ImageFile } from './type'
|
||||
|
||||
export { getImageListApi, uploadImageFileApi }
|
||||
export type { ImageFile }
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
export interface ImageFile {
|
||||
id: string
|
||||
name: string
|
||||
md5: string
|
||||
url: string
|
||||
createTime: string
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { deletePageApi, getPageApi, getPageListApi, savePageApi, updatePageApi } from './pages'
|
||||
import type { LayoutData, SimpleLayoutData } from './type'
|
||||
|
||||
export type { LayoutData, SimpleLayoutData }
|
||||
export { deletePageApi, getPageApi, getPageListApi, savePageApi, updatePageApi }
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
import { apiHttp as http } from '@/utils/http'
|
||||
|
||||
import type { LayoutData, SimpleLayoutData } from './type'
|
||||
|
||||
/***
|
||||
* 获取页面数据
|
||||
* @param index 页面ID
|
||||
* url: `/api/api/report/list/${index}` `/page/page/${index}/`
|
||||
*/
|
||||
export const getPageApi = async (index: string): Promise<AxiosResponse<string>> => {
|
||||
return http.get<string>({
|
||||
url: `/api/api/report/list/${index}`
|
||||
})
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取页面数据列表
|
||||
*/
|
||||
export const getPageListApi = async (): Promise<AxiosResponse<SimpleLayoutData[]>> => {
|
||||
return http.get({
|
||||
url: '/api/api/report/list'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存页面数据
|
||||
* @param componentData 页面数据
|
||||
*/
|
||||
export const savePageApi = (componentData: LayoutData): Promise<AxiosResponse<LayoutData>> => {
|
||||
return http.post<LayoutData>({
|
||||
url: '/api/api/report',
|
||||
data: componentData
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新页面数据
|
||||
* @param id 页面ID
|
||||
* @param componentData 页面数据
|
||||
*/
|
||||
export const updatePageApi = (
|
||||
id: string,
|
||||
componentData: LayoutData
|
||||
): Promise<AxiosResponse<LayoutData>> => {
|
||||
return http.put<LayoutData>({
|
||||
url: `/api/api/report/${id}`,
|
||||
data: componentData
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除页面数据
|
||||
* @param id 页面ID
|
||||
*/
|
||||
export const deletePageApi = (id: string): Promise<AxiosResponse<string>> => {
|
||||
return http.delete<string>({
|
||||
url: `/api/api/report/${id}`
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import type { ComponentDataType } from 'open-data-v/base'
|
||||
import type { CanvasStyleData } from 'open-data-v/designer'
|
||||
|
||||
export interface LayoutData {
|
||||
id?: string
|
||||
name?: string
|
||||
thumbnail?: string
|
||||
author?: string
|
||||
createTime?: string
|
||||
isHome?: boolean
|
||||
canvasData: ComponentDataType[]
|
||||
canvasStyle: CanvasStyleData
|
||||
isPublish?: boolean
|
||||
dataSlotters: Array<{ type: string; config: any }>
|
||||
}
|
||||
|
||||
export type SimpleLayoutData = Omit<LayoutData, 'canvasStyle' | 'canvasData'>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import { loginApi } from './user'
|
||||
|
||||
export { loginApi }
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
import type { LoginData } from '@/types/user'
|
||||
import { apiHttp as http } from '@/utils/http'
|
||||
|
||||
//
|
||||
/***
|
||||
* 获取页面数据
|
||||
* @param data 登录数据
|
||||
*/
|
||||
export const loginApi = async (data: LoginData): Promise<AxiosResponse<string>> => {
|
||||
return http.post<string>({
|
||||
url: '/user/login',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
After Width: | Height: | Size: 121 KiB |
|
|
@ -0,0 +1,538 @@
|
|||
/* Logo 字体 */
|
||||
@font-face {
|
||||
font-family: "iconfont logo";
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: "iconfont logo";
|
||||
font-size: 160px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
.nav-tabs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-more {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#tabs li {
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-bottom: -1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
#tabs .active {
|
||||
border-bottom-color: #f00;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.tab-container .content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 页面布局 */
|
||||
.main {
|
||||
padding: 30px 100px;
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main .logo {
|
||||
color: #333;
|
||||
text-align: left;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1;
|
||||
height: 110px;
|
||||
margin-top: -50px;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
font-size: 160px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.helps {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.helps pre {
|
||||
padding: 20px;
|
||||
margin: 10px 0;
|
||||
border: solid 1px #e7e1cd;
|
||||
background-color: #fffdef;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon_lists {
|
||||
width: 100% !important;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.icon_lists li {
|
||||
width: 100px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
list-style: none !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.icon_lists li .code-name {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.icon_lists .icon {
|
||||
display: block;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
font-size: 42px;
|
||||
margin: 10px auto;
|
||||
color: #333;
|
||||
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
-moz-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
transition: font-size 0.25s linear, width 0.25s linear;
|
||||
}
|
||||
|
||||
.icon_lists .icon:hover {
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.icon_lists .svg-icon {
|
||||
/* 通过设置 font-size 来改变图标大小 */
|
||||
width: 1em;
|
||||
/* 图标和文字相邻时,垂直对齐 */
|
||||
vertical-align: -0.15em;
|
||||
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||
fill: currentColor;
|
||||
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
|
||||
normalize.css 中也包含这行 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon_lists li .name,
|
||||
.icon_lists li .code-name {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* markdown 样式 */
|
||||
.markdown {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.markdown img {
|
||||
vertical-align: middle;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
color: #404040;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown h2,
|
||||
.markdown h3,
|
||||
.markdown h4,
|
||||
.markdown h5,
|
||||
.markdown h6 {
|
||||
color: #404040;
|
||||
margin: 1.6em 0 0.6em 0;
|
||||
font-weight: 500;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.markdown h5 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
background: #e9e9e9;
|
||||
margin: 16px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown>p,
|
||||
.markdown>blockquote,
|
||||
.markdown>.highlight,
|
||||
.markdown>ol,
|
||||
.markdown>ul {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.markdown ul>li {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
.markdown>ul li,
|
||||
.markdown blockquote ul>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown>ul li p,
|
||||
.markdown>ol li p {
|
||||
margin: 0.6em 0;
|
||||
}
|
||||
|
||||
.markdown ol>li {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.markdown>ol li,
|
||||
.markdown blockquote ol>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown code {
|
||||
margin: 0 3px;
|
||||
padding: 0 5px;
|
||||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown strong,
|
||||
.markdown b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0px;
|
||||
empty-cells: show;
|
||||
border: 1px solid #e9e9e9;
|
||||
width: 95%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table th,
|
||||
.markdown>table td {
|
||||
border: 1px solid #e9e9e9;
|
||||
padding: 8px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
background: #F7F7F7;
|
||||
}
|
||||
|
||||
.markdown blockquote {
|
||||
font-size: 90%;
|
||||
color: #999;
|
||||
border-left: 4px solid #e9e9e9;
|
||||
padding-left: 0.8em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown blockquote p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown .anchor {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.markdown .waiting {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.markdown h1:hover .anchor,
|
||||
.markdown h2:hover .anchor,
|
||||
.markdown h3:hover .anchor,
|
||||
.markdown h4:hover .anchor,
|
||||
.markdown h5:hover .anchor,
|
||||
.markdown h6:hover .anchor {
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.markdown>br,
|
||||
.markdown>p>br {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
background: white;
|
||||
padding: 0.5em;
|
||||
color: #333333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-meta {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-strong,
|
||||
.hljs-emphasis,
|
||||
.hljs-quote {
|
||||
color: #df5000;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-type {
|
||||
color: #a71d5d;
|
||||
}
|
||||
|
||||
.hljs-literal,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-attribute {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-attr,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 代码高亮 */
|
||||
/* PrismJS 1.15.0
|
||||
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection,
|
||||
pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection,
|
||||
code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection,
|
||||
pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection,
|
||||
code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre)>code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre)>code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #9a6e3a;
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: #DD4A68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
|
@ -0,0 +1,351 @@
|
|||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2946854 */
|
||||
src: url('iconfont.woff2?t=1640069620094') format('woff2'),
|
||||
url('iconfont.woff?t=1640069620094') format('woff'),
|
||||
url('iconfont.ttf?t=1640069620094') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-zu:before {
|
||||
content: "\e854";
|
||||
}
|
||||
|
||||
.icon-zhuxingtu:before {
|
||||
content: "\e626";
|
||||
}
|
||||
|
||||
.icon-yibiaopan:before {
|
||||
content: "\eb67";
|
||||
}
|
||||
|
||||
.icon-tubiao-bingtu:before {
|
||||
content: "\eb95";
|
||||
}
|
||||
|
||||
.icon-tubiao-qiapian:before {
|
||||
content: "\eb96";
|
||||
}
|
||||
|
||||
.icon-tubiao-zhexiantu:before {
|
||||
content: "\eb97";
|
||||
}
|
||||
|
||||
.icon-anniuzu:before {
|
||||
content: "\e782";
|
||||
}
|
||||
|
||||
.icon-daohang:before {
|
||||
content: "\e77d";
|
||||
}
|
||||
|
||||
.icon-liebiao:before {
|
||||
content: "\ec6b";
|
||||
}
|
||||
|
||||
.icon-caimeiji:before {
|
||||
content: "\e603";
|
||||
}
|
||||
|
||||
.icon-qita:before {
|
||||
content: "\e63b";
|
||||
}
|
||||
|
||||
.icon-dingzhi:before {
|
||||
content: "\e609";
|
||||
}
|
||||
|
||||
.icon-jichu:before {
|
||||
content: "\e60f";
|
||||
}
|
||||
|
||||
.icon-chartt:before {
|
||||
content: "\e60a";
|
||||
}
|
||||
|
||||
.icon-zhuangshizhuangxiu:before {
|
||||
content: "\e6a6";
|
||||
}
|
||||
|
||||
.icon-wenduji:before {
|
||||
content: "\e6de";
|
||||
}
|
||||
|
||||
.icon-tiaoxingjindutu:before {
|
||||
content: "\e66b";
|
||||
}
|
||||
|
||||
.icon-biankuang:before {
|
||||
content: "\e6b7";
|
||||
}
|
||||
|
||||
.icon-jindutu:before {
|
||||
content: "\f24b";
|
||||
}
|
||||
|
||||
.icon-jinggao1:before {
|
||||
content: "\e601";
|
||||
}
|
||||
|
||||
.icon-yujingdeng:before {
|
||||
content: "\e67f";
|
||||
}
|
||||
|
||||
.icon-zidongxitongjinggaodeng1:before {
|
||||
content: "\e605";
|
||||
}
|
||||
|
||||
.icon-yujing1:before {
|
||||
content: "\e644";
|
||||
}
|
||||
|
||||
.icon-yujing3:before {
|
||||
content: "\e6fe";
|
||||
}
|
||||
|
||||
.icon-jinggaodeng:before {
|
||||
content: "\e64b";
|
||||
}
|
||||
|
||||
.icon-baojing1:before {
|
||||
content: "\e630";
|
||||
}
|
||||
|
||||
.icon-hong:before {
|
||||
content: "\e60d";
|
||||
}
|
||||
|
||||
.icon-yujingdeng1:before {
|
||||
content: "\e623";
|
||||
}
|
||||
|
||||
.icon-a-baojingjinggaoyujingjinggaodeng:before {
|
||||
content: "\e628";
|
||||
}
|
||||
|
||||
.icon-light:before {
|
||||
content: "\e63a";
|
||||
}
|
||||
|
||||
.icon-gaojingdeng9:before {
|
||||
content: "\e664";
|
||||
}
|
||||
|
||||
.icon-jinggaodeng1:before {
|
||||
content: "\e613";
|
||||
}
|
||||
|
||||
.icon-yujing5:before {
|
||||
content: "\e671";
|
||||
}
|
||||
|
||||
.icon-shanchu:before {
|
||||
content: "\e74b";
|
||||
}
|
||||
|
||||
.icon-shanchu1:before {
|
||||
content: "\e625";
|
||||
}
|
||||
|
||||
.icon-shouye:before {
|
||||
content: "\e62d";
|
||||
}
|
||||
|
||||
.icon-chakan:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.icon-bianji:before {
|
||||
content: "\e602";
|
||||
}
|
||||
|
||||
.icon-fuzhi:before {
|
||||
content: "\e608";
|
||||
}
|
||||
|
||||
.icon-shouye-moren:before {
|
||||
content: "\e619";
|
||||
}
|
||||
|
||||
.icon-jiesuo:before {
|
||||
content: "\e669";
|
||||
}
|
||||
|
||||
.icon-xuanzhuan:before {
|
||||
content: "\e680";
|
||||
}
|
||||
|
||||
.icon-kaiguan:before {
|
||||
content: "\e61f";
|
||||
}
|
||||
|
||||
.icon-clock:before {
|
||||
content: "\e63e";
|
||||
}
|
||||
|
||||
.icon-fankuixinxi:before {
|
||||
content: "\e660";
|
||||
}
|
||||
|
||||
.icon-leijianzhuxiulix:before {
|
||||
content: "\e695";
|
||||
}
|
||||
|
||||
.icon-weibiaoti-:before {
|
||||
content: "\e618";
|
||||
}
|
||||
|
||||
.icon-sds_di37leijianzhuxiuli:before {
|
||||
content: "\e61e";
|
||||
}
|
||||
|
||||
.icon-liandongkongzhi:before {
|
||||
content: "\e624";
|
||||
}
|
||||
|
||||
.icon-unlock-full:before {
|
||||
content: "\e882";
|
||||
}
|
||||
|
||||
.icon-ascend:before {
|
||||
content: "\e6b4";
|
||||
}
|
||||
|
||||
.icon-falling:before {
|
||||
content: "\e6b6";
|
||||
}
|
||||
|
||||
.icon-buttonoff:before {
|
||||
content: "\e614";
|
||||
}
|
||||
|
||||
.icon-buttonon:before {
|
||||
content: "\e615";
|
||||
}
|
||||
|
||||
.icon-jiantoushang:before {
|
||||
content: "\e62c";
|
||||
}
|
||||
|
||||
.icon-jiantouyou:before {
|
||||
content: "\e632";
|
||||
}
|
||||
|
||||
.icon-jiantouxia:before {
|
||||
content: "\e633";
|
||||
}
|
||||
|
||||
.icon-24gl-next:before {
|
||||
content: "\ea6b";
|
||||
}
|
||||
|
||||
.icon-24gl-pauseCircle:before {
|
||||
content: "\ea6f";
|
||||
}
|
||||
|
||||
.icon-24gl-previous:before {
|
||||
content: "\ea73";
|
||||
}
|
||||
|
||||
.icon-24gl-stopCircle:before {
|
||||
content: "\ea79";
|
||||
}
|
||||
|
||||
.icon-anniu_kaiqi:before {
|
||||
content: "\e659";
|
||||
}
|
||||
|
||||
.icon-anniu_guanbi:before {
|
||||
content: "\e65b";
|
||||
}
|
||||
|
||||
.icon-jiantoushangzuo-copy:before {
|
||||
content: "\ec63";
|
||||
}
|
||||
|
||||
.icon-xiangzuo2-copy:before {
|
||||
content: "\ec64";
|
||||
}
|
||||
|
||||
.icon-guanbibofang:before {
|
||||
content: "\e61d";
|
||||
}
|
||||
|
||||
.icon-xiangshang3:before {
|
||||
content: "\e76f";
|
||||
}
|
||||
|
||||
.icon-xiangxia5:before {
|
||||
content: "\e774";
|
||||
}
|
||||
|
||||
.icon-xiangyou2:before {
|
||||
content: "\e776";
|
||||
}
|
||||
|
||||
.icon-youbofang:before {
|
||||
content: "\e62e";
|
||||
}
|
||||
|
||||
.icon-zuobofang:before {
|
||||
content: "\e62f";
|
||||
}
|
||||
|
||||
.icon-bofang2:before {
|
||||
content: "\e606";
|
||||
}
|
||||
|
||||
.icon-bofang5:before {
|
||||
content: "\e607";
|
||||
}
|
||||
|
||||
.icon-xiangzuojiaohuan:before {
|
||||
content: "\e8f8";
|
||||
}
|
||||
|
||||
.icon-xiangyoujiaohuan:before {
|
||||
content: "\e8f9";
|
||||
}
|
||||
|
||||
.icon-bofang6:before {
|
||||
content: "\e87c";
|
||||
}
|
||||
|
||||
.icon-24gl-playCircle:before {
|
||||
content: "\ea6e";
|
||||
}
|
||||
|
||||
.icon-xiangshang7:before {
|
||||
content: "\e63c";
|
||||
}
|
||||
|
||||
.icon-bofang26:before {
|
||||
content: "\e60e";
|
||||
}
|
||||
|
||||
.icon-play1:before {
|
||||
content: "\ea8d";
|
||||
}
|
||||
|
||||
.icon-bofang-kuaitui:before {
|
||||
content: "\e68a";
|
||||
}
|
||||
|
||||
.icon-bofang09:before {
|
||||
content: "\e690";
|
||||
}
|
||||
|
||||
.icon-bofang-kuaijin:before {
|
||||
content: "\e6d8";
|
||||
}
|
||||
|
||||
.icon-xiangxia7:before {
|
||||
content: "\ec62";
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,597 @@
|
|||
{
|
||||
"id": "2946854",
|
||||
"name": "方向",
|
||||
"font_family": "iconfont",
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "11291350",
|
||||
"name": "组",
|
||||
"font_class": "zu",
|
||||
"unicode": "e854",
|
||||
"unicode_decimal": 59476
|
||||
},
|
||||
{
|
||||
"icon_id": "1817751",
|
||||
"name": "柱形图",
|
||||
"font_class": "zhuxingtu",
|
||||
"unicode": "e626",
|
||||
"unicode_decimal": 58918
|
||||
},
|
||||
{
|
||||
"icon_id": "3868284",
|
||||
"name": "仪表盘",
|
||||
"font_class": "yibiaopan",
|
||||
"unicode": "eb67",
|
||||
"unicode_decimal": 60263
|
||||
},
|
||||
{
|
||||
"icon_id": "4354243",
|
||||
"name": "图表-饼图",
|
||||
"font_class": "tubiao-bingtu",
|
||||
"unicode": "eb95",
|
||||
"unicode_decimal": 60309
|
||||
},
|
||||
{
|
||||
"icon_id": "4354246",
|
||||
"name": "图表-卡片",
|
||||
"font_class": "tubiao-qiapian",
|
||||
"unicode": "eb96",
|
||||
"unicode_decimal": 60310
|
||||
},
|
||||
{
|
||||
"icon_id": "4354248",
|
||||
"name": "图表-折线图",
|
||||
"font_class": "tubiao-zhexiantu",
|
||||
"unicode": "eb97",
|
||||
"unicode_decimal": 60311
|
||||
},
|
||||
{
|
||||
"icon_id": "4906240",
|
||||
"name": "按钮组",
|
||||
"font_class": "anniuzu",
|
||||
"unicode": "e782",
|
||||
"unicode_decimal": 59266
|
||||
},
|
||||
{
|
||||
"icon_id": "5383645",
|
||||
"name": "导航",
|
||||
"font_class": "daohang",
|
||||
"unicode": "e77d",
|
||||
"unicode_decimal": 59261
|
||||
},
|
||||
{
|
||||
"icon_id": "5961366",
|
||||
"name": "列表",
|
||||
"font_class": "liebiao",
|
||||
"unicode": "ec6b",
|
||||
"unicode_decimal": 60523
|
||||
},
|
||||
{
|
||||
"icon_id": "5971297",
|
||||
"name": "采煤机",
|
||||
"font_class": "caimeiji",
|
||||
"unicode": "e603",
|
||||
"unicode_decimal": 58883
|
||||
},
|
||||
{
|
||||
"icon_id": "7040619",
|
||||
"name": "其他",
|
||||
"font_class": "qita",
|
||||
"unicode": "e63b",
|
||||
"unicode_decimal": 58939
|
||||
},
|
||||
{
|
||||
"icon_id": "7556180",
|
||||
"name": "定制",
|
||||
"font_class": "dingzhi",
|
||||
"unicode": "e609",
|
||||
"unicode_decimal": 58889
|
||||
},
|
||||
{
|
||||
"icon_id": "9752796",
|
||||
"name": "基础",
|
||||
"font_class": "jichu",
|
||||
"unicode": "e60f",
|
||||
"unicode_decimal": 58895
|
||||
},
|
||||
{
|
||||
"icon_id": "9921108",
|
||||
"name": "chart",
|
||||
"font_class": "chartt",
|
||||
"unicode": "e60a",
|
||||
"unicode_decimal": 58890
|
||||
},
|
||||
{
|
||||
"icon_id": "14475731",
|
||||
"name": "装饰装修",
|
||||
"font_class": "zhuangshizhuangxiu",
|
||||
"unicode": "e6a6",
|
||||
"unicode_decimal": 59046
|
||||
},
|
||||
{
|
||||
"icon_id": "16562592",
|
||||
"name": "温度计",
|
||||
"font_class": "wenduji",
|
||||
"unicode": "e6de",
|
||||
"unicode_decimal": 59102
|
||||
},
|
||||
{
|
||||
"icon_id": "22712019",
|
||||
"name": "条形进度图",
|
||||
"font_class": "tiaoxingjindutu",
|
||||
"unicode": "e66b",
|
||||
"unicode_decimal": 58987
|
||||
},
|
||||
{
|
||||
"icon_id": "22761375",
|
||||
"name": "边框",
|
||||
"font_class": "biankuang",
|
||||
"unicode": "e6b7",
|
||||
"unicode_decimal": 59063
|
||||
},
|
||||
{
|
||||
"icon_id": "22885432",
|
||||
"name": "进度图",
|
||||
"font_class": "jindutu",
|
||||
"unicode": "f24b",
|
||||
"unicode_decimal": 62027
|
||||
},
|
||||
{
|
||||
"icon_id": "124495",
|
||||
"name": "警告",
|
||||
"font_class": "jinggao1",
|
||||
"unicode": "e601",
|
||||
"unicode_decimal": 58881
|
||||
},
|
||||
{
|
||||
"icon_id": "1846445",
|
||||
"name": "预警灯",
|
||||
"font_class": "yujingdeng",
|
||||
"unicode": "e67f",
|
||||
"unicode_decimal": 59007
|
||||
},
|
||||
{
|
||||
"icon_id": "2239579",
|
||||
"name": "自动系统警告灯",
|
||||
"font_class": "zidongxitongjinggaodeng1",
|
||||
"unicode": "e605",
|
||||
"unicode_decimal": 58885
|
||||
},
|
||||
{
|
||||
"icon_id": "4988542",
|
||||
"name": "预警",
|
||||
"font_class": "yujing1",
|
||||
"unicode": "e644",
|
||||
"unicode_decimal": 58948
|
||||
},
|
||||
{
|
||||
"icon_id": "9974390",
|
||||
"name": "预警",
|
||||
"font_class": "yujing3",
|
||||
"unicode": "e6fe",
|
||||
"unicode_decimal": 59134
|
||||
},
|
||||
{
|
||||
"icon_id": "10817515",
|
||||
"name": "警告灯",
|
||||
"font_class": "jinggaodeng",
|
||||
"unicode": "e64b",
|
||||
"unicode_decimal": 58955
|
||||
},
|
||||
{
|
||||
"icon_id": "14151833",
|
||||
"name": "报警",
|
||||
"font_class": "baojing1",
|
||||
"unicode": "e630",
|
||||
"unicode_decimal": 58928
|
||||
},
|
||||
{
|
||||
"icon_id": "16965399",
|
||||
"name": "警告灯",
|
||||
"font_class": "hong",
|
||||
"unicode": "e60d",
|
||||
"unicode_decimal": 58893
|
||||
},
|
||||
{
|
||||
"icon_id": "20379044",
|
||||
"name": "预警灯",
|
||||
"font_class": "yujingdeng1",
|
||||
"unicode": "e623",
|
||||
"unicode_decimal": 58915
|
||||
},
|
||||
{
|
||||
"icon_id": "23819336",
|
||||
"name": "报警 警告 预警 警告灯",
|
||||
"font_class": "a-baojingjinggaoyujingjinggaodeng",
|
||||
"unicode": "e628",
|
||||
"unicode_decimal": 58920
|
||||
},
|
||||
{
|
||||
"icon_id": "24312218",
|
||||
"name": "light",
|
||||
"font_class": "light",
|
||||
"unicode": "e63a",
|
||||
"unicode_decimal": 58938
|
||||
},
|
||||
{
|
||||
"icon_id": "24614518",
|
||||
"name": "告警灯",
|
||||
"font_class": "gaojingdeng9",
|
||||
"unicode": "e664",
|
||||
"unicode_decimal": 58980
|
||||
},
|
||||
{
|
||||
"icon_id": "25567211",
|
||||
"name": "警告灯",
|
||||
"font_class": "jinggaodeng1",
|
||||
"unicode": "e613",
|
||||
"unicode_decimal": 58899
|
||||
},
|
||||
{
|
||||
"icon_id": "12041083",
|
||||
"name": "预警",
|
||||
"font_class": "yujing5",
|
||||
"unicode": "e671",
|
||||
"unicode_decimal": 58993
|
||||
},
|
||||
{
|
||||
"icon_id": "577357",
|
||||
"name": "删除",
|
||||
"font_class": "shanchu",
|
||||
"unicode": "e74b",
|
||||
"unicode_decimal": 59211
|
||||
},
|
||||
{
|
||||
"icon_id": "1986988",
|
||||
"name": "删除",
|
||||
"font_class": "shanchu1",
|
||||
"unicode": "e625",
|
||||
"unicode_decimal": 58917
|
||||
},
|
||||
{
|
||||
"icon_id": "12795401",
|
||||
"name": "首页",
|
||||
"font_class": "shouye",
|
||||
"unicode": "e62d",
|
||||
"unicode_decimal": 58925
|
||||
},
|
||||
{
|
||||
"icon_id": "201556",
|
||||
"name": "查看",
|
||||
"font_class": "chakan",
|
||||
"unicode": "e600",
|
||||
"unicode_decimal": 58880
|
||||
},
|
||||
{
|
||||
"icon_id": "201638",
|
||||
"name": "编辑",
|
||||
"font_class": "bianji",
|
||||
"unicode": "e602",
|
||||
"unicode_decimal": 58882
|
||||
},
|
||||
{
|
||||
"icon_id": "11729723",
|
||||
"name": "复制",
|
||||
"font_class": "fuzhi",
|
||||
"unicode": "e608",
|
||||
"unicode_decimal": 58888
|
||||
},
|
||||
{
|
||||
"icon_id": "7269302",
|
||||
"name": "首页-默认",
|
||||
"font_class": "shouye-moren",
|
||||
"unicode": "e619",
|
||||
"unicode_decimal": 58905
|
||||
},
|
||||
{
|
||||
"icon_id": "658044",
|
||||
"name": "解锁",
|
||||
"font_class": "jiesuo",
|
||||
"unicode": "e669",
|
||||
"unicode_decimal": 58985
|
||||
},
|
||||
{
|
||||
"icon_id": "10097949",
|
||||
"name": "旋转",
|
||||
"font_class": "xuanzhuan",
|
||||
"unicode": "e680",
|
||||
"unicode_decimal": 59008
|
||||
},
|
||||
{
|
||||
"icon_id": "374774",
|
||||
"name": "开关",
|
||||
"font_class": "kaiguan",
|
||||
"unicode": "e61f",
|
||||
"unicode_decimal": 58911
|
||||
},
|
||||
{
|
||||
"icon_id": "122162",
|
||||
"name": "时钟",
|
||||
"font_class": "clock",
|
||||
"unicode": "e63e",
|
||||
"unicode_decimal": 58942
|
||||
},
|
||||
{
|
||||
"icon_id": "1242185",
|
||||
"name": "反馈信息",
|
||||
"font_class": "fankuixinxi",
|
||||
"unicode": "e660",
|
||||
"unicode_decimal": 58976
|
||||
},
|
||||
{
|
||||
"icon_id": "2468505",
|
||||
"name": "37类建筑修理x16",
|
||||
"font_class": "leijianzhuxiulix",
|
||||
"unicode": "e695",
|
||||
"unicode_decimal": 59029
|
||||
},
|
||||
{
|
||||
"icon_id": "11747267",
|
||||
"name": "时间",
|
||||
"font_class": "weibiaoti-",
|
||||
"unicode": "e618",
|
||||
"unicode_decimal": 58904
|
||||
},
|
||||
{
|
||||
"icon_id": "14794143",
|
||||
"name": "sds_第37类 建筑修理",
|
||||
"font_class": "sds_di37leijianzhuxiuli",
|
||||
"unicode": "e61e",
|
||||
"unicode_decimal": 58910
|
||||
},
|
||||
{
|
||||
"icon_id": "17762035",
|
||||
"name": "联动控制",
|
||||
"font_class": "liandongkongzhi",
|
||||
"unicode": "e624",
|
||||
"unicode_decimal": 58916
|
||||
},
|
||||
{
|
||||
"icon_id": "18165278",
|
||||
"name": "锁,密码,开锁,解锁",
|
||||
"font_class": "unlock-full",
|
||||
"unicode": "e882",
|
||||
"unicode_decimal": 59522
|
||||
},
|
||||
{
|
||||
"icon_id": "288554",
|
||||
"name": "ascend",
|
||||
"font_class": "ascend",
|
||||
"unicode": "e6b4",
|
||||
"unicode_decimal": 59060
|
||||
},
|
||||
{
|
||||
"icon_id": "288556",
|
||||
"name": "falling",
|
||||
"font_class": "falling",
|
||||
"unicode": "e6b6",
|
||||
"unicode_decimal": 59062
|
||||
},
|
||||
{
|
||||
"icon_id": "815867",
|
||||
"name": "按钮-关",
|
||||
"font_class": "buttonoff",
|
||||
"unicode": "e614",
|
||||
"unicode_decimal": 58900
|
||||
},
|
||||
{
|
||||
"icon_id": "815872",
|
||||
"name": "按钮-开",
|
||||
"font_class": "buttonon",
|
||||
"unicode": "e615",
|
||||
"unicode_decimal": 58901
|
||||
},
|
||||
{
|
||||
"icon_id": "2674878",
|
||||
"name": "箭头 上",
|
||||
"font_class": "jiantoushang",
|
||||
"unicode": "e62c",
|
||||
"unicode_decimal": 58924
|
||||
},
|
||||
{
|
||||
"icon_id": "2674928",
|
||||
"name": "箭头 右",
|
||||
"font_class": "jiantouyou",
|
||||
"unicode": "e632",
|
||||
"unicode_decimal": 58930
|
||||
},
|
||||
{
|
||||
"icon_id": "2674929",
|
||||
"name": "箭头 下",
|
||||
"font_class": "jiantouxia",
|
||||
"unicode": "e633",
|
||||
"unicode_decimal": 58931
|
||||
},
|
||||
{
|
||||
"icon_id": "7594038",
|
||||
"name": "24gl-next",
|
||||
"font_class": "24gl-next",
|
||||
"unicode": "ea6b",
|
||||
"unicode_decimal": 60011
|
||||
},
|
||||
{
|
||||
"icon_id": "7594046",
|
||||
"name": "24gl-pauseCircle",
|
||||
"font_class": "24gl-pauseCircle",
|
||||
"unicode": "ea6f",
|
||||
"unicode_decimal": 60015
|
||||
},
|
||||
{
|
||||
"icon_id": "7594068",
|
||||
"name": "24gl-previous",
|
||||
"font_class": "24gl-previous",
|
||||
"unicode": "ea73",
|
||||
"unicode_decimal": 60019
|
||||
},
|
||||
{
|
||||
"icon_id": "7594086",
|
||||
"name": "24gl-stopCircle",
|
||||
"font_class": "24gl-stopCircle",
|
||||
"unicode": "ea79",
|
||||
"unicode_decimal": 60025
|
||||
},
|
||||
{
|
||||
"icon_id": "10268256",
|
||||
"name": "按钮_开启",
|
||||
"font_class": "anniu_kaiqi",
|
||||
"unicode": "e659",
|
||||
"unicode_decimal": 58969
|
||||
},
|
||||
{
|
||||
"icon_id": "10268257",
|
||||
"name": "按钮_关闭",
|
||||
"font_class": "anniu_guanbi",
|
||||
"unicode": "e65b",
|
||||
"unicode_decimal": 58971
|
||||
},
|
||||
{
|
||||
"icon_id": "25807963",
|
||||
"name": "箭头 上",
|
||||
"font_class": "jiantoushangzuo-copy",
|
||||
"unicode": "ec63",
|
||||
"unicode_decimal": 60515
|
||||
},
|
||||
{
|
||||
"icon_id": "25808146",
|
||||
"name": "向右2",
|
||||
"font_class": "xiangzuo2-copy",
|
||||
"unicode": "ec64",
|
||||
"unicode_decimal": 60516
|
||||
},
|
||||
{
|
||||
"icon_id": "166602",
|
||||
"name": "关闭播放",
|
||||
"font_class": "guanbibofang",
|
||||
"unicode": "e61d",
|
||||
"unicode_decimal": 58909
|
||||
},
|
||||
{
|
||||
"icon_id": "577396",
|
||||
"name": "向上3",
|
||||
"font_class": "xiangshang3",
|
||||
"unicode": "e76f",
|
||||
"unicode_decimal": 59247
|
||||
},
|
||||
{
|
||||
"icon_id": "577401",
|
||||
"name": "向下5",
|
||||
"font_class": "xiangxia5",
|
||||
"unicode": "e774",
|
||||
"unicode_decimal": 59252
|
||||
},
|
||||
{
|
||||
"icon_id": "577403",
|
||||
"name": "向右2",
|
||||
"font_class": "xiangyou2",
|
||||
"unicode": "e776",
|
||||
"unicode_decimal": 59254
|
||||
},
|
||||
{
|
||||
"icon_id": "1159910",
|
||||
"name": "右播放",
|
||||
"font_class": "youbofang",
|
||||
"unicode": "e62e",
|
||||
"unicode_decimal": 58926
|
||||
},
|
||||
{
|
||||
"icon_id": "1159911",
|
||||
"name": "左播放",
|
||||
"font_class": "zuobofang",
|
||||
"unicode": "e62f",
|
||||
"unicode_decimal": 58927
|
||||
},
|
||||
{
|
||||
"icon_id": "1185945",
|
||||
"name": "播放",
|
||||
"font_class": "bofang2",
|
||||
"unicode": "e606",
|
||||
"unicode_decimal": 58886
|
||||
},
|
||||
{
|
||||
"icon_id": "1488892",
|
||||
"name": "播放",
|
||||
"font_class": "bofang5",
|
||||
"unicode": "e607",
|
||||
"unicode_decimal": 58887
|
||||
},
|
||||
{
|
||||
"icon_id": "1727559",
|
||||
"name": "323向左交换",
|
||||
"font_class": "xiangzuojiaohuan",
|
||||
"unicode": "e8f8",
|
||||
"unicode_decimal": 59640
|
||||
},
|
||||
{
|
||||
"icon_id": "1727561",
|
||||
"name": "324向右交换",
|
||||
"font_class": "xiangyoujiaohuan",
|
||||
"unicode": "e8f9",
|
||||
"unicode_decimal": 59641
|
||||
},
|
||||
{
|
||||
"icon_id": "2076218",
|
||||
"name": "播放2",
|
||||
"font_class": "bofang6",
|
||||
"unicode": "e87c",
|
||||
"unicode_decimal": 59516
|
||||
},
|
||||
{
|
||||
"icon_id": "7594051",
|
||||
"name": "24gl-playCircle",
|
||||
"font_class": "24gl-playCircle",
|
||||
"unicode": "ea6e",
|
||||
"unicode_decimal": 60014
|
||||
},
|
||||
{
|
||||
"icon_id": "11121478",
|
||||
"name": "向上",
|
||||
"font_class": "xiangshang7",
|
||||
"unicode": "e63c",
|
||||
"unicode_decimal": 58940
|
||||
},
|
||||
{
|
||||
"icon_id": "16388177",
|
||||
"name": "播放",
|
||||
"font_class": "bofang26",
|
||||
"unicode": "e60e",
|
||||
"unicode_decimal": 58894
|
||||
},
|
||||
{
|
||||
"icon_id": "18176557",
|
||||
"name": "播放",
|
||||
"font_class": "play1",
|
||||
"unicode": "ea8d",
|
||||
"unicode_decimal": 60045
|
||||
},
|
||||
{
|
||||
"icon_id": "24267227",
|
||||
"name": "播放-快退",
|
||||
"font_class": "bofang-kuaitui",
|
||||
"unicode": "e68a",
|
||||
"unicode_decimal": 59018
|
||||
},
|
||||
{
|
||||
"icon_id": "24267260",
|
||||
"name": "播放09",
|
||||
"font_class": "bofang09",
|
||||
"unicode": "e690",
|
||||
"unicode_decimal": 59024
|
||||
},
|
||||
{
|
||||
"icon_id": "24268374",
|
||||
"name": "播放-快进",
|
||||
"font_class": "bofang-kuaijin",
|
||||
"unicode": "e6d8",
|
||||
"unicode_decimal": 59096
|
||||
},
|
||||
{
|
||||
"icon_id": "25807740",
|
||||
"name": "向上",
|
||||
"font_class": "xiangxia7",
|
||||
"unicode": "ec62",
|
||||
"unicode_decimal": 60514
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Group 5 Copy 5</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox"
|
||||
id="filter-1">
|
||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1"
|
||||
result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix"
|
||||
in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||
<feMergeNode in="SourceGraphic"></feMergeNode>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
|
||||
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox"
|
||||
id="filter-4">
|
||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1"
|
||||
result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix"
|
||||
in="shadowBlurOuter1"></feColorMatrix>
|
||||
</filter>
|
||||
</defs>
|
||||
<g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="setting-copy-2" transform="translate(-1190.000000, -136.000000)">
|
||||
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
|
||||
<g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
|
||||
<mask id="mask-3" fill="white">
|
||||
<use xlink:href="#path-2"></use>
|
||||
</mask>
|
||||
<g id="Rectangle-18">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
|
||||
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
|
||||
</g>
|
||||
<rect id="Rectangle-11" fill="#303648" mask="url(#mask-3)" x="-1" y="0" width="49"
|
||||
height="10"></rect>
|
||||
<rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16"
|
||||
height="44"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Group 5 Copy 5</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox"
|
||||
id="filter-1">
|
||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1"
|
||||
result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix"
|
||||
in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||
<feMergeNode in="SourceGraphic"></feMergeNode>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
|
||||
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox"
|
||||
id="filter-4">
|
||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1"
|
||||
result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix"
|
||||
in="shadowBlurOuter1"></feColorMatrix>
|
||||
</filter>
|
||||
</defs>
|
||||
<g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="setting-copy-2" transform="translate(-1190.000000, -136.000000)">
|
||||
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
|
||||
<g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
|
||||
<mask id="mask-3" fill="white">
|
||||
<use xlink:href="#path-2"></use>
|
||||
</mask>
|
||||
<g id="Rectangle-18">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
|
||||
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
|
||||
</g>
|
||||
<rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="-1" y="0" width="49"
|
||||
height="10"></rect>
|
||||
<rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16"
|
||||
height="44"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Group 5</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox"
|
||||
id="filter-1">
|
||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1"
|
||||
result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix"
|
||||
in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||
<feMergeNode in="SourceGraphic"></feMergeNode>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
|
||||
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox"
|
||||
id="filter-4">
|
||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1"
|
||||
result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix"
|
||||
in="shadowBlurOuter1"></feColorMatrix>
|
||||
</filter>
|
||||
</defs>
|
||||
<g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="setting-copy-2" transform="translate(-1254.000000, -136.000000)">
|
||||
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
|
||||
<g id="Group-5" filter="url(#filter-1)" transform="translate(89.000000, 137.000000)">
|
||||
<mask id="mask-3" fill="white">
|
||||
<use xlink:href="#path-2"></use>
|
||||
</mask>
|
||||
<g id="Rectangle-18">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
|
||||
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
|
||||
</g>
|
||||
<rect id="Rectangle-18" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="16"
|
||||
height="44"></rect>
|
||||
<rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="-1" y="0" width="49"
|
||||
height="10"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
|
@ -0,0 +1,155 @@
|
|||
<template>
|
||||
<div class="editor">
|
||||
<div class="tool-bar">
|
||||
<slot name="tool-bar"> </slot>
|
||||
</div>
|
||||
<div class="main" :style="{ maxHeight: codemirrorConfig.height }">
|
||||
<codemirror
|
||||
:model-value="value || ''"
|
||||
:style="{
|
||||
width: '100%',
|
||||
height: codemirrorConfig.height,
|
||||
backgroundColor: '#fff',
|
||||
color: '#333'
|
||||
}"
|
||||
placeholder="Please enter the code."
|
||||
:extensions="extensions"
|
||||
:autofocus="codemirrorConfig.autofocus"
|
||||
:disabled="codemirrorConfig.disabled"
|
||||
:indent-with-tab="codemirrorConfig.indentWithTab"
|
||||
:tab-size="codemirrorConfig.tabSize"
|
||||
@ready="handleReady"
|
||||
@focus="log('focus', $event)"
|
||||
@blur="log('blur', $event)"
|
||||
@update:model-value="codeChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="footer"><slot name="footer"></slot></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { redo, undo } from '@codemirror/commands'
|
||||
import { javascript } from '@codemirror/lang-javascript'
|
||||
import type { EditorState, Extension } from '@codemirror/state'
|
||||
import { oneDark } from '@codemirror/theme-one-dark'
|
||||
import type { EditorView, ViewUpdate } from '@codemirror/view'
|
||||
import type { ComputedRef } from 'vue'
|
||||
import { computed, inject } from 'vue'
|
||||
import { Codemirror } from 'vue-codemirror'
|
||||
|
||||
import type { CodemirrorOption } from './type'
|
||||
|
||||
const Logger = console
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
value?: string
|
||||
height?: string
|
||||
disabled?: boolean
|
||||
mode?: 'debug' | 'use'
|
||||
}>(),
|
||||
{
|
||||
value: '',
|
||||
height: '600px',
|
||||
disabled: false,
|
||||
mode: 'use'
|
||||
}
|
||||
)
|
||||
|
||||
const codemirrorConfig = computed<CodemirrorOption>(() => {
|
||||
return {
|
||||
height: props.height,
|
||||
tabSize: 4,
|
||||
indentWithTab: true,
|
||||
autofocus: true,
|
||||
disabled: props.disabled,
|
||||
line: false
|
||||
}
|
||||
})
|
||||
const emits = defineEmits<{
|
||||
(e: 'update:value', value: string): void
|
||||
(e: 'change', value: string, viewUpdate: ViewUpdate): void
|
||||
}>()
|
||||
|
||||
let cmView: EditorView
|
||||
const darkTheme = inject<ComputedRef<boolean>>(
|
||||
'DarkTheme',
|
||||
computed(() => true)
|
||||
)
|
||||
const extensions = computed(() => {
|
||||
const result: Extension[] = [javascript()]
|
||||
if (darkTheme.value) {
|
||||
result.push(oneDark)
|
||||
}
|
||||
return result
|
||||
})
|
||||
|
||||
const handleReady = ({
|
||||
view,
|
||||
state: _state,
|
||||
container: _container
|
||||
}: {
|
||||
view: EditorView
|
||||
state: EditorState
|
||||
container: HTMLDivElement
|
||||
}) => {
|
||||
cmView = view
|
||||
return true
|
||||
}
|
||||
const log = Logger.log
|
||||
const codeChange = (value: string, viewUpdate: ViewUpdate) => {
|
||||
emits('update:value', value)
|
||||
emits('change', value, viewUpdate)
|
||||
return true
|
||||
}
|
||||
|
||||
const handleRedo = () => {
|
||||
if (cmView) {
|
||||
redo({
|
||||
state: cmView.state,
|
||||
dispatch: cmView.dispatch
|
||||
})
|
||||
}
|
||||
}
|
||||
const handleUndo = () => {
|
||||
if (cmView) {
|
||||
undo({
|
||||
state: cmView.state,
|
||||
dispatch: cmView.dispatch
|
||||
})
|
||||
}
|
||||
}
|
||||
defineExpose({ handleRedo, handleUndo })
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.editor {
|
||||
.main {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
::-webkit-scrollbar {
|
||||
/*滚动条整体样式*/
|
||||
width: 6px; /*高宽分别对应横竖滚动条的尺寸*/
|
||||
height: 6px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #8b8b8b;
|
||||
-webkit-border-radius: 2em;
|
||||
-moz-border-radius: 2em;
|
||||
border-radius: 2em;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.code {
|
||||
width: 30%;
|
||||
height: 100px;
|
||||
margin: 0;
|
||||
padding: 0.4em;
|
||||
font-family: monospace;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||