Compare commits

..

4 Commits

Author SHA1 Message Date
奔跑的面条
ddee396cea Merge branch 'dev' into dev-feat-multi-select 2022-07-07 19:56:12 +08:00
奔跑的面条
84fd1b2181 Merge branch 'dev' into dev-feat-multi-select 2022-07-07 13:15:13 +08:00
奔跑的面条
a58eb4a53c Merge branch 'dev' into dev-feat-multi-select 2022-07-06 21:51:11 +08:00
奔跑的面条
3a066fc9bb feat: 新增组件多选和右键成组按钮 2022-06-28 21:57:29 +08:00
115 changed files with 973 additions and 3087 deletions

12
.env
View File

@@ -1,8 +1,14 @@
# port # port
VITE_DEV_PORT = '8080' VITE_DEV_PORT = '8001'
# development path # development path
VITE_DEV_PATH = 'http://1.117.240.165:8080' VITE_DEV_PATH = '/'
# production path # production path
VITE_PRO_PATH = 'http://1.117.240.165:8080' VITE_PRO_PATH = '/'
# spa-title
VITE_GLOB_APP_TITLE = GoView
# spa shortname
VITE_GLOB_APP_SHORT_NAME = GoView

183
README.md
View File

@@ -2,121 +2,128 @@
![logo](readme/logo-t-y.png) ![logo](readme/logo-t-y.png)
**`master-fetch` 分支是带有后端接口请求的分支** GoView 是一个高效的拖拽式低代码数据可视化开发平台,将图表或页面元素封装为基础组件,无需编写代码即可制作数据大屏,减少心智负担。
**后端项目地址:[https://gitee.com/MTrun/go-view-serve](https://gitee.com/MTrun/go-view-serve)** ### 纯 **😶 前端** 分支: **`master`**
**接口说明地址:[https://docs.apipost.cn/preview/5aa85d10a59d66ce/ddb813732007ad2b?target_id=84dbc5b0-158f-4bcb-8f74-793ac604ada3#3e053622-1e76-43f9-a039-756aee822dbb](https://docs.apipost.cn/preview/5aa85d10a59d66ce/ddb813732007ad2b?target_id=84dbc5b0-158f-4bcb-8f74-793ac604ada3#3e053622-1e76-43f9-a039-756aee822dbb)** ### 携带 **👻 后端** 请求分支: **`master-fetch`**
## 使用 项目纯前端-Demo 地址:[https://www.mtruning.club](https://www.mtruning.club)
所有的接口地址位置:`src\api\path\*` 项目带后端-Demo 地址:[后端 Demo 地址](http://1.117.240.165:8080/goview/#/login)
接口地址修改:`.env` 文档-在线地址:[http://www.mtruning.club:81/](http://www.mtruning.club:81/)
### 🤯 后端项目 文档-源码地址:[https://gitee.com/MTrun/go-view-doc](https://gitee.com/MTrun/go-view-doc)
后端项目gitee地址[https://gitee.com/MTrun/go-view-serve](https://gitee.com/MTrun/go-view-serve) 技术点:
接口说明地址:[https://docs.apipost.cn/preview/5aa85d10a59d66ce/ddb813732007ad2b?target_id=84dbc5b0-158f-4bcb-8f74-793ac604ada3#3e053622-1e76-43f9-a039-756aee822dbb](https://docs.apipost.cn/preview/5aa85d10a59d66ce/ddb813732007ad2b?target_id=84dbc5b0-158f-4bcb-8f74-793ac604ada3#3e053622-1e76-43f9-a039-756aee822dbb) - 框架:基于 `Vue3` 框架编写,使用 `hooks` 写法抽离部分逻辑,使代码结构更加清晰;
- 类型:使用 `TypeScript` 进行类型约束,减少未知错误发生概率,可以大胆修改逻辑内容;
- 性能:多处性能优化,使用页面懒加载、组件动态注册、数据滚动加载等方式,提升页面渲染速度;
- 存储:拥有本地记忆,部分配置项采用 `storage` 存储本地,提升使用体验;
- 封装:项目进行了详细的工具类封装如:路由、存储、加/解密、文件处理、主题、NaiveUI 全局方法、组件等
项目截图:
![项目截图](readme/goView-canvas.png)
主要技术栈为:
| 名称 | 版本 | 名称 | 版本 |
| ------------------- | ----- | ----------- | ------ |
| Vue | 3.2.x | TypeScript4 | 4.6.x |
| Vite | 2.9.x | NaiveUI | 2.27.x |
| ECharts | 5.3.x | Pinia | 2.0.x |
| 详见 `package.json` | 😁 | 🥰 | 🤗 |
开发环境:
| 名称 | 版本 | 名称 | 版本 |
| ---- | ------- | ------- | ----- |
| node | 16.14.x | npm | 8.5.x |
| pnpm | 7.1.x | windows | 11 |
已完成图表:
| 分类 | 名称 | 名称 | 名称 |
| ------ | ---------------- | ---------------- | -------- |
| 图表 | 柱状图 | 横向柱状图 | 折线图 |
| \* | 单/多 折线面积图 | 饼图 | 水球图 |
| \* | 环形图 | NaiveUI 多种进度 | 🤠 |
| 信息 | 文字 | 图片 | 😶 |
| 列表 | 滚动排名列表 | 滚动表格 | 🤓 |
| 小组件 | 边框-01~13 | 装饰-01~05 | 数字翻牌 |
## 浏览器支持
开发和测试平台均在 `Google` 和最新版 `EDGE` 上完成,暂未测试 `IE11` 等其它浏览器,如有需求请自行测试与兼容。
## 安装
本项目采用` pnpm` 进行包管理
```shell ```shell
# port #建议使用 nrm 切换到淘宝源 https://registry.npmmirror.com/
VITE_DEV_PORT = '8080' #pnpm
pnpm install
# development path #yarn
VITE_DEV_PATH = 'http://127.0.0.1:8080' yarn install
# production path #npm
VITE_PRO_PATH = 'http://127.0.0.1:8080' npm install
``` ```
公共前缀修改:`src\settings\httpSetting.ts` ## 启动
```shell ```shell
// 请求前缀 #pnpm
export const axiosPre = '/api/goview' pnpm dev
# npm
npm run dev
#yarn
yarn dev
#Makefile
make dev
``` ```
接口封装:`src\api\http.ts` ## 编译
```ts ```shell
import axiosInstance from './axios' #pnpm
import { RequestHttpEnum, ContentTypeEnum } from '@/enums/httpEnum' pnpm run build
export const get = (url: string, params?: object) => { # npm
return axiosInstance({ npm run build
url: url,
method: RequestHttpEnum.GET,
params: params,
})
}
export const post = (url: string, data?: object, headersType?: string) => { #yarn
return axiosInstance({ yarn run build
url: url,
method: RequestHttpEnum.POST,
data: data,
headers: {
'Content-Type': headersType || ContentTypeEnum.JSON
}
})
}
export const put = (url: string, data?: object, headersType?: string) => { #Makefile
return axiosInstance({ make dist
url: url,
method: RequestHttpEnum.PUT,
data: data,
headers: {
'Content-Type': headersType || ContentTypeEnum.JSON
}
})
}
export const del = (url: string, params?: object) => {
return axiosInstance({
url: url,
method: RequestHttpEnum.DELETE,
params
})
}
// 获取请求函数默认get
export const http = (type?: RequestHttpEnum) => {
switch (type) {
case RequestHttpEnum.GET:
return get
case RequestHttpEnum.POST:
return post
case RequestHttpEnum.PUT:
return put
case RequestHttpEnum.DELETE:
return del
default:
return get
}
}
``` ```
## 代码提交 ## 代码提交
* feat: 新功能 - feat: 新功能
* fix: 修复 Bug - fix: 修复 Bug
* docs: 文档修改 - docs: 文档修改
* perf: 性能优化 - perf: 性能优化
* revert: 版本回退 - revert: 版本回退
* ci: CICD集成相关 - ci: CICD 集成相关
* test: 添加测试代码 - test: 添加测试代码
* refactor: 代码重构 - refactor: 代码重构
* build: 影响项目构建或依赖修改 - build: 影响项目构建或依赖修改
* style: 不影响程序逻辑的代码修改 - style: 不影响程序逻辑的代码修改
* chore: 不属于以上类型的其他类型(日常事务) - chore: 不属于以上类型的其他类型(日常事务)
## 交流 ## 交流

View File

@@ -0,0 +1,9 @@
/**
* Get the configuration file variable name
* @param env
*/
export const getConfigFileName = (env: Record<string, any>) => {
return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`
.toUpperCase()
.replace(/\s/g, '');
};

View File

@@ -1,6 +1,6 @@
{ {
"name": "go-view", "name": "go-view",
"version": "2.0.3", "version": "1.0.4",
"scripts": { "scripts": {
"dev": "vite --host", "dev": "vite --host",
"build": "vue-tsc --noEmit && vite build", "build": "vue-tsc --noEmit && vite build",
@@ -13,7 +13,7 @@
"@types/crypto-js": "^4.1.1", "@types/crypto-js": "^4.1.1",
"@types/keymaster": "^1.6.30", "@types/keymaster": "^1.6.30",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"axios": "^0.27.2", "axios": "0.23.0",
"color": "^4.2.3", "color": "^4.2.3",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"echarts-liquidfill": "^3.1.0", "echarts-liquidfill": "^3.1.0",
@@ -26,7 +26,7 @@
"screenfull": "^6.0.1", "screenfull": "^6.0.1",
"vue": "^3.2.31", "vue": "^3.2.31",
"vue-demi": "^0.13.1", "vue-demi": "^0.13.1",
"vue-i18n": "9.1.10", "vue-i18n": "9.1.9",
"vue-router": "4.0.12", "vue-router": "4.0.12",
"vue3-lazyload": "^0.2.5-beta", "vue3-lazyload": "^0.2.5-beta",
"vue3-sketch-ruler": "^1.3.3", "vue3-sketch-ruler": "^1.3.3",

72
pnpm-lock.yaml generated
View File

@@ -51,7 +51,7 @@ specifiers:
vue: ^3.2.31 vue: ^3.2.31
vue-demi: ^0.13.1 vue-demi: ^0.13.1
vue-echarts: ^6.0.2 vue-echarts: ^6.0.2
vue-i18n: 9.1.10 vue-i18n: 9.1.9
vue-router: 4.0.12 vue-router: 4.0.12
vue-tsc: ^0.28.10 vue-tsc: ^0.28.10
vue3-lazyload: ^0.2.5-beta vue3-lazyload: ^0.2.5-beta
@@ -76,7 +76,7 @@ dependencies:
screenfull: 6.0.1 screenfull: 6.0.1
vue: 3.2.37 vue: 3.2.37
vue-demi: 0.13.1_vue@3.2.37 vue-demi: 0.13.1_vue@3.2.37
vue-i18n: 9.1.10_vue@3.2.37 vue-i18n: 9.1.9_vue@3.2.37
vue-router: 4.0.12_vue@3.2.37 vue-router: 4.0.12_vue@3.2.37
vue3-lazyload: 0.2.5-beta_2yymnzrok6eda47acnj2yjm3ae vue3-lazyload: 0.2.5-beta_2yymnzrok6eda47acnj2yjm3ae
vue3-sketch-ruler: 1.3.4_vue@3.2.37 vue3-sketch-ruler: 1.3.4_vue@3.2.37
@@ -647,60 +647,60 @@ packages:
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
dev: true dev: true
/@intlify/core-base/9.1.10: /@intlify/core-base/9.1.9:
resolution: {integrity: sha512-So9CNUavB/IsZ+zBmk2Cv6McQp6vc2wbGi1S0XQmJ8Vz+UFcNn9MFXAe9gY67PreIHrbLsLxDD0cwo1qsxM1Nw==} resolution: {integrity: sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
dependencies: dependencies:
'@intlify/devtools-if': 9.1.10 '@intlify/devtools-if': 9.1.9
'@intlify/message-compiler': 9.1.10 '@intlify/message-compiler': 9.1.9
'@intlify/message-resolver': 9.1.10 '@intlify/message-resolver': 9.1.9
'@intlify/runtime': 9.1.10 '@intlify/runtime': 9.1.9
'@intlify/shared': 9.1.10 '@intlify/shared': 9.1.9
'@intlify/vue-devtools': 9.1.10 '@intlify/vue-devtools': 9.1.9
dev: false dev: false
/@intlify/devtools-if/9.1.10: /@intlify/devtools-if/9.1.9:
resolution: {integrity: sha512-SHaKoYu6sog3+Q8js1y3oXLywuogbH1sKuc7NSYkN3GElvXSBaMoCzW+we0ZSFqj/6c7vTNLg9nQ6rxhKqYwnQ==} resolution: {integrity: sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
dependencies: dependencies:
'@intlify/shared': 9.1.10 '@intlify/shared': 9.1.9
dev: false dev: false
/@intlify/message-compiler/9.1.10: /@intlify/message-compiler/9.1.9:
resolution: {integrity: sha512-+JiJpXff/XTb0EadYwdxOyRTB0hXNd4n1HaJ/a4yuV960uRmPXaklJsedW0LNdcptd/hYUZtCkI7Lc9J5C1gxg==} resolution: {integrity: sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
dependencies: dependencies:
'@intlify/message-resolver': 9.1.10 '@intlify/message-resolver': 9.1.9
'@intlify/shared': 9.1.10 '@intlify/shared': 9.1.9
source-map: 0.6.1 source-map: 0.6.1
dev: false dev: false
/@intlify/message-resolver/9.1.10: /@intlify/message-resolver/9.1.9:
resolution: {integrity: sha512-5YixMG/M05m0cn9+gOzd4EZQTFRUu8RGhzxJbR1DWN21x/Z3bJ8QpDYj6hC4FwBj5uKsRfKpJQ3Xqg98KWoA+w==} resolution: {integrity: sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
dev: false dev: false
/@intlify/runtime/9.1.10: /@intlify/runtime/9.1.9:
resolution: {integrity: sha512-7QsuByNzpe3Gfmhwq6hzgXcMPpxz8Zxb/XFI6s9lQdPLPe5Lgw4U1ovRPZTOs6Y2hwitR3j/HD8BJNGWpJnOFA==} resolution: {integrity: sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
dependencies: dependencies:
'@intlify/message-compiler': 9.1.10 '@intlify/message-compiler': 9.1.9
'@intlify/message-resolver': 9.1.10 '@intlify/message-resolver': 9.1.9
'@intlify/shared': 9.1.10 '@intlify/shared': 9.1.9
dev: false dev: false
/@intlify/shared/9.1.10: /@intlify/shared/9.1.9:
resolution: {integrity: sha512-Om54xJeo1Vw+K1+wHYyXngE8cAbrxZHpWjYzMR9wCkqbhGtRV5VLhVc214Ze2YatPrWlS2WSMOWXR8JktX/IgA==} resolution: {integrity: sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
dev: false dev: false
/@intlify/vue-devtools/9.1.10: /@intlify/vue-devtools/9.1.9:
resolution: {integrity: sha512-5l3qYARVbkWAkagLu1XbDUWRJSL8br1Dj60wgMaKB0+HswVsrR6LloYZTg7ozyvM621V6+zsmwzbQxbVQyrytQ==} resolution: {integrity: sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
dependencies: dependencies:
'@intlify/message-resolver': 9.1.10 '@intlify/message-resolver': 9.1.9
'@intlify/runtime': 9.1.10 '@intlify/runtime': 9.1.9
'@intlify/shared': 9.1.10 '@intlify/shared': 9.1.9
dev: false dev: false
/@jridgewell/gen-mapping/0.1.1: /@jridgewell/gen-mapping/0.1.1:
@@ -5339,15 +5339,15 @@ packages:
- supports-color - supports-color
dev: true dev: true
/vue-i18n/9.1.10_vue@3.2.37: /vue-i18n/9.1.9_vue@3.2.37:
resolution: {integrity: sha512-jpr7gV5KPk4n+sSPdpZT8Qx3XzTcNDWffRlHV/cT2NUyEf+sEgTTmLvnBAibjOFJ0zsUyZlVTAWH5DDnYep+1g==} resolution: {integrity: sha512-JeRdNVxS2OGp1E+pye5XB6+M6BBkHwAv9C80Q7+kzoMdUDGRna06tjC0vCB/jDX9aWrl5swxOMFcyAr7or8XTA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
peerDependencies: peerDependencies:
vue: ^3.0.0 vue: ^3.0.0
dependencies: dependencies:
'@intlify/core-base': 9.1.10 '@intlify/core-base': 9.1.9
'@intlify/shared': 9.1.10 '@intlify/shared': 9.1.9
'@intlify/vue-devtools': 9.1.10 '@intlify/vue-devtools': 9.1.9
'@vue/devtools-api': 6.1.4 '@vue/devtools-api': 6.1.4
vue: 3.2.37 vue: 3.2.37
dev: false dev: false

View File

@@ -18,7 +18,7 @@ import { zhCN, dateZhCN, NConfigProvider } from 'naive-ui'
import { GoAppProvider } from '@/components/GoAppProvider' import { GoAppProvider } from '@/components/GoAppProvider'
import { I18n } from '@/components/I18n' import { I18n } from '@/components/I18n'
import { useSystemInit, useDarkThemeHook, useThemeOverridesHook, useCode } from '@/hooks' import { useDarkThemeHook, useThemeOverridesHook, useCode } from '@/hooks'
// 暗黑主题 // 暗黑主题
const darkTheme = useDarkThemeHook() const darkTheme = useDarkThemeHook()
@@ -28,7 +28,4 @@ const overridesTheme = useThemeOverridesHook()
// 代码主题 // 代码主题
const hljsTheme = useCode() const hljsTheme = useCode()
// 系统全局数据初始化
useSystemInit()
</script> </script>

View File

@@ -1,14 +0,0 @@
import { ModuleTypeEnum } from '@/enums/httpEnum'
// 接口白名单(免登录)
export const fetchAllowList = [
// 登录
`${ModuleTypeEnum.SYSTEM}/login`,
// 获取 OSS 接口
`${ModuleTypeEnum.SYSTEM}/getOssInfo`,
// 预览获取数据
`${ModuleTypeEnum.PROJECT}/getData`,
]
// 接口黑名单
export const fetchBlockList = []

View File

@@ -1,37 +1,20 @@
import axios, { AxiosResponse, AxiosRequestConfig } from 'axios' import axios, { AxiosResponse, AxiosRequestConfig } from 'axios'
import { ResultEnum, RequestHttpHeaderEnum } from "@/enums/httpEnum" import { ResultEnum } from "@/enums/httpEnum"
import { PageEnum, ErrorPageNameMap } from "@/enums/pageEnum" import { ErrorPageNameMap } from "@/enums/pageEnum"
import { StorageEnum } from '@/enums/storageEnum' import { redirectErrorPage } from '@/utils'
import { axiosPre } from '@/settings/httpSetting'
import { SystemStoreEnum, SystemStoreUserInfoEnum } from '@/store/modules/systemStore/systemStore.d'
import { redirectErrorPage, getLocalStorage, routerTurnByName, httpErrorHandle } from '@/utils'
import { fetchAllowList } from './axios.config'
import includes from 'lodash/includes'
const axiosInstance = axios.create({ const axiosInstance = axios.create({
baseURL: `${import.meta.env.PROD ? import.meta.env.VITE_PRO_PATH : ''}${axiosPre}`, baseURL: import.meta.env.DEV ? import.meta.env.VITE_DEV_PATH : import.meta.env.VITE_PRO_PATH,
timeout: ResultEnum.TIMEOUT, timeout: ResultEnum.TIMEOUT,
}) })
axiosInstance.interceptors.request.use( axiosInstance.interceptors.request.use(
(config: AxiosRequestConfig) => { (config: AxiosRequestConfig) => {
// 白名单校验 config.headers = {}
if (includes(fetchAllowList, config.url)) return config
// 获取 token
const info = getLocalStorage(StorageEnum.GO_SYSTEM_STORE)
// 重新登录
if (!info) {
routerTurnByName(PageEnum.BASE_LOGIN_NAME)
return config
}
config.headers = {
...config.headers,
[RequestHttpHeaderEnum.TOKEN]: info[SystemStoreEnum.USER_INFO][SystemStoreUserInfoEnum.USER_TOKEN] || ''
}
return config return config
}, },
(err: AxiosRequestConfig) => { (error: AxiosRequestConfig) => {
Promise.reject(err) Promise.reject(error)
} }
) )
@@ -39,31 +22,13 @@ axiosInstance.interceptors.request.use(
axiosInstance.interceptors.response.use( axiosInstance.interceptors.response.use(
(res: AxiosResponse) => { (res: AxiosResponse) => {
const { code } = res.data as { code: number } const { code } = res.data as { code: number }
if (code === ResultEnum.DATA_SUCCESS) return Promise.resolve(res.data)
// 成功 // 重定向
if (code === ResultEnum.SUCCESS) { if (ErrorPageNameMap.get(code)) redirectErrorPage(code)
return Promise.resolve(res.data)
}
// 登录过期
if (code === ResultEnum.TOKEN_OVERDUE) {
window['$message'].error(window['$t']('http.token_overdue_message'))
routerTurnByName(PageEnum.BASE_LOGIN_NAME)
return Promise.resolve(res.data)
}
// 固定错误码重定向
if (ErrorPageNameMap.get(code)) {
redirectErrorPage(code)
return Promise.resolve(res.data)
}
// 提示错误
window['$message'].error(window['$t']((res.data as any).msg))
return Promise.resolve(res.data) return Promise.resolve(res.data)
}, },
(err: AxiosResponse) => { (err: AxiosResponse) => {
httpErrorHandle() window['$message'].error('接口异常,请检查!')
Promise.reject(err) Promise.reject(err)
} }
) )

View File

@@ -1,19 +1,11 @@
import axiosInstance from './axios' import axiosInstance from './axios'
import { import { RequestHttpEnum, ContentTypeEnum } from '@/enums/httpEnum'
RequestHttpEnum,
ContentTypeEnum,
RequestBodyEnum,
RequestDataTypeEnum,
RequestContentTypeEnum,
RequestParamsObjType
} from '@/enums/httpEnum'
import type { RequestGlobalConfigType, RequestConfigType } from '@/store/modules/chartEditStore/chartEditStore.d'
export const get = (url: string, params?: object) => { export const get = (url: string, params?: object) => {
return axiosInstance({ return axiosInstance({
url: url, url: url,
method: RequestHttpEnum.GET, method: RequestHttpEnum.GET,
params: params, params: params
}) })
} }
@@ -28,17 +20,6 @@ export const post = (url: string, data?: object, headersType?: string) => {
}) })
} }
export const patch = (url: string, data?: object, headersType?: string) => {
return axiosInstance({
url: url,
method: RequestHttpEnum.PATCH,
data: data,
headers: {
'Content-Type': headersType || ContentTypeEnum.JSON
}
})
}
export const put = (url: string, data?: object, headersType?: ContentTypeEnum) => { export const put = (url: string, data?: object, headersType?: ContentTypeEnum) => {
return axiosInstance({ return axiosInstance({
url: url, url: url,
@@ -67,9 +48,6 @@ export const http = (type?: RequestHttpEnum) => {
case RequestHttpEnum.POST: case RequestHttpEnum.POST:
return post return post
case RequestHttpEnum.PATCH:
return patch
case RequestHttpEnum.PUT: case RequestHttpEnum.PUT:
return put return put
@@ -80,111 +58,3 @@ export const http = (type?: RequestHttpEnum) => {
return get return get
} }
} }
/**
* * 自定义请求
* @param targetParams 当前组件参数
* @param globalParams 全局参数
*/
export const customizeHttp = (targetParams: RequestConfigType, globalParams: RequestGlobalConfigType) => {
if(!targetParams || !globalParams) {
return
}
// 全局
const {
// 全局请求源地址
requestOriginUrl,
// 全局请求内容
requestParams: globalRequestParams
} = globalParams
// 目标组件(优先级 > 全局组件)
const {
// 请求地址
requestUrl,
// 普通 / sql
requestContentType,
// 获取数据的方式
requestDataType,
// 请求方式 get/post/del/put/patch
requestHttpType,
// 请求体类型 none / form-data / x-www-form-urlencoded / json /xml
requestParamsBodyType,
// SQL 请求对象
requestSQLContent,
// 请求内容 params / cookie / header / body: 同 requestParamsBodyType
requestParams: targetRequestParams
} = targetParams
// 静态排除
if (requestDataType === RequestDataTypeEnum.STATIC) return
if (!requestUrl) {
return
}
// 处理头部
const headers: RequestParamsObjType = {
...globalRequestParams.Header,
...targetRequestParams.Header,
}
// data 参数
let data: RequestParamsObjType | FormData | string = {}
// params 参数
let params: RequestParamsObjType = targetRequestParams.Params
// form 类型处理
let formData: FormData = new FormData()
formData.set('default', 'defaultData')
// 类型处理
switch (requestParamsBodyType) {
case RequestBodyEnum.NONE:
break
case RequestBodyEnum.JSON:
headers['Content-Type'] = ContentTypeEnum.JSON
data = JSON.parse(targetRequestParams.Body['json'])
// json 赋值给 data
break
case RequestBodyEnum.XML:
headers['Content-Type'] = ContentTypeEnum.XML
// xml 字符串赋值给 data
data = targetRequestParams.Body['xml']
break
case RequestBodyEnum.X_WWW_FORM_URLENCODED:
headers['Content-Type'] = ContentTypeEnum.FORM_URLENCODED
const bodyFormData = targetRequestParams.Body['x-www-form-urlencoded']
for (const i in bodyFormData) formData.set(i, bodyFormData[i])
// FormData 赋值给 data
data = formData
break
case RequestBodyEnum.FORM_DATA:
headers['Content-Type'] = ContentTypeEnum.FORM_DATA
const bodyFormUrlencoded = targetRequestParams.Body['form-data']
for (const i in bodyFormUrlencoded) {
formData.set(i, bodyFormUrlencoded[i])
}
// FormData 赋值给 data
data = formData
break
}
// sql 处理
if (requestContentType === RequestContentTypeEnum.SQL) {
headers['Content-Type'] = ContentTypeEnum.JSON
data = requestSQLContent
}
return axiosInstance({
url: `${requestOriginUrl}${requestUrl}`,
method: requestHttpType,
data,
params,
headers
})
}

View File

@@ -1,2 +0,0 @@
export * from '@/api/path/project.api'
export * from '@/api/path/system.api'

View File

@@ -1,84 +0,0 @@
import { http } from '@/api/http'
import { httpErrorHandle } from '@/utils'
import { ContentTypeEnum, RequestHttpEnum, ModuleTypeEnum } from '@/enums/httpEnum'
// * 项目列表
export const projectListApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.GET)(`${ModuleTypeEnum.PROJECT}/list`, data);
return res;
} catch {
httpErrorHandle();
}
}
// * 新增项目
export const createProjectApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.POST)(`${ModuleTypeEnum.PROJECT}/create`, data);
return res;
} catch {
httpErrorHandle();
}
}
// * 获取项目
export const fetchProjectApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.GET)(`${ModuleTypeEnum.PROJECT}/getData`, data);
return res;
} catch {
httpErrorHandle();
}
}
// * 保存项目
export const saveProjectApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.POST)(`${ModuleTypeEnum.PROJECT}/save/data`, data, ContentTypeEnum.FORM_URLENCODED);
return res;
} catch {
httpErrorHandle();
}
}
// * 修改项目基础信息
export const updateProjectApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.POST)(`${ModuleTypeEnum.PROJECT}/edit`, data);
return res;
} catch {
httpErrorHandle();
}
}
// * 删除项目
export const deleteProjectApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.DELETE)(`${ModuleTypeEnum.PROJECT}/delete`, data);
return res;
} catch {
httpErrorHandle();
}
}
// * 修改发布状态 [-1未发布,1发布]
export const changeProjectReleaseApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.PUT)(`${ModuleTypeEnum.PROJECT}/publish`, data);
return res;
} catch {
httpErrorHandle();
}
}
// * 上传文件
export const uploadFile = async (url:string, data: object) => {
try {
const res = await http(RequestHttpEnum.POST)(url, data, ContentTypeEnum.FORM_DATA);
return res;
} catch {
httpErrorHandle();
}
}

View File

@@ -1,33 +0,0 @@
import { http } from '@/api/http'
import { httpErrorHandle } from '@/utils'
import { RequestHttpEnum, ModuleTypeEnum } from '@/enums/httpEnum'
// * 登录
export const loginApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.POST)(`${ModuleTypeEnum.SYSTEM}/login`, data);
return res;
} catch(err) {
httpErrorHandle();
}
}
// * 登出
export const logoutApi = async () => {
try {
const res = await http(RequestHttpEnum.GET)(`${ModuleTypeEnum.SYSTEM}/logout`);
return res;
} catch(err) {
httpErrorHandle();
}
}
// * 获取 oss 上传接口
export const ossUrlApi = async (data: object) => {
try {
const res = await http(RequestHttpEnum.GET)(`${ModuleTypeEnum.SYSTEM}/getOssInfo`, data);
return res;
} catch(err) {
httpErrorHandle();
}
}

View File

@@ -8,9 +8,9 @@
</n-text> </n-text>
<div <div
class="item-right" class="item-right"
justify="space-between"
:style="{ :style="{
gridTemplateColumns: alone ? '1fr' : '1fr 1fr', gridTemplateColumns: alone ? '1fr' : '1fr 1fr'
...itemRightStyle
}" }"
> >
<slot></slot> <slot></slot>
@@ -28,11 +28,6 @@ defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
required: false required: false
},
itemRightStyle: {
type: Object,
default: () => {},
required: false
} }
}) })
</script> </script>
@@ -53,6 +48,7 @@ $leftWidth: 60px;
.item-right { .item-right {
display: grid; display: grid;
grid-column-gap: 10px; grid-column-gap: 10px;
grid-template-columns: 1fr 1fr;
width: calc(100% - #{$leftWidth}); width: calc(100% - #{$leftWidth});
} }
} }

View File

@@ -5,7 +5,6 @@ import * as monaco from 'monaco-editor'
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker' import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker' import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker' import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
self.MonacoEnvironment = { self.MonacoEnvironment = {
getWorker(workerId, label) { getWorker(workerId, label) {
@@ -15,9 +14,6 @@ self.MonacoEnvironment = {
if (label === 'typescript' || label === 'javascript') { if (label === 'typescript' || label === 'javascript') {
return new tsWorker() return new tsWorker()
} }
if (label === 'html') {
return new htmlWorker()
}
return new editorWorker() return new editorWorker()
} }
} }

View File

@@ -1,3 +1,9 @@
// 鼠标点击左右键
export enum MouseEventButton {
LEFT = 1,
RIGHT = 2
}
// 页面拖拽键名 // 页面拖拽键名
export enum DragKeyEnum { export enum DragKeyEnum {
DROG_KEY = 'ChartData' DROG_KEY = 'ChartData'
@@ -19,8 +25,7 @@ export enum MenuEnum {
DOWN = 'down', DOWN = 'down',
CLEAR = 'clear', CLEAR = 'clear',
BACK = 'back', BACK = 'back',
FORWORD = 'forward', FORWORD = 'forward'
SAVE = 'save'
} }
// Win 键盘枚举 // Win 键盘枚举
@@ -28,6 +33,9 @@ export enum WinKeyboard {
CTRL = 'ctrl', CTRL = 'ctrl',
SHIFT = 'shift', SHIFT = 'shift',
ALT = ' alt', ALT = ' alt',
CTRL_SOURCE_KEY = "control",
SHIFT_SOURCE_KEY = "shift",
ALT_SOURCE_KEY = "alt"
} }
// Mac 键盘枚举 // Mac 键盘枚举
@@ -36,16 +44,7 @@ export enum MacKeyboard {
CTRL = '⌘', CTRL = '⌘',
SHIFT = '⇧', SHIFT = '⇧',
ALT = '⌥', ALT = '⌥',
CTRL_SOURCE_KEY = "⌘",
SHIFT_SOURCE_KEY = "⇧",
ALT_SOURCE_KEY = "⌥"
} }
// 同步状态枚举
export enum SyncEnum {
// 等待
PENDING,
// 开始
START,
// 成功
SUCCESS,
// 失败
FAILURE
}

View File

@@ -1,17 +1,12 @@
// 模块 Path 前缀分类 /**
export enum ModuleTypeEnum { * @description: 请求结果集
SYSTEM = 'sys', */
PROJECT = 'project',
}
// 请求结果集
export enum ResultEnum { export enum ResultEnum {
DATA_SUCCESS = 0, DATA_SUCCESS = 0,
SUCCESS = 200, SUCCESS = 200,
SERVER_ERROR = 500, SERVER_ERROR = 500,
SERVER_FORBIDDEN = 403, SERVER_FORBIDDEN = 403,
NOT_FOUND = 404, NOT_FOUND = 404,
TOKEN_OVERDUE = 886,
TIMEOUT = 10042, TIMEOUT = 10042,
} }
@@ -20,114 +15,30 @@ export enum RequestDataTypeEnum {
// 静态数据 // 静态数据
STATIC = 0, STATIC = 0,
// 请求数据 // 请求数据
AJAX = 1 AJAX = 1,
} }
// 请求主体类型 /**
export enum RequestContentTypeEnum { * @description: 请求方法
// 普通请求 */
DEFAULT = 0,
// SQL请求
SQL = 1
}
// 头部
export enum RequestHttpHeaderEnum {
TOKEN = 'Token',
COOKIE = 'Cookie'
}
// 请求方法
export enum RequestHttpEnum { export enum RequestHttpEnum {
GET = 'get', GET = 'get',
POST = 'post', POST = 'post',
PATCH = 'patch', PATCH = 'patch',
PUT = 'put', PUT = 'put',
DELETE = 'delete' DELETE = 'delete',
} }
/** /**
* @description: 请求间隔 * @description: 常用的contentTyp类型
*/ */
export enum RequestHttpIntervalEnum {
// 秒
SECOND = 'second',
// 分
MINUTE = 'minute',
// 时
HOUR = 'hour',
// 天
DAY = 'day'
}
/**
* @description: 请求间隔名称
*/
export const SelectHttpTimeNameObj = {
[RequestHttpIntervalEnum.SECOND]: '秒',
[RequestHttpIntervalEnum.MINUTE]: '分',
[RequestHttpIntervalEnum.HOUR]: '时',
[RequestHttpIntervalEnum.DAY]: '天'
}
/**
* @description: 请求头部类型
*/
export enum RequestBodyEnum {
NONE = 'none',
FORM_DATA = 'form-data',
X_WWW_FORM_URLENCODED = 'x-www-form-urlencoded',
JSON = 'json',
XML = 'xml'
}
/**
* @description: 请求头部类型数组
*/
export const RequestBodyEnumList = [
RequestBodyEnum.NONE,
RequestBodyEnum.FORM_DATA,
RequestBodyEnum.X_WWW_FORM_URLENCODED,
RequestBodyEnum.JSON,
RequestBodyEnum.XML
]
/**
* @description: 请求参数类型
*/
export enum RequestParamsTypeEnum {
PARAMS = 'Params',
BODY = 'Body',
HEADER = 'Header',
}
/**
* @description: 请求参数主体
*/
export type RequestParamsObjType = {
[T: string]: string
}
export type RequestParams = {
[RequestParamsTypeEnum.PARAMS]: RequestParamsObjType
[RequestParamsTypeEnum.HEADER]: RequestParamsObjType
[RequestParamsTypeEnum.BODY]: {
[RequestBodyEnum.FORM_DATA]: RequestParamsObjType
[RequestBodyEnum.X_WWW_FORM_URLENCODED]: RequestParamsObjType
[RequestBodyEnum.JSON]: string
[RequestBodyEnum.XML]: string
}
}
// 常用的contentTyp类型
export enum ContentTypeEnum { export enum ContentTypeEnum {
// json // json
JSON = 'application/json;charset=UTF-8', JSON = 'application/json;charset=UTF-8',
// text // json
TEXT = 'text/plain;charset=UTF-8', TEXT = 'text/plain;charset=UTF-8',
// xml // form-data 一般配合qs
XML = 'application/xml;charset=UTF-8',
// application/x-www-form-urlencoded 一般配合qs
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8', FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
// form-data 上传 // form-data 上传
FORM_DATA = 'multipart/form-data;charset=UTF-8' FORM_DATA = 'multipart/form-data;charset=UTF-8',
} }

View File

@@ -20,15 +20,10 @@ export enum PageEnum {
//重定向 //重定向
REDIRECT = '/redirect', REDIRECT = '/redirect',
REDIRECT_NAME = 'Redirect', REDIRECT_NAME = 'Redirect',
// 未发布
REDIRECT_UN_PUBLISH = '/redirect/unPublish',
REDIRECT_UN_PUBLISH_NAME = 'redirect-un-publish',
// 重载
RELOAD = '/reload', RELOAD = '/reload',
RELOAD_NAME = 'Reload', RELOAD_NAME = 'Reload',
// 首页 // 首页
BASE_HOME = '/project', BASE_HOME = '/project',
BASE_HOME_NAME = 'Project', BASE_HOME_NAME = 'Project',

View File

@@ -1,8 +1,10 @@
export enum StorageEnum { export enum StorageEnum {
// 全局设置 // 全局设置
GO_SETTING_STORE = 'GO_SETTING', GO_SYSTEM_SETTING_STORE = 'GO_SYSTEM_SETTING',
// token 等信息
GO_ACCESS_TOKEN_STORE = 'GO_ACCESS_TOKEN',
// 登录信息 // 登录信息
GO_SYSTEM_STORE = 'GO_SYSTEM', GO_LOGIN_INFO_STORE = 'GO_LOGIN_INFO',
// 语言 // 语言
GO_LANG_STORE = 'GO_LANG', GO_LANG_STORE = 'GO_LANG',
// 当前选择的主题 // 当前选择的主题

View File

@@ -1,5 +1,4 @@
export * from '@/hooks/useTheme.hook' export * from '@/hooks/useTheme.hook'
export * from '@/hooks/usePreviewScale.hook' export * from '@/hooks/usePreviewScale.hook'
export * from '@/hooks/useCode.hook' export * from '@/hooks/useCode.hook'
export * from '@/hooks/useChartDataFetch.hook' export * from '@/hooks/useChartDataFetch.hook'
export * from '@/hooks/useSystemInit.hook'

View File

@@ -1,14 +1,15 @@
import { ref, toRefs, toRaw } from 'vue' import { ref, toRefs } from 'vue'
import type VChart from 'vue-echarts' import type VChart from 'vue-echarts'
import { customizeHttp } from '@/api/http' import { http } from '@/api/http'
import { CreateComponentType, ChartFrameEnum } from '@/packages/index.d' import { CreateComponentType, ChartFrameEnum } from '@/packages/index.d'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { RequestDataTypeEnum } from '@/enums/httpEnum' import { RequestDataTypeEnum } from '@/enums/httpEnum'
import { isPreview, newFunctionHandle, intervalUnitHandle } from '@/utils' import { isPreview, newFunctionHandle } from '@/utils'
// 获取类型 // 获取类型
type ChartEditStoreType = typeof useChartEditStore type ChartEditStoreType = typeof useChartEditStore
/** /**
* setdata 数据监听与更改 * setdata 数据监听与更改
* @param targetComponent * @param targetComponent
@@ -25,73 +26,51 @@ export const useChartDataFetch = (
const requestIntervalFn = () => { const requestIntervalFn = () => {
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const { requestOriginUrl, requestInterval } = toRefs(chartEditStore.getRequestGlobalConfig)
// 全局数据
const {
requestOriginUrl,
requestIntervalUnit: globalUnit,
requestInterval: globalRequestInterval
} = toRefs(chartEditStore.getRequestGlobalConfig)
// 目标组件
const {
requestDataType,
requestUrl,
requestIntervalUnit: targetUnit,
requestInterval: targetInterval
} = toRefs(targetComponent.request)
// 组件类型 // 组件类型
const { chartFrame } = targetComponent.chartConfig const { chartFrame } = targetComponent.chartConfig
// 请求配置
const { requestDataType, requestHttpType, requestUrl } = toRefs(targetComponent.data)
// 非请求类型 // 非请求类型
if (requestDataType.value !== RequestDataTypeEnum.AJAX) return if (requestDataType.value !== RequestDataTypeEnum.AJAX) return
// 处理地址
if (requestUrl?.value && requestInterval.value > 0) {
// requestOriginUrl 允许为空
const completePath = requestOriginUrl && requestOriginUrl.value + requestUrl.value
if (!completePath) return
try { clearInterval(fetchInterval)
// 处理地址
// @ts-ignore
if (requestUrl?.value) {
// requestOriginUrl 允许为空
const completePath = requestOriginUrl && requestOriginUrl.value + requestUrl.value
if (!completePath) return
clearInterval(fetchInterval) const fetchFn = async () => {
const res: any = await http(requestHttpType.value)(completePath || '', {})
const fetchFn = async () => { if (res.data) {
const res = await customizeHttp(toRaw(targetComponent.request), toRaw(chartEditStore.requestGlobalConfig)) try {
if (res && res.data) { const filter = targetComponent.filter
try { // 更新回调函数
const filter = targetComponent.filter if (updateCallback) {
// 更新回调函数 updateCallback(newFunctionHandle(res.data, filter))
if (updateCallback) { } else {
updateCallback(newFunctionHandle(res.data, filter)) // eCharts 组件配合 vChart 库更新方式
} else { if (chartFrame === ChartFrameEnum.ECHARTS) {
// eCharts 组件配合 vChart 库更新方式 if (vChartRef.value) {
if (chartFrame === ChartFrameEnum.ECHARTS) { vChartRef.value.setOption({ dataset: newFunctionHandle(res.data, filter) })
if (vChartRef.value) {
vChartRef.value.setOption({ dataset: newFunctionHandle(res.data, filter) })
}
} }
} }
} catch (error) {
console.error(error)
} }
} catch (error) {
console.error(error)
} }
} }
// 立即调用
fetchFn()
// 定时时间
const time = targetInterval && targetInterval.value ? targetInterval.value : globalRequestInterval.value
// 单位
const unit = targetInterval && targetInterval.value ? targetUnit.value : globalUnit.value
// 开启轮询
if (time) fetchInterval = setInterval(fetchFn, intervalUnitHandle(time, unit))
} }
} catch (error) {}
// 立即调用
fetchFn()
// 开启定时
fetchInterval = setInterval(fetchFn, requestInterval.value * 1000)
}
} }
isPreview() && requestIntervalFn() isPreview() && requestIntervalFn()
return { vChartRef } return { vChartRef }
} }

View File

@@ -1,23 +0,0 @@
import { useSystemStore } from '@/store/modules/systemStore/systemStore'
import { SystemStoreEnum } from '@/store/modules/systemStore/systemStore.d'
import { ResultEnum } from '@/enums/httpEnum'
import { ossUrlApi } from '@/api/path/'
// * 初始化
export const useSystemInit = async () => {
const systemStore = useSystemStore()
// 获取 OSS 信息
const getOssUrl = async () => {
const res = await ossUrlApi({}) as unknown as MyResponseType
if (res.code === ResultEnum.SUCCESS) {
systemStore.setItem(SystemStoreEnum.FETCH_INFO, {
OSSUrl: res.data?.bucketURL
})
}
}
// 执行
getOssUrl()
}

View File

@@ -11,8 +11,6 @@ const global = {
help: 'Help', help: 'Help',
contact: 'Contact Us', contact: 'Contact Us',
logout: 'Logout', logout: 'Logout',
logout_success: 'Logout success',
logout_failure: 'Logout Failed',
// system setting // system setting
sys_set: 'System Setting', sys_set: 'System Setting',
lang_set: 'Language Setting', lang_set: 'Language Setting',
@@ -28,14 +26,8 @@ const global = {
r_more: 'More', r_more: 'More',
} }
const http = {
error_message: 'The interface is abnormal, please check the interface!',
token_overdue_message: 'Login expired, please log in again!'
}
export default { export default {
global, global,
http,
login, login,
project project
} }

View File

@@ -2,6 +2,6 @@ export default {
desc: "Login", desc: "Login",
form_auto: "Sign in automatically", form_auto: "Sign in automatically",
form_button: "Login", form_button: "Login",
login_success: "Login success!", login_success: "Login success",
login_message: "Please complete the letter!", login_message: "Please complete the letter",
} }

View File

@@ -1,8 +1,6 @@
export default { export default {
create_btn: 'Creat', create_btn: 'Creat',
create_success: 'Creat Success!', create_tip: 'Please select a content for development',
create_failure: 'Failed to create, please try again later',
create_tip: 'Please select a content for development!',
project: 'Project', project: 'Project',
my: 'My', my: 'My',
new_project: 'New Project', new_project: 'New Project',

View File

@@ -11,8 +11,6 @@ const global = {
help: '帮助中心', help: '帮助中心',
contact: '联系我们', contact: '联系我们',
logout: '退出登录', logout: '退出登录',
logout_success: '退出成功!',
logout_failure: '退出失败!',
// 系统设置 // 系统设置
sys_set: '系统设置', sys_set: '系统设置',
lang_set: '语言设置', lang_set: '语言设置',
@@ -20,27 +18,16 @@ const global = {
r_edit: '编辑', r_edit: '编辑',
r_preview: '预览', r_preview: '预览',
r_copy: '克隆', r_copy: '克隆',
r_copy_success: '克隆成功!',
r_rename: '重命名', r_rename: '重命名',
r_rename_success: '重命名成功!',
r_publish: '发布', r_publish: '发布',
r_publish_success: '成功发布!',
r_unpublish: '取消发布', r_unpublish: '取消发布',
r_unpublish_success: '取消成功!',
r_download: '下载', r_download: '下载',
r_delete: '删除', r_delete: '删除',
r_delete_success: '删除成功!',
r_more: '更多', r_more: '更多',
} }
const http = {
error_message: '获取数据失败,请稍后重试!',
token_overdue_message: '登录过期,请重新登录!'
}
export default { export default {
global, global,
http,
login, login,
project project
} }

View File

@@ -2,6 +2,6 @@ export default {
desc: "登录", desc: "登录",
form_auto: "自动登录", form_auto: "自动登录",
form_button: "登录", form_button: "登录",
login_success: "登录成功",
login_message: "请填写完整信息", login_message: "请填写完整信息",
login_success: "登录成功!",
} }

View File

@@ -1,8 +1,6 @@
export default { export default {
// aside // aside
create_btn: '新建', create_btn: '新建',
create_success: '新建成功!',
create_failure: '新建失败,请稍后重试!',
create_tip: '从哪里出发好呢?', create_tip: '从哪里出发好呢?',
project: '项目', project: '项目',
my: '我的', my: '我的',

View File

@@ -7,8 +7,6 @@ import { setupNaive, setupDirectives, setupCustomComponents } from '@/plugins'
import { GoAppProvider } from '@/components/GoAppProvider/index' import { GoAppProvider } from '@/components/GoAppProvider/index'
import { setHtmlTheme } from '@/utils' import { setHtmlTheme } from '@/utils'
// 引入全局样式
import '@/styles/pages/index.scss'
// 引入动画 // 引入动画
import 'animate.css/animate.min.css' import 'animate.css/animate.min.css'
// 引入标尺 // 引入标尺

View File

@@ -44,7 +44,7 @@
<n-color-picker <n-color-picker
size="small" size="small"
:modes="['hex']" :modes="['hex']"
v-model:value="optionData.prefixColor" v-model:value="optionData.suffixColor"
></n-color-picker> ></n-color-picker>
</SettingItem> </SettingItem>
<SettingItem name="后缀"> <SettingItem name="后缀">

View File

@@ -27,7 +27,9 @@ export type ConfigType = {
// 数据请求 // 数据请求
interface requestConfig { interface requestConfig {
request: RequestConfigType, data: RequestConfigType,
// 暂时约定为数据存储区域(未使用)
requestData: any
} }
// Echarts 数据类型 // Echarts 数据类型

View File

@@ -1,36 +1,13 @@
import { getUUID } from '@/utils' import { getUUID } from '@/utils'
import { PublicConfigType } from '@/packages/index.d' import { PublicConfigType } from '@/packages/index.d'
import { RequestConfigType } from '@/store/modules/chartEditStore/chartEditStore.d' import { RequestConfigType } from '@/store/modules/chartEditStore/chartEditStore.d'
import { import { RequestHttpEnum, RequestDataTypeEnum } from '@/enums/httpEnum'
RequestHttpEnum,
RequestDataTypeEnum,
RequestHttpIntervalEnum,
RequestContentTypeEnum,
RequestBodyEnum
} from '@/enums/httpEnum'
import { chartInitConfig } from '@/settings/designSetting' import { chartInitConfig } from '@/settings/designSetting'
const requestConfig: RequestConfigType = { const requestConfig: RequestConfigType = {
requestDataType: RequestDataTypeEnum.STATIC, requestDataType: RequestDataTypeEnum.STATIC,
requestHttpType: RequestHttpEnum.GET, requestHttpType: RequestHttpEnum.GET,
requestUrl: '', requestUrl: ''
requestInterval: undefined,
requestIntervalUnit: RequestHttpIntervalEnum.SECOND,
requestContentType: RequestContentTypeEnum.DEFAULT,
requestParamsBodyType: RequestBodyEnum.NONE,
requestSQLContent: {
sql: 'select * from where'
},
requestParams: {
Body: {
'form-data': {},
'x-www-form-urlencoded': {},
json: '',
xml: ''
},
Header: {},
Params: {}
}
} }
export class publicConfig implements PublicConfigType { export class publicConfig implements PublicConfigType {
@@ -60,12 +37,14 @@ export class publicConfig implements PublicConfigType {
// 倾斜 // 倾斜
skewX: 0, skewX: 0,
skewY: 0, skewY: 0,
// 动画 // 动画
animations: [] animations: []
} }
// 请求 // 数据
public request = { ...requestConfig } public data = { ...requestConfig }
// 数据获取
public requestData = []
// 数据过滤 // 数据过滤
public filter = undefined public filter = undefined

View File

@@ -8,10 +8,7 @@ import { SketchRule } from 'vue3-sketch-ruler'
* @param app * @param app
*/ */
export function setupCustomComponents(app: App) { export function setupCustomComponents(app: App) {
// 骨架屏
app.component('GoSkeleton', GoSkeleton) app.component('GoSkeleton', GoSkeleton)
// 加载
app.component('GoLoading', GoLoading) app.component('GoLoading', GoLoading)
// 标尺
app.component('SketchRule', SketchRule) app.component('SketchRule', SketchRule)
} }

View File

@@ -53,10 +53,6 @@ import {
ArrowForward as ArrowForwardIcon, ArrowForward as ArrowForwardIcon,
Planet as PawIcon, Planet as PawIcon,
Search as SearchIcon, Search as SearchIcon,
Reload as ReloadIcon,
ChevronUpOutline as ChevronUpOutlineIcon,
ChevronDownOutline as ChevronDownOutlineIcon,
Pulse as PulseIcon
} from '@vicons/ionicons5' } from '@vicons/ionicons5'
import { import {
@@ -85,7 +81,8 @@ import {
FitToScreen as FitToScreenIcon, FitToScreen as FitToScreenIcon,
FitToHeight as FitToHeightIcon, FitToHeight as FitToHeightIcon,
FitToWidth as FitToWidthIcon, FitToWidth as FitToWidthIcon,
Save as SaveIcon, Carbon3DCursor as Carbon3DCursorIcon,
Carbon3DSoftware as Carbon3DSoftwareIcon,
Filter as FilterIcon, Filter as FilterIcon,
FilterEdit as FilterEditIcon FilterEdit as FilterEditIcon
} from '@vicons/carbon' } from '@vicons/carbon'
@@ -198,16 +195,8 @@ const ionicons5 = {
PawIcon, PawIcon,
// 搜索(放大镜) // 搜索(放大镜)
SearchIcon, SearchIcon,
// 加载
ReloadIcon,
// 过滤器 // 过滤器
FilterIcon, FilterIcon
// 向上
ChevronUpOutlineIcon,
// 向下
ChevronDownOutlineIcon,
// 脉搏
PulseIcon
} }
const carbon = { const carbon = {
@@ -252,8 +241,10 @@ const carbon = {
FitToScreenIcon, FitToScreenIcon,
FitToHeightIcon, FitToHeightIcon,
FitToWidthIcon, FitToWidthIcon,
// 保存 // 成组
SaveIcon, Carbon3DCursorIcon,
// 解组
Carbon3DSoftwareIcon,
// 过滤器 // 过滤器
FilterIcon, FilterIcon,
FilterEditIcon FilterEditIcon

View File

@@ -37,7 +37,6 @@ import {
NTooltip, NTooltip,
NAvatar, NAvatar,
NTabs, NTabs,
NTab,
NTabPane, NTabPane,
NCard, NCard,
NRow, NRow,
@@ -69,7 +68,6 @@ import {
NSteps, NSteps,
NStep, NStep,
NInputGroup, NInputGroup,
NInputGroupLabel,
NResult, NResult,
NDescriptions, NDescriptions,
NDescriptionsItem, NDescriptionsItem,
@@ -138,7 +136,6 @@ const naive = create({
NTooltip, NTooltip,
NAvatar, NAvatar,
NTabs, NTabs,
NTab,
NTabPane, NTabPane,
NCard, NCard,
NRow, NRow,
@@ -170,7 +167,6 @@ const naive = create({
NSteps, NSteps,
NStep, NStep,
NInputGroup, NInputGroup,
NInputGroupLabel,
NResult, NResult,
NDescriptions, NDescriptions,
NDescriptionsItem, NDescriptionsItem,

View File

@@ -1,13 +1,13 @@
import { RouteRecordRaw } from 'vue-router' import { RouteRecordRaw } from 'vue-router'
import type { AppRouteRecordRaw } from '@/router/types'; import type { AppRouteRecordRaw } from '@/router/types';
import { ErrorPage404, ErrorPage403, ErrorPage500, Layout, RedirectHome, RedirectUnPublish } from '@/router/constant'; import { ErrorPage404, ErrorPage403, ErrorPage500, Layout } from '@/router/constant';
import { PageEnum } from '@/enums/pageEnum' import { PageEnum } from '@/enums/pageEnum'
import { GoReload } from '@/components/GoReload' import { GoReload } from '@/components/GoReload'
export const LoginRoute: RouteRecordRaw = { export const LoginRoute: RouteRecordRaw = {
path: PageEnum.BASE_LOGIN, path: '/login',
name: PageEnum.BASE_LOGIN_NAME, name: 'Login',
component: () => import('@/views/login/index.vue'), component: () => import('@/views/login/index.vue'),
meta: { meta: {
title: '登录', title: '登录',
@@ -60,21 +60,22 @@ export const ReloadRoute: AppRouteRecordRaw = {
}, },
} }
export const RedirectRoute: RouteRecordRaw[] = [ export const RedirectRoute: AppRouteRecordRaw = {
{ path: PageEnum.REDIRECT,
path: PageEnum.REDIRECT, name: PageEnum.REDIRECT_NAME,
name: PageEnum.REDIRECT_NAME, component: Layout,
component: RedirectHome, meta: {
meta: { title: PageEnum.REDIRECT_NAME,
title: PageEnum.REDIRECT_NAME,
},
}, },
{ children: [
path: PageEnum.REDIRECT_UN_PUBLISH, {
name: PageEnum.REDIRECT_UN_PUBLISH_NAME, path: '/redirect/:path(.*)',
component: RedirectUnPublish, name: PageEnum.REDIRECT_NAME,
meta: { component: () => import('@/views/redirect/index.vue'),
title: PageEnum.REDIRECT_UN_PUBLISH_NAME, meta: {
title: PageEnum.REDIRECT_NAME,
hideBreadcrumb: true,
},
}, },
}, ],
] };

View File

@@ -4,10 +4,6 @@ export const ErrorPage403 = () => import('@/views/exception/403.vue');
export const ErrorPage500 = () => import('@/views/exception/500.vue'); export const ErrorPage500 = () => import('@/views/exception/500.vue');
export const RedirectHome = () => import('@/views/redirect/index.vue');
export const RedirectUnPublish = () => import('@/views/redirect/UnPublish.vue');
export const Layout = () => import('@/layout/index.vue'); export const Layout = () => import('@/layout/index.vue');
export const ParentLayout = () => import('@/layout/parentLayout.vue'); export const ParentLayout = () => import('@/layout/parentLayout.vue');

View File

@@ -1,8 +1,9 @@
import type { App } from 'vue' import type { App } from 'vue'
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router' import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import { RedirectRoute } from '@/router/base'
import { createRouterGuards } from './router-guards' import { createRouterGuards } from './router-guards'
import { PageEnum } from '@/enums/pageEnum' import { PageEnum } from '@/enums/pageEnum'
import { HttpErrorPage, LoginRoute, ReloadRoute, RedirectRoute } from '@/router/base' import { HttpErrorPage, LoginRoute, ReloadRoute } from '@/router/base'
import { Layout } from '@/router/constant' import { Layout } from '@/router/constant'
import modules from '@/router/modules' import modules from '@/router/modules'
@@ -18,7 +19,6 @@ const RootRoute: Array<RouteRecordRaw> = [
}, },
children: [ children: [
...HttpErrorPage, ...HttpErrorPage,
...RedirectRoute,
modules.projectRoutes, modules.projectRoutes,
modules.chartRoutes, modules.chartRoutes,
modules.previewRoutes modules.previewRoutes
@@ -27,7 +27,7 @@ const RootRoute: Array<RouteRecordRaw> = [
] ]
export const constantRouter: any[] = [LoginRoute, ...RootRoute, ReloadRoute]; export const constantRouter: any[] = [LoginRoute, ...RootRoute, RedirectRoute, ReloadRoute];
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(''), history: createWebHashHistory(''),

View File

@@ -1,15 +1,7 @@
import { Router } from 'vue-router'; import { Router } from 'vue-router';
import { PageEnum, PreviewEnum } from '@/enums/pageEnum' import { PageEnum } from '@/enums/pageEnum'
import { loginCheck } from '@/utils' import { loginCheck } from '@/utils'
// 路由白名单
const routerAllowList = [
// 登录
PageEnum.BASE_LOGIN_NAME,
// 预览
PreviewEnum.CHART_PREVIEW_NAME
]
export function createRouterGuards(router: Router) { export function createRouterGuards(router: Router) {
// 前置 // 前置
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
@@ -20,8 +12,10 @@ export function createRouterGuards(router: Router) {
next({ name: PageEnum.ERROR_PAGE_NAME_404 }) next({ name: PageEnum.ERROR_PAGE_NAME_404 })
} }
// @ts-ignore if (!loginCheck()) {
if (!routerAllowList.includes(to.name) && !loginCheck()) { if (to.name === PageEnum.BASE_LOGIN_NAME) {
next()
}
next({ name: PageEnum.BASE_LOGIN_NAME }) next({ name: PageEnum.BASE_LOGIN_NAME })
} }
next() next()

View File

@@ -1,5 +1,4 @@
import { LangEnum, PreviewScaleEnum } from '@/enums/styleEnum' import { LangEnum, PreviewScaleEnum } from '@/enums/styleEnum'
import { RequestHttpIntervalEnum } from '@/enums/httpEnum'
import designColor from './designColor.json' import designColor from './designColor.json'
// 默认语言 // 默认语言
@@ -49,14 +48,8 @@ export const backgroundImageSize = 5
// 预览展示方式 // 预览展示方式
export const previewScaleType = PreviewScaleEnum.FIT export const previewScaleType = PreviewScaleEnum.FIT
// 数据请求间隔s // 数据请求间隔
export const requestInterval = 30 export const requestInterval = 30
// 工作台自动保存间隔s
export const saveInterval = 30
// 数据请求间隔单位
export const requestIntervalUnit = RequestHttpIntervalEnum.SECOND
// 工作区域历史记录存储最大数量 // 工作区域历史记录存储最大数量
export const editHistoryMax = 100 export const editHistoryMax = 100

View File

@@ -1,2 +0,0 @@
// 请求前缀
export const axiosPre = '/api/goview'

View File

@@ -1,37 +1,11 @@
import { CreateComponentType, FilterEnum } from '@/packages/index.d' import { CreateComponentType, FilterEnum} from '@/packages/index.d'
import { HistoryActionTypeEnum } from '@/store/modules/chartHistoryStore/chartHistoryStore.d' import { HistoryActionTypeEnum } from '@/store/modules/chartHistoryStore/chartHistoryStore.d'
import { SyncEnum } from '@/enums/editPageEnum' import { RequestHttpEnum, RequestDataTypeEnum } from '@/enums/httpEnum'
import {
RequestHttpEnum,
RequestContentTypeEnum,
RequestDataTypeEnum,
RequestHttpIntervalEnum,
RequestParams,
RequestBodyEnum,
RequestParamsObjType
} from '@/enums/httpEnum'
import { PreviewScaleEnum } from '@/enums/styleEnum' import { PreviewScaleEnum } from '@/enums/styleEnum'
import type { ChartColorsNameType, GlobalThemeJsonType } from '@/settings/chartThemes/index' import type {
ChartColorsNameType,
// 项目数据枚举 GlobalThemeJsonType,
export enum ProjectInfoEnum { } from '@/settings/chartThemes/index'
// 名称
PROJECT_NAME = 'projectName',
// 描述
REMARKS = 'remarks',
// 缩略图
THUMBNAIL= 'thumbnail',
// 是否公开发布
RELEASE = 'release'
}
// 项目数据
export type ProjectInfoType = {
[ProjectInfoEnum.PROJECT_NAME]: string,
[ProjectInfoEnum.REMARKS]: string,
[ProjectInfoEnum.THUMBNAIL]: string,
[ProjectInfoEnum.RELEASE]: boolean
}
// 编辑画布属性 // 编辑画布属性
export enum EditCanvasTypeEnum { export enum EditCanvasTypeEnum {
@@ -43,10 +17,9 @@ export enum EditCanvasTypeEnum {
LOCK_SCALE = 'lockScale', LOCK_SCALE = 'lockScale',
IS_CREATE = 'isCreate', IS_CREATE = 'isCreate',
IS_DRAG = 'isDrag', IS_DRAG = 'isDrag',
SAVE_STATUS = 'saveStatus'
} }
// 编辑区域(临时) // 编辑区域
export type EditCanvasType = { export type EditCanvasType = {
// 编辑区域 DOM // 编辑区域 DOM
[EditCanvasTypeEnum.EDIT_LAYOUT_DOM]: HTMLElement | null [EditCanvasTypeEnum.EDIT_LAYOUT_DOM]: HTMLElement | null
@@ -63,11 +36,9 @@ export type EditCanvasType = {
[EditCanvasTypeEnum.IS_CREATE]: boolean [EditCanvasTypeEnum.IS_CREATE]: boolean
// 拖拽中 // 拖拽中
[EditCanvasTypeEnum.IS_DRAG]: boolean [EditCanvasTypeEnum.IS_DRAG]: boolean
// 保存状态
[EditCanvasTypeEnum.SAVE_STATUS]: SyncEnum
} }
// 画布数据/滤镜/背景色/宽高主题等 // 滤镜/背景色/宽高主题等
export enum EditCanvasConfigEnum { export enum EditCanvasConfigEnum {
WIDTH = 'width', WIDTH = 'width',
HEIGHT = 'height', HEIGHT = 'height',
@@ -76,15 +47,10 @@ export enum EditCanvasConfigEnum {
BACKGROUND = 'background', BACKGROUND = 'background',
BACKGROUND_IMAGE = 'backgroundImage', BACKGROUND_IMAGE = 'backgroundImage',
SELECT_COLOR = 'selectColor', SELECT_COLOR = 'selectColor',
PREVIEW_SCALE_TYPE = 'previewScaleType' PREVIEW_SCALE_TYPE = 'previewScaleType',
} }
// 画布属性(需保存) export interface EditCanvasConfigType {
export type EditCanvasConfigType = {
// 项目名称
[EditCanvasConfigEnum.PROJECT_NAME]: string,
// 项目描述
[EditCanvasConfigEnum.REMARKS]: string,
// 滤镜-色相 // 滤镜-色相
[FilterEnum.HUE_ROTATE]: number [FilterEnum.HUE_ROTATE]: number
// 滤镜-饱和度 // 滤镜-饱和度
@@ -123,7 +89,7 @@ export enum EditCanvasTypeEnum {
START_X = 'startX', START_X = 'startX',
START_Y = 'startY', START_Y = 'startY',
X = 'x', X = 'x',
Y = 'y' Y = 'y',
} }
// 鼠标位置 // 鼠标位置
@@ -152,7 +118,6 @@ export type RecordChartType = {
// Store 枚举 // Store 枚举
export enum ChartEditStoreEnum { export enum ChartEditStoreEnum {
PROJECT_INFO = 'projectInfo',
EDIT_RANGE = 'editRange', EDIT_RANGE = 'editRange',
EDIT_CANVAS = 'editCanvas', EDIT_CANVAS = 'editCanvas',
RIGHT_MENU_SHOW = 'rightMenuShow', RIGHT_MENU_SHOW = 'rightMenuShow',
@@ -162,48 +127,28 @@ export enum ChartEditStoreEnum {
// 以下需要存储 // 以下需要存储
EDIT_CANVAS_CONFIG = 'editCanvasConfig', EDIT_CANVAS_CONFIG = 'editCanvasConfig',
REQUEST_GLOBAL_CONFIG = 'requestGlobalConfig', REQUEST_GLOBAL_CONFIG = 'requestGlobalConfig',
COMPONENT_LIST = 'componentList' COMPONENT_LIST = 'componentList',
}
// 请求公共类型
type RequestPublicConfigType = {
// 时间单位(时分秒)
requestIntervalUnit: RequestHttpIntervalEnum
// 请求内容
requestParams: RequestParams
} }
// 全局的图表请求配置 // 全局的图表请求配置
export interface RequestGlobalConfigType extends RequestPublicConfigType { export type RequestGlobalConfigType = {
// 组件定制轮询时间
requestInterval: number
// 请求源地址 // 请求源地址
requestOriginUrl?: string requestOriginUrl?: string
// 轮询时间
requestInterval: number
} }
// 单个图表请求配置 // 单个图表请求配置
export interface RequestConfigType extends RequestPublicConfigType { export type RequestConfigType = {
// 组件定制轮询时间
requestInterval?: number
// 获取数据的方式 // 获取数据的方式
requestDataType: RequestDataTypeEnum requestDataType: RequestDataTypeEnum
// 请求方式 get/post/del/put/patch // 请求方式 get/post/del/put/patch
requestHttpType: RequestHttpEnum requestHttpType: RequestHttpEnum
// 源后续的 url // 源后续的 url
requestUrl?: string requestUrl?: string
// 请求内容主体方式 普通/sql
requestContentType: RequestContentTypeEnum
// 请求体类型
requestParamsBodyType: RequestBodyEnum
// SQL 请求对象
requestSQLContent: {
sql: string
}
} }
// Store 类型 // Store 类型
export interface ChartEditStoreType { export interface ChartEditStoreType {
[ChartEditStoreEnum.PROJECT_INFO]: ProjectInfoType
[ChartEditStoreEnum.EDIT_CANVAS]: EditCanvasType [ChartEditStoreEnum.EDIT_CANVAS]: EditCanvasType
[ChartEditStoreEnum.EDIT_CANVAS_CONFIG]: EditCanvasConfigType [ChartEditStoreEnum.EDIT_CANVAS_CONFIG]: EditCanvasConfigType
[ChartEditStoreEnum.RIGHT_MENU_SHOW]: boolean [ChartEditStoreEnum.RIGHT_MENU_SHOW]: boolean
@@ -214,7 +159,6 @@ export interface ChartEditStoreType {
[ChartEditStoreEnum.COMPONENT_LIST]: CreateComponentType[] [ChartEditStoreEnum.COMPONENT_LIST]: CreateComponentType[]
} }
// 需要存储的数据内容
export interface ChartEditStorage { export interface ChartEditStorage {
[ChartEditStoreEnum.EDIT_CANVAS_CONFIG]: EditCanvasConfigType [ChartEditStoreEnum.EDIT_CANVAS_CONFIG]: EditCanvasConfigType
[ChartEditStoreEnum.REQUEST_GLOBAL_CONFIG]: RequestGlobalConfigType [ChartEditStoreEnum.REQUEST_GLOBAL_CONFIG]: RequestGlobalConfigType

View File

@@ -3,17 +3,13 @@ import { CreateComponentType } from '@/packages/index.d'
import debounce from 'lodash/debounce' import debounce from 'lodash/debounce'
import cloneDeep from 'lodash/cloneDeep' import cloneDeep from 'lodash/cloneDeep'
import { defaultTheme, globalThemeJson } from '@/settings/chartThemes/index' import { defaultTheme, globalThemeJson } from '@/settings/chartThemes/index'
import { requestInterval, previewScaleType, requestIntervalUnit } from '@/settings/designSetting' import { requestInterval, previewScaleType } from '@/settings/designSetting'
import { RequestBodyEnum } from '@/enums/httpEnum'
// 记录记录 // 记录记录
import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore' import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore'
// 全局设置 // 全局设置
import { useSettingStore } from '@/store/modules/settingStore/settingStore' import { useSettingStore } from '@/store/modules/settingStore/settingStore'
// 历史类型
import { HistoryActionTypeEnum, HistoryItemType, HistoryTargetTypeEnum } from '@/store/modules/chartHistoryStore/chartHistoryStore.d' import { HistoryActionTypeEnum, HistoryItemType, HistoryTargetTypeEnum } from '@/store/modules/chartHistoryStore/chartHistoryStore.d'
// 画布枚举 import { MenuEnum } from '@/enums/editPageEnum'
import { MenuEnum, SyncEnum } from '@/enums/editPageEnum'
import { import {
getUUID, getUUID,
loadingStart, loadingStart,
@@ -22,9 +18,7 @@ import {
isString, isString,
isArray isArray
} from '@/utils' } from '@/utils'
import { import {
ProjectInfoType,
ChartEditStoreEnum, ChartEditStoreEnum,
ChartEditStorage, ChartEditStorage,
ChartEditStoreType, ChartEditStoreType,
@@ -43,13 +37,6 @@ const settingStore = useSettingStore()
export const useChartEditStore = defineStore({ export const useChartEditStore = defineStore({
id: 'useChartEditStore', id: 'useChartEditStore',
state: (): ChartEditStoreType => ({ state: (): ChartEditStoreType => ({
// 项目数据
projectInfo: {
projectName: '',
remarks: '',
thumbnail: '',
release: false
},
// 画布属性 // 画布属性
editCanvas: { editCanvas: {
// 编辑区域 Dom // 编辑区域 Dom
@@ -66,9 +53,7 @@ export const useChartEditStore = defineStore({
// 初始化 // 初始化
isCreate: false, isCreate: false,
// 拖拽中 // 拖拽中
isDrag: false, isDrag: false
// 同步中
saveStatus: SyncEnum.PENDING
}, },
// 右键菜单 // 右键菜单
rightMenuShow: false, rightMenuShow: false,
@@ -124,26 +109,12 @@ export const useChartEditStore = defineStore({
// 数据请求处理(需存储给后端) // 数据请求处理(需存储给后端)
requestGlobalConfig: { requestGlobalConfig: {
requestOriginUrl: '', requestOriginUrl: '',
requestInterval: requestInterval, requestInterval: requestInterval
requestIntervalUnit: requestIntervalUnit,
requestParams: {
Body: {
"form-data": {},
"x-www-form-urlencoded": {},
json: '',
xml: ''
},
Header: {},
Params: {}
}
}, },
// 图表数组(需存储给后端) // 图表数组(需存储给后端)
componentList: [] componentList: []
}), }),
getters: { getters: {
getProjectInfo(): ProjectInfoType {
return this.projectInfo
},
getMousePosition(): MousePositionType { getMousePosition(): MousePositionType {
return this.mousePosition return this.mousePosition
}, },
@@ -178,10 +149,6 @@ export const useChartEditStore = defineStore({
} }
}, },
actions: { actions: {
// * 设置 peojectInfo 数据项
setProjectInfo<T extends keyof ProjectInfoType, K extends ProjectInfoType[T]>(key: T, value: K) {
this.projectInfo[key] = value
},
// * 设置 editCanvas 数据项 // * 设置 editCanvas 数据项
setEditCanvas<T extends keyof EditCanvasType, K extends EditCanvasType[T]>(key: T, value: K) { setEditCanvas<T extends keyof EditCanvasType, K extends EditCanvasType[T]>(key: T, value: K) {
this.editCanvas[key] = value this.editCanvas[key] = value
@@ -208,7 +175,7 @@ export const useChartEditStore = defineStore({
this.targetChart.selectId = [] this.targetChart.selectId = []
return return
} }
// 新增 // 多选
if(push) { if(push) {
// 字符串 // 字符串
if(isString(selectId)) { if(isString(selectId)) {
@@ -413,7 +380,7 @@ export const useChartEditStore = defineStore({
type: isCut ? HistoryActionTypeEnum.CUT : HistoryActionTypeEnum.COPY type: isCut ? HistoryActionTypeEnum.CUT : HistoryActionTypeEnum.COPY
} }
this.setRecordChart(copyData) this.setRecordChart(copyData)
window['$message'].success(isCut ? '剪切图表成功' : '复制图表成功!') window['$message'].success(isCut ? '剪切成功' : '复制成功!')
loadingFinish() loadingFinish()
} }
} catch(value) { } catch(value) {
@@ -586,8 +553,8 @@ export const useChartEditStore = defineStore({
attr.x -= distance attr.x -= distance
break; break;
} }
}, },
// * 页面缩放设置----------------- // ----------------
// * 设置页面大小 // * 设置页面大小
setPageSize(scale: number): void { setPageSize(scale: number): void {
this.setPageStyle('height', `${this.editCanvasConfig.height * scale}px`) this.setPageStyle('height', `${this.editCanvasConfig.height * scale}px`)

View File

@@ -51,7 +51,8 @@ export enum HistoryStackItemEnum {
// 历史记录项类型 // 历史记录项类型
export interface HistoryItemType { export interface HistoryItemType {
[HistoryStackItemEnum.ID]: string // 会有同时操作多个组件场景
[HistoryStackItemEnum.ID]: string | string[]
[HistoryStackItemEnum.TARGET_TYPE]: HistoryTargetTypeEnum [HistoryStackItemEnum.TARGET_TYPE]: HistoryTargetTypeEnum
[HistoryStackItemEnum.ACTION_TYPE]: HistoryActionTypeEnum [HistoryStackItemEnum.ACTION_TYPE]: HistoryActionTypeEnum
[HistoryStackItemEnum.HISTORY_DATA]: CreateComponentType | EditCanvasType [HistoryStackItemEnum.HISTORY_DATA]: CreateComponentType | EditCanvasType

View File

@@ -4,10 +4,10 @@ import { asideCollapsedWidth } from '@/settings/designSetting'
import { SettingStoreType, ToolsStatusEnum } from './settingStore.d' import { SettingStoreType, ToolsStatusEnum } from './settingStore.d'
import { setLocalStorage, getLocalStorage } from '@/utils' import { setLocalStorage, getLocalStorage } from '@/utils'
import { StorageEnum } from '@/enums/storageEnum' import { StorageEnum } from '@/enums/storageEnum'
const { GO_SETTING_STORE } = StorageEnum const { GO_SYSTEM_SETTING_STORE } = StorageEnum
const storageSetting: SettingStoreType = getLocalStorage( const storageSetting: SettingStoreType = getLocalStorage(
GO_SETTING_STORE GO_SYSTEM_SETTING_STORE
) )
// 全局设置 // 全局设置
@@ -45,7 +45,7 @@ export const useSettingStore = defineStore({
this.$patch(state => { this.$patch(state => {
state[key] = value state[key] = value
}) })
setLocalStorage(GO_SETTING_STORE, this.$state) setLocalStorage(GO_SYSTEM_SETTING_STORE, this.$state)
} }
} }
}) })

View File

@@ -1,29 +0,0 @@
export enum SystemStoreUserInfoEnum {
USER_TOKEN = 'userToken',
USER_ID = 'userId',
USER_NAME = 'userName',
NICK_NAME = 'nickName',
}
export interface UserInfoType {
[SystemStoreUserInfoEnum.USER_TOKEN]?: string,
[SystemStoreUserInfoEnum.USER_ID]?: string,
[SystemStoreUserInfoEnum.USER_NAME]?: string,
[SystemStoreUserInfoEnum.NICK_NAME]?: string,
}
export interface FetchInfoType {
OSSUrl?: string,
}
export enum SystemStoreEnum {
// 用户
USER_INFO = 'userInfo',
// 请求
FETCH_INFO = 'fetchInfo'
}
export interface SystemStoreType {
[SystemStoreEnum.USER_INFO]: UserInfoType
[SystemStoreEnum.FETCH_INFO]: FetchInfoType
}

View File

@@ -1,40 +0,0 @@
import { defineStore } from 'pinia'
import { SystemStoreType, UserInfoType, FetchInfoType } from './systemStore.d'
import { setLocalStorage, getLocalStorage } from '@/utils'
import { StorageEnum } from '@/enums/storageEnum'
const { GO_SYSTEM_STORE } = StorageEnum
const storageSystem: SystemStoreType = getLocalStorage(GO_SYSTEM_STORE)
// 系统数据记录
export const useSystemStore = defineStore({
id: 'useSystemStore',
state: (): SystemStoreType => storageSystem || {
userInfo: {
userId: undefined,
userName: undefined,
userToken: undefined,
nickName: undefined
},
fetchInfo: {
OSSUrl: undefined
}
},
getters: {
getUserInfo(): UserInfoType {
return this.userInfo
},
getFetchInfo(): FetchInfoType {
return this.fetchInfo
},
},
actions: {
setItem<T extends keyof SystemStoreType, K extends SystemStoreType[T]>(key: T, value: K): void {
this.$patch(state => {
state[key] = value
});
setLocalStorage(GO_SYSTEM_STORE, this.$state)
}
}
})

View File

@@ -1 +0,0 @@
// 页面全局样式

View File

@@ -1,65 +1,3 @@
/**
* * base64转file
* @param dataurl
* @param fileName
* @returns
*/
export const base64toFile = (dataurl: string, fileName: string) => {
let dataArr = dataurl.split(","),
mime = (dataArr as any[])[0].match(/:(.*?);/)[1],
bstr = atob(dataArr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], fileName, { type: mime });
}
/**
* * file转url
*/
export const fileToUrl = (file: File): string => {
const Url = URL || window.URL || window.webkitURL
const ImageUrl = Url.createObjectURL(file)
return ImageUrl
}
/**
* * url转file
*/
export const urlToFile = (fileUrl: string, fileName = `${new Date().getTime()}`): File => {
const dataArr = fileUrl.split(',')
const mime = (dataArr as any[])[0].match(/:(.*);/)[1]
const originStr = atob(dataArr[1])
return new File([originStr], `${fileName}`, { type: mime })
}
/**
* * file转base64
* @param file 文件数据
* @param callback 回调函数
*/
export const fileTobase64 = (file: File, callback: Function) => {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = function (e: ProgressEvent<FileReader>) {
if (e.target) {
let base64 = e.target.result
callback(base64)
}
}
}
/**
* * canvas转file
* @param canvas
*/
export const canvastoFile = (canvas: HTMLCanvasElement, name?: string) => {
const dataurl = canvas.toDataURL('image/png')
return urlToFile(dataurl, name)
}
/** /**
* *获取上传的文件数据 * *获取上传的文件数据
* @param { File } file 文件对象 * @param { File } file 文件对象
@@ -100,7 +38,7 @@ export const downloadByA = (url: string, filename = new Date().getTime(), fileSu
} }
/** /**
* * 下载数据 * 下载数据
* @param { string } content 数据内容 * @param { string } content 数据内容
* @param { ?string } filename 文件名称(默认随机字符) * @param { ?string } filename 文件名称(默认随机字符)
* @param { ?string } fileSuffix 文件名称(默认随机字符) * @param { ?string } fileSuffix 文件名称(默认随机字符)
@@ -113,4 +51,4 @@ export const downloadTextFile = (
// 字符内容转变成blob地址 // 字符内容转变成blob地址
const blob = new Blob([content]) const blob = new Blob([content])
downloadByA(URL.createObjectURL(blob), filename, fileSuffix) downloadByA(URL.createObjectURL(blob), filename, fileSuffix)
} }

View File

@@ -1,6 +0,0 @@
/**
* * 请求失败统一处理
*/
export const httpErrorHandle = () => {
window['$message'].error(window['$t']('http.error_message'))
}

View File

@@ -7,4 +7,3 @@ export * from '@/utils/plugin'
export * from '@/utils/componets' export * from '@/utils/componets'
export * from '@/utils/type' export * from '@/utils/type'
export * from '@/utils/file' export * from '@/utils/file'
export * from '@/utils/http'

View File

@@ -35,7 +35,7 @@ export const loadingError = () => {
* }) * })
* ``` * ```
*/ */
export const goDialog = ( export const goDialog = (
params: { params: {
// 基本 // 基本
type?: DialogEnum type?: DialogEnum

View File

@@ -1,12 +1,11 @@
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { ResultEnum, RequestHttpHeaderEnum } from '@/enums/httpEnum' import { ResultEnum } from '@/enums/httpEnum'
import { ErrorPageNameMap, PageEnum, PreviewEnum } from '@/enums/pageEnum' import { ErrorPageNameMap, PageEnum } from '@/enums/pageEnum'
import { docPath, giteeSourceCodePath } from '@/settings/pathConst' import { docPath, giteeSourceCodePath } from '@/settings/pathConst'
import { SystemStoreEnum, SystemStoreUserInfoEnum } from '@/store/modules/systemStore/systemStore.d' import { cryptoDecode } from './crypto'
import { StorageEnum } from '@/enums/storageEnum' import { StorageEnum } from '@/enums/storageEnum'
import { clearLocalStorage, getLocalStorage, clearCookie } from './storage' import { clearLocalStorage, getLocalStorage } from './storage'
import router from '@/router' import router from '@/router'
import { logoutApi } from '@/api/path'
/** /**
* * 根据名字跳转路由 * * 根据名字跳转路由
@@ -102,20 +101,11 @@ export const reloadRoutePage = () => {
} }
/** /**
* * 退出登录 * * 退出
*/ */
export const logout = async () => { export const logout = () => {
try { clearLocalStorage(StorageEnum.GO_LOGIN_INFO_STORE)
const res = await logoutApi() as unknown as MyResponseType routerTurnByName(PageEnum.BASE_LOGIN_NAME)
if(res.code === ResultEnum.SUCCESS) {
window['$message'].success(window['$t']('global.logout_success'))
clearCookie(RequestHttpHeaderEnum.COOKIE)
clearLocalStorage(StorageEnum.GO_SYSTEM_STORE)
routerTurnByName(PageEnum.BASE_LOGIN_NAME)
}
} catch (error) {
window['$message'].success(window['$t']('global.logout_failure'))
}
} }
/** /**
@@ -163,19 +153,6 @@ export const fetchRouteParams = () => {
} }
} }
/**
* * 通过硬解析获取当前路由下的参数
* @returns object
*/
export const fetchRouteParamsLocation = () => {
try {
return document.location.hash.split('/').pop() || ''
} catch (error) {
window['$message'].warning('查询路由信息失败,请联系管理员!')
return ''
}
}
/** /**
* * 回到主页面 * * 回到主页面
* @param confirm * @param confirm
@@ -185,29 +162,19 @@ export const goHome = () => {
} }
/** /**
* * 判断是否登录 * * 判断是否登录(现阶段是有 login 数据即可)
* @return boolean * @return boolean
*/ */
export const loginCheck = () => { export const loginCheck = () => {
try { try {
const info = getLocalStorage(StorageEnum.GO_SYSTEM_STORE) const info = getLocalStorage(StorageEnum.GO_LOGIN_INFO_STORE)
if (!info) return false if (!info) return false
if (info[SystemStoreEnum.USER_INFO][SystemStoreUserInfoEnum.USER_TOKEN]) { const decodeInfo = cryptoDecode(info)
if (decodeInfo) {
return true return true
} }
return false return false
} catch (error) { } catch (error) {
return false return false
} }
} }
/**
* * 预览地址
* @returns
*/
export const previewPath = () => {
const { origin, pathname } = document.location
const path = fetchPathByName(PreviewEnum.CHART_PREVIEW_NAME, 'href')
const previewPath = `${origin}${pathname}${path}/${fetchRouteParamsLocation()}`
return previewPath
}

View File

@@ -5,7 +5,7 @@
* @param v 键值无需stringiiy * @param v 键值无需stringiiy
* @returns RemovableRef * @returns RemovableRef
*/ */
export const setLocalStorage = <T>(k: string, v: T) => { export const setLocalStorage = <T>(k: string, v: T) => {
try { try {
window.localStorage.setItem(k, JSON.stringify(v)) window.localStorage.setItem(k, JSON.stringify(v))
} catch (error) { } catch (error) {
@@ -18,7 +18,7 @@ export const setLocalStorage = <T>(k: string, v: T) => {
* @param k 键名 * @param k 键名
* @returns any * @returns any
*/ */
export const getLocalStorage = (k: string) => { export const getLocalStorage = (k: string) => {
const item = window.localStorage.getItem(k) const item = window.localStorage.getItem(k)
try { try {
return item ? JSON.parse(item) : item return item ? JSON.parse(item) : item
@@ -31,7 +31,7 @@ export const getLocalStorage = (k: string) => {
* * 清除本地会话数据 * * 清除本地会话数据
* @param name * @param name
*/ */
export const clearLocalStorage = (name: string) => { export const clearLocalStorage = (name: string) => {
window.localStorage.removeItem(name) window.localStorage.removeItem(name)
} }
@@ -68,42 +68,4 @@ export const getSessionStorage: (k: string) => any = (k: string) => {
*/ */
export const clearSessioStorage = (name: string) => { export const clearSessioStorage = (name: string) => {
window.sessionStorage.removeItem(name) window.sessionStorage.removeItem(name)
}
/**
* * 设置 cookie
* @param name 键名
* @param cvalue 键值
* @param exdays 过期时间
*/
export const setCookie = (name: string, cvalue: string, exdays: number) => {
const d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
const expires = "expires=" + d.toUTCString();
document.cookie = name + "=" + cvalue + "; " + expires;
}
/**
* * 获取 cookie
* @param cname 键名
* @returns string
*/
export const getCookie = (cname: string) => {
const name = cname + "=";
const ca = document.cookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1);
if (c.indexOf(name) != -1) return c.substring(name.length, c.length);
}
return "";
}
/**
* * 清除 cookie
* @param name 键名
* @returns string
*/
export const clearCookie = (name: string) => {
setCookie(name, "", -1);
} }

View File

@@ -6,7 +6,7 @@ import { EditCanvasConfigType } from '@/store/modules/chartEditStore/chartEditSt
type AttrType = PickCreateComponentType<'attr'> type AttrType = PickCreateComponentType<'attr'>
type StylesType = PickCreateComponentType<'styles'> type StylesType = PickCreateComponentType<'styles'>
// * 动画 // 动画
export const animationsClass = (animations: string[]) => { export const animationsClass = (animations: string[]) => {
if (animations.length) { if (animations.length) {
return `animate__animated animate__${animations[0]}` return `animate__animated animate__${animations[0]}`
@@ -14,7 +14,7 @@ export const animationsClass = (animations: string[]) => {
return '' return ''
} }
// * 滤镜 // 滤镜
export const getFilterStyle = (styles: StylesType | EditCanvasConfigType) => { export const getFilterStyle = (styles: StylesType | EditCanvasConfigType) => {
const { opacity, saturate, contrast, hueRotate, brightness } = styles const { opacity, saturate, contrast, hueRotate, brightness } = styles
return { return {
@@ -23,7 +23,7 @@ export const getFilterStyle = (styles: StylesType | EditCanvasConfigType) => {
} }
} }
// * 变换 // 变换
export const getTransformStyle = (styles: StylesType) => { export const getTransformStyle = (styles: StylesType) => {
const { rotateZ, rotateX, rotateY, skewX, skewY } = styles const { rotateZ, rotateX, rotateY, skewX, skewY } = styles
return { return {

View File

@@ -6,8 +6,7 @@ import Image_404 from '../assets/images/exception/image-404.png'
import html2canvas from 'html2canvas' import html2canvas from 'html2canvas'
import { downloadByA } from './file' import { downloadByA } from './file'
import { toString } from './type' import { toString } from './type'
import cloneDeep from 'lodash/cloneDeep' import cloneDeep from 'lodash/cloneDeep';
import { RequestHttpIntervalEnum, RequestParamsObjType } from '@/enums/httpEnum'
/** /**
* * 判断是否是开发环境 * * 判断是否是开发环境
@@ -109,6 +108,29 @@ export const isMac = () => {
return /macintosh|mac os x/i.test(navigator.userAgent) return /macintosh|mac os x/i.test(navigator.userAgent)
} }
/**
* * file转url
*/
export const fileToUrl = (file: File): string => {
const Url = URL || window.URL || window.webkitURL
const ImageUrl = Url.createObjectURL(file)
return ImageUrl
}
/**
* * file转base64
*/
export const fileTobase64 = (file: File, callback: Function) => {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = function (e: ProgressEvent<FileReader>) {
if (e.target) {
let base64 = e.target.result
callback(base64)
}
}
}
/** /**
* * 挂载监听 * * 挂载监听
*/ */
@@ -181,7 +203,7 @@ export const newFunctionHandle = (
try { try {
if (!funcStr) return data if (!funcStr) return data
const fn = new Function('data', funcStr) const fn = new Function('data', funcStr)
const fnRes = fn(cloneDeep(data)) const fnRes = fn( cloneDeep(data))
const resHandle = isToString ? toString(fnRes) : fnRes const resHandle = isToString ? toString(fnRes) : fnRes
// 成功回调 // 成功回调
successCallBack && successCallBack(resHandle) successCallBack && successCallBack(resHandle)
@@ -192,43 +214,3 @@ export const newFunctionHandle = (
return '函数执行错误' return '函数执行错误'
} }
} }
/**
* * 处理请求事件单位
* @param num 时间间隔
* @param unit RequestHttpIntervalEnum
* @return number 秒数
*/
export const intervalUnitHandle = (num: number, unit: RequestHttpIntervalEnum) => {
switch (unit) {
// 秒
case RequestHttpIntervalEnum.SECOND:
return num * 1000
// 分
case RequestHttpIntervalEnum.MINUTE:
return num * 1000 * 60
// 时
case RequestHttpIntervalEnum.HOUR:
return num * 1000 * 60 * 60
// 天
case RequestHttpIntervalEnum.DAY:
return num * 1000 * 60 * 60 * 24
default:
return num * 1000
}
}
/**
* * 对象转换 cookie 格式
* @param obj
* @returns string
*/
export const objToCookie = (obj: RequestParamsObjType) => {
if(!obj) return ''
let str = ''
for (const key in obj) {
str += key + '=' + obj[key] + ';'
}
return str.substr(0, str.length - 1)
}

View File

@@ -0,0 +1,30 @@
<template>
<div class="go-chart-data-setting">
<setting-item-box name="源地址" :alone="true">
<n-input
v-model:value.trim="chartEditStore.getRequestGlobalConfig.requestOriginUrl"
placeholder="源地址如: http://127.0.0.1"
></n-input>
</setting-item-box>
<setting-item-box name="更新间隔">
<n-input-number
v-model:value.trim="chartEditStore.getRequestGlobalConfig.requestInterval"
min="5"
:show-button="false"
placeholder="为 0 不更新"
>
<template #suffix>
</template>
</n-input-number>
</setting-item-box>
</div>
</template>
<script setup lang="ts">
import { SettingItemBox } from '@/components/Pages/ChartItemSetting'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
const chartEditStore = useChartEditStore()
</script>

View File

@@ -128,20 +128,16 @@ import { backgroundImageSize } from '@/settings/designSetting'
import { FileTypeEnum } from '@/enums/fileTypeEnum' import { FileTypeEnum } from '@/enums/fileTypeEnum'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { EditCanvasConfigEnum } from '@/store/modules/chartEditStore/chartEditStore.d' import { EditCanvasConfigEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
import { useSystemStore } from '@/store/modules/systemStore/systemStore'
import { StylesSetting } from '@/components/Pages/ChartItemSetting' import { StylesSetting } from '@/components/Pages/ChartItemSetting'
import { UploadCustomRequestOptions } from 'naive-ui' import { UploadCustomRequestOptions } from 'naive-ui'
import { fileToUrl, loadAsyncComponent, fetchRouteParamsLocation } from '@/utils' import { fileToUrl, loadAsyncComponent } from '@/utils'
import { PreviewScaleEnum } from '@/enums/styleEnum' import { PreviewScaleEnum } from '@/enums/styleEnum'
import { ResultEnum } from '@/enums/httpEnum'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { uploadFile} from '@/api/path'
const { ColorPaletteIcon } = icon.ionicons5 const { ColorPaletteIcon } = icon.ionicons5
const { ScaleIcon, FitToScreenIcon, FitToHeightIcon, FitToWidthIcon } = icon.carbon const { ZAxisIcon, ScaleIcon, FitToScreenIcon, FitToHeightIcon, FitToWidthIcon } = icon.carbon
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const systemStore = useSystemStore()
const canvasConfig = chartEditStore.getEditCanvasConfig const canvasConfig = chartEditStore.getEditCanvasConfig
const editCanvas = chartEditStore.getEditCanvas const editCanvas = chartEditStore.getEditCanvas
@@ -151,6 +147,9 @@ const switchSelectColorLoading = ref(false)
const ChartThemeColor = loadAsyncComponent(() => const ChartThemeColor = loadAsyncComponent(() =>
import('./components/ChartThemeColor/index.vue') import('./components/ChartThemeColor/index.vue')
) )
const ChartDataSetting = loadAsyncComponent(() =>
import('./components/ChartDataSetting/index.vue')
)
// 北京默认展示颜色列表 // 北京默认展示颜色列表
const swatchesColors = [ const swatchesColors = [
@@ -170,6 +169,12 @@ const globalTabList = [
title: '主题颜色', title: '主题颜色',
icon: ColorPaletteIcon, icon: ColorPaletteIcon,
render: ChartThemeColor render: ChartThemeColor
},
{
key: 'ChartSysSetting',
title: '数据配置',
icon: ZAxisIcon,
render: ChartDataSetting
} }
] ]
@@ -265,34 +270,17 @@ const switchSelectColorHandle = () => {
// 自定义上传操作 // 自定义上传操作
const customRequest = (options: UploadCustomRequestOptions) => { const customRequest = (options: UploadCustomRequestOptions) => {
const { file } = options const { file } = options
nextTick(async () => { nextTick(() => {
if(!systemStore.getFetchInfo.OSSUrl) {
window['$message'].error('添加图片失败,请刷新页面重试!')
return
}
if (file.file) { if (file.file) {
// 修改名称 const ImageUrl = fileToUrl(file.file)
const newNameFile = new File( chartEditStore.setEditCanvasConfig(
[file.file], EditCanvasConfigEnum.BACKGROUND_IMAGE,
`${fetchRouteParamsLocation()}_index_background.png`, ImageUrl
{ type: file.file.type } )
chartEditStore.setEditCanvasConfig(
EditCanvasConfigEnum.SELECT_COLOR,
false
) )
let uploadParams = new FormData()
uploadParams.append('object', newNameFile)
const uploadRes = await uploadFile(systemStore.getFetchInfo.OSSUrl ,uploadParams) as unknown as MyResponseType
if(uploadRes.code === ResultEnum.SUCCESS) {
chartEditStore.setEditCanvasConfig(
EditCanvasConfigEnum.BACKGROUND_IMAGE,
uploadRes.data.objectContent.httpRequest.uri
)
chartEditStore.setEditCanvasConfig(
EditCanvasConfigEnum.SELECT_COLOR,
false
)
return
}
window['$message'].error('添加图片失败,请稍后重试!')
} else { } else {
window['$message'].error('添加图片失败,请稍后重试!') window['$message'].error('添加图片失败,请稍后重试!')
} }

View File

@@ -1,56 +1,32 @@
<template> <template>
<div class="go-chart-configurations-data-ajax"> <div class="go-chart-configurations-data-ajax">
<n-card class="n-card-shallow"> <setting-item-box name="类型" :alone="true">
<setting-item-box name="请求配置"> <n-select v-model:value="targetData.data.requestHttpType" :options="selectOptions" />
<setting-item name="类型"> </setting-item-box>
<n-tag :bordered="false" type="primary" style="border-radius: 5px">
{{ requestContentType === RequestContentTypeEnum.DEFAULT ? '普通请求' : 'SQL请求' }}
</n-tag>
</setting-item>
<setting-item name="方式"> <setting-item-box name="源地址:" :alone="true">
<n-input size="small" :placeholder="requestHttpType || '暂无'" :disabled="true"></n-input> <n-text type="info">{{ requestOriginUrl || '暂无' }}</n-text>
</setting-item> </setting-item-box>
<setting-item name="组件间隔(高级)"> <setting-item-box :alone="true">
<n-input size="small" :placeholder="`${requestInterval || '暂无'}`" :disabled="true"> <template #name>
<template #suffix> {{ SelectHttpTimeNameObj[requestIntervalUnit] }} </template> 地址
</n-input> <n-tooltip trigger="hover" v-if="isDev()">
</setting-item> <template #trigger>
<n-icon size="21" :depth="3">
<setting-item name="全局间隔(默认)"> <help-outline-icon></help-outline-icon>
<n-input size="small" :placeholder="`${GlobalRequestInterval || '暂无'} `" :disabled="true"> </n-icon>
<template #suffix> {{ SelectHttpTimeNameObj[GlobalRequestIntervalUnit] }} </template>
</n-input>
</setting-item>
</setting-item-box>
<setting-item-box name="源地址" :alone="true">
<n-input size="small" :placeholder="requestOriginUrl || '暂无'" :disabled="true">
<template #prefix>
<n-icon :component="PulseIcon" />
</template> </template>
</n-input> <ul class="go-pl-0">
</setting-item-box> 开发环境使用 mock 数据请输入
<li v-for="item in apiList" :key="item.value">
<setting-item-box name="组件地址" :alone="true"> <n-text type="info"> {{ item.value }} </n-text>
<n-input size="small" :placeholder="requestUrl || '暂无'" :disabled="true"> </li>
<template #prefix> </ul>
<n-icon :component="FlashIcon" /> </n-tooltip>
</template> </template>
</n-input> <n-input v-model:value.trim="targetData.data.requestUrl" :min="1" placeholder="请输入地址(去除源)" />
</setting-item-box> </setting-item-box>
<n-space justify="end">
<n-text depth="3" style="font-size: 12px">更新内容请点击展示区域</n-text>
</n-space>
<div class="edit-text" @click="requestModelHandle">
<div class="go-absolute-center">
<n-button type="primary" secondary>查看更多</n-button>
</div>
</div>
</n-card>
<setting-item-box :alone="true"> <setting-item-box :alone="true">
<template #name> <template #name>
@@ -64,91 +40,105 @@
默认赋值给 dataset 字段 默认赋值给 dataset 字段
</n-tooltip> </n-tooltip>
</template> </template>
<n-button type="primary" ghost @click="sendHandle"> <n-space>
<template #icon> <n-button @click="sendHandle">
<n-icon> <template #icon>
<flash-icon /> <n-icon>
</n-icon> <flash-icon />
</template> </n-icon>
发送请求 </template>
</n-button> 发送请求
</n-button>
</n-space>
</setting-item-box> </setting-item-box>
<!-- 底部数据展示 -->
<chart-data-matching-and-show :show="showMatching && !loading" :ajax="true"></chart-data-matching-and-show> <chart-data-matching-and-show :show="showMatching && !loading" :ajax="true"></chart-data-matching-and-show>
<!-- 骨架图 -->
<go-skeleton :load="loading" :repeat="3"></go-skeleton> <go-skeleton :load="loading" :repeat="3"></go-skeleton>
<!-- 请求配置model -->
<chart-data-request v-model:modelShow="requestShow"></chart-data-request>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, toRefs, onBeforeUnmount, watchEffect, toRaw } from 'vue' import { ref, toRefs, onBeforeUnmount, watchEffect } from 'vue'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { useDesignStore } from '@/store/modules/designStore/designStore' import { SettingItemBox } from '@/components/Pages/ChartItemSetting'
import { SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' import { RequestHttpEnum, ResultEnum } from '@/enums/httpEnum'
import { ChartDataRequest } from '../ChartDataRequest/index'
import { RequestHttpEnum, ResultEnum, SelectHttpTimeNameObj, RequestContentTypeEnum } from '@/enums/httpEnum'
import { chartDataUrl, rankListUrl, scrollBoardUrl, numberFloatUrl, numberIntUrl, textUrl, imageUrl } from '@/api/mock' import { chartDataUrl, rankListUrl, scrollBoardUrl, numberFloatUrl, numberIntUrl, textUrl, imageUrl } from '@/api/mock'
import { http, customizeHttp } from '@/api/http' import { http } from '@/api/http'
import { SelectHttpType } from '../../index.d' import { SelectHttpType } from '../../index.d'
import { ChartDataMatchingAndShow } from '../ChartDataMatchingAndShow' import { ChartDataMatchingAndShow } from '../ChartDataMatchingAndShow'
import { useTargetData } from '../../../hooks/useTargetData.hook' import { useTargetData } from '../../../hooks/useTargetData.hook'
import { isDev, newFunctionHandle } from '@/utils' import { isDev, newFunctionHandle } from '@/utils'
const { HelpOutlineIcon, FlashIcon, PulseIcon } = icon.ionicons5 const { HelpOutlineIcon, FlashIcon } = icon.ionicons5
const { targetData, chartEditStore } = useTargetData() const { targetData, chartEditStore } = useTargetData()
const { requestUrl, requestHttpType, requestInterval, requestIntervalUnit, requestContentType } = toRefs( const { requestOriginUrl } = toRefs(chartEditStore.getRequestGlobalConfig)
targetData.value.request
)
const {
requestOriginUrl,
requestInterval: GlobalRequestInterval,
requestIntervalUnit: GlobalRequestIntervalUnit
} = toRefs(chartEditStore.getRequestGlobalConfig)
const designStore = useDesignStore()
const themeColor = ref(designStore.getAppTheme)
// 是否展示数据分析 // 是否展示数据分析
const loading = ref(false) const loading = ref(false)
const requestShow = ref(false)
const showMatching = ref(false) const showMatching = ref(false)
let firstFocus = 0
let lastFilter: any = undefined let lastFilter: any = undefined
// 请求配置 model const apiList = [
const requestModelHandle = () => { {
requestShow.value = true value: `【图表】${chartDataUrl}`
} },
{
value: `【文本】${textUrl}`
},
{
value: `【0~100 整数】${numberIntUrl}`
},
{
value: `【0~1小数】${numberFloatUrl}`
},
{
value: `【图片地址】${imageUrl}`
},
{
value: `【排名列表】${rankListUrl}`
},
{
value: `【滚动表格】${scrollBoardUrl}`
}
]
// 选项
const selectOptions: SelectHttpType[] = [
{
label: RequestHttpEnum.GET,
value: RequestHttpEnum.GET
},
{
label: RequestHttpEnum.POST,
value: RequestHttpEnum.POST
}
]
// 发送请求 // 发送请求
const sendHandle = async () => { const sendHandle = async () => {
if(!targetData.value?.request) return
loading.value = true loading.value = true
try { if(!targetData.value) return
const res = await customizeHttp(toRaw(targetData.value.request), toRaw(chartEditStore.requestGlobalConfig)) const { requestUrl, requestHttpType } = targetData.value.data
loading.value = false if (!requestUrl) {
if (res && res.data) { window['$message'].warning('请求参数不正确,请检查!')
targetData.value.option.dataset = newFunctionHandle(res.data, targetData.value.filter) return
showMatching.value = true
return
}
window['$message'].warning('数据异常,请检查参数!')
} catch (error) {
loading.value = false
window['$message'].warning('数据异常,请检查参数!')
} }
const completePath = requestOriginUrl && requestOriginUrl.value + requestUrl
const res = await http(requestHttpType)(completePath || '', {})
loading.value = false
if (res.status === ResultEnum.SUCCESS) {
targetData.value.option.dataset = newFunctionHandle(res.data, targetData.value.filter)
showMatching.value = true
return
}
window['$message'].warning('数据异常,请检查接口数据!')
} }
watchEffect(() => { watchEffect(() => {
const filter = targetData.value?.filter const filter = targetData.value?.filter
if (lastFilter !== filter && firstFocus) { if (lastFilter !== filter) {
lastFilter = filter lastFilter = filter
sendHandle() sendHandle()
} }
firstFocus ++
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
@@ -158,33 +148,5 @@ onBeforeUnmount(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
@include go('chart-configurations-data-ajax') { @include go('chart-configurations-data-ajax') {
.n-card-shallow {
&.n-card {
@extend .go-background-filter;
@include deep() {
.n-card__content {
padding: 10px;
}
}
}
.edit-text {
position: absolute;
top: 0px;
left: 0px;
width: 325px;
height: 292px;
cursor: pointer;
opacity: 0;
transition: all 0.3s;
@extend .go-background-filter;
backdrop-filter: blur(2px) !important;
}
&:hover {
border-color: v-bind('themeColor');
.edit-text {
opacity: 1;
}
}
}
} }
</style> </style>

View File

@@ -72,7 +72,7 @@
</div> </div>
</n-space> </n-space>
<n-card size="small"> <n-card size="small">
<n-code :code="toString(source)" language="json"></n-code> <n-code :code="filterRes(source)" language="json"></n-code>
</n-card> </n-card>
</n-space> </n-space>
</n-timeline-item> </n-timeline-item>
@@ -81,7 +81,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, watch } from 'vue' import { ref, computed, watch } from 'vue'
import { PackagesCategoryEnum } from '@/packages/index.d' import { CreateComponentType, PackagesCategoryEnum } from '@/packages/index.d'
import { RequestDataTypeEnum } from '@/enums/httpEnum' import { RequestDataTypeEnum } from '@/enums/httpEnum'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { DataResultEnum, TimelineTitleEnum } from '../../index.d' import { DataResultEnum, TimelineTitleEnum } from '../../index.d'
@@ -89,6 +89,7 @@ import { ChartDataMonacoEditor } from '../ChartDataMonacoEditor'
import { useFile } from '../../hooks/useFile.hooks' import { useFile } from '../../hooks/useFile.hooks'
import { useTargetData } from '../../../hooks/useTargetData.hook' import { useTargetData } from '../../../hooks/useTargetData.hook'
import isObject from 'lodash/isObject' import isObject from 'lodash/isObject'
import cloneDeep from 'lodash/cloneDeep'
import { toString } from '@/utils' import { toString } from '@/utils'
const { targetData } = useTargetData() const { targetData } = useTargetData()
@@ -117,7 +118,7 @@ const { uploadFileListRef, customRequest, beforeUpload, download } = useFile(tar
// 是否展示过滤器 // 是否展示过滤器
const filterShow = computed(() => { const filterShow = computed(() => {
return targetData.value.request.requestDataType === RequestDataTypeEnum.AJAX return targetData.value.data.requestDataType === RequestDataTypeEnum.AJAX
}) })
// 是图表类型 // 是图表类型
@@ -162,6 +163,20 @@ const dimensionsAndSourceHandle = () => {
} }
} }
// 过滤结果
const filterRes = (data: any) => {
try {
if(targetData.value.filter) {
const fn = new Function('data', targetData.value.filter)
const res = fn(cloneDeep(data))
return toString(res)
}
return toString(cloneDeep(data))
} catch (error) {
return '过滤函数错误'
}
}
watch( watch(
() => targetData.value?.option?.dataset, () => targetData.value?.option?.dataset,
( (

View File

@@ -17,13 +17,15 @@
</template> </template>
编辑 编辑
</n-button> </n-button>
<n-button tertiary size="small" @click="delFilter"> 删除 </n-button> <n-button tertiary size="small" @click="delFilter">
删除
</n-button>
</n-space> </n-space>
</template> </template>
</n-card> </n-card>
</template> </template>
<template v-else> <template v-else>
<n-button class="go-ml-3" @click="addFilter"> <n-button @click="addFilter">
<template #icon> <template #icon>
<n-icon> <n-icon>
<filter-icon /> <filter-icon />
@@ -95,19 +97,19 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, watch, toRefs, toRaw } from 'vue' import { ref, computed, watch, toRefs } from 'vue'
import { MonacoEditor } from '@/components/Pages/MonacoEditor' import { MonacoEditor } from '@/components/Pages/MonacoEditor'
import { useTargetData } from '../../../hooks/useTargetData.hook' import { useTargetData } from '../../../hooks/useTargetData.hook'
import { RequestHttpEnum, RequestDataTypeEnum, ResultEnum } from '@/enums/httpEnum' import { RequestHttpEnum, RequestDataTypeEnum, ResultEnum } from '@/enums/httpEnum'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { goDialog, toString } from '@/utils' import { goDialog, toString } from '@/utils'
import { http, customizeHttp } from '@/api/http' import { http } from '@/api/http'
import cloneDeep from 'lodash/cloneDeep' import cloneDeep from 'lodash/cloneDeep'
const { DocumentTextIcon } = icon.ionicons5 const { DocumentTextIcon } = icon.ionicons5
const { FilterIcon, FilterEditIcon } = icon.carbon const { FilterIcon, FilterEditIcon } = icon.carbon
const { targetData, chartEditStore } = useTargetData() const { targetData, chartEditStore } = useTargetData()
const { requestDataType } = toRefs(targetData.value.request) const { requestDataType } = toRefs(targetData.value.data)
const { requestOriginUrl } = toRefs(chartEditStore.getRequestGlobalConfig) const { requestOriginUrl } = toRefs(chartEditStore.getRequestGlobalConfig)
// 受控弹窗 // 受控弹窗
@@ -122,14 +124,20 @@ const sourceData = ref<any>('')
// 动态获取数据 // 动态获取数据
const fetchTargetData = async () => { const fetchTargetData = async () => {
try { try {
const res = await customizeHttp(toRaw(targetData.value.request), toRaw(chartEditStore.requestGlobalConfig)) const { requestUrl, requestHttpType } = targetData.value.data
if (res && res.data) { if (!requestUrl) {
window['$message'].warning('请求参数不正确,请检查!')
sourceData.value = '请求参数不正确,请检查!'
return
}
const completePath = requestOriginUrl && requestOriginUrl.value + requestUrl
const res = await http(requestHttpType)(completePath || '', {})
if (res.status === ResultEnum.SUCCESS) {
sourceData.value = res.data sourceData.value = res.data
return return
} }
window['$message'].warning('数据异常,请检查参数!')
} catch (error) { } catch (error) {
window['$message'].warning('数据异常,请检查参数') window['$message'].warning('数据异常,请检查接口数据')
} }
} }

View File

@@ -1,3 +0,0 @@
import RequestGlobalConfig from './index.vue'
export { RequestGlobalConfig }

View File

@@ -1,105 +0,0 @@
<template>
<!-- 全局配置 -->
<n-card class="n-card-shallow">
<n-tag type="info" :bordered="false" style="border-radius: 5px"> 全局配置 </n-tag>
<setting-item-box
name="服务"
:itemRightStyle="{
gridTemplateColumns: '5fr 2fr 1fr'
}"
>
<!-- 源地址 -->
<setting-item name="前置 URL">
<n-input v-model:value.trim="requestOriginUrl" :disabled="editDisabled" placeholder="例http://127.0.0.1/"></n-input>
</setting-item>
<setting-item name="更新间隔,为 0 只会初始化">
<n-input-group>
<n-input-number
class="select-time-number"
v-model:value.trim="requestInterval"
min="0"
:show-button="false"
:disabled="editDisabled"
placeholder="请输入数字"
>
</n-input-number>
<!-- 单位 -->
<n-select
class="select-time-options"
v-model:value="requestIntervalUnit"
:options="selectTimeOptions"
:disabled="editDisabled"
/>
</n-input-group>
</setting-item>
<n-button v-show="editDisabled" type="primary" ghost @click="editDisabled = false">
<template #icon>
<n-icon>
<pencil-icon />
</n-icon>
</template>
编辑配置
</n-button>
</setting-item-box>
<!-- table 内容体 -->
<n-collapse-transition :show="showTable">
<request-global-header-table :editDisabled="editDisabled"></request-global-header-table>
</n-collapse-transition>
<!-- 箭头 -->
<div v-if="showTable" class="go-flex-center go-mt-3 down" @click="showTable = false">
<n-icon size="32">
<chevron-up-outline-icon />
</n-icon>
</div>
<div v-else class="go-flex-center go-mt-3 down" @click="showTable = true">
<n-tooltip trigger="hover" placement="top" :keep-alive-on-hover="false">
<template #trigger>
<n-icon size="32">
<chevron-down-outline-icon />
</n-icon>
</template>
展开
</n-tooltip>
</div>
</n-card>
</template>
<script setup lang="ts">
import { ref, toRefs } from 'vue'
import { useDesignStore } from '@/store/modules/designStore/designStore'
import { SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { useTargetData } from '@/views/chart/ContentConfigurations/components/hooks/useTargetData.hook'
import { selectTypeOptions, selectTimeOptions } from '@/views/chart/ContentConfigurations/components/ChartData/index.d'
import { RequestGlobalHeaderTable } from '../RequestGlobalHeaderTable'
import { icon } from '@/plugins'
const { PencilIcon, ChevronDownOutlineIcon, ChevronUpOutlineIcon } = icon.ionicons5
const { chartEditStore } = useTargetData()
const { requestOriginUrl, requestInterval, requestIntervalUnit } = toRefs(chartEditStore.getRequestGlobalConfig)
const editDisabled = ref(true)
const designStore = useDesignStore()
const themeColor = ref(designStore.getAppTheme)
const showTable = ref(false)
</script>
<style lang="scss" scoped>
.n-card-shallow {
&:hover {
border-color: v-bind('themeColor');
}
}
.down {
cursor: pointer;
&:hover {
color: v-bind('themeColor');
}
}
.select-time-number {
width: 100%;
}
.select-time-options {
width: 100px;
}
</style>

View File

@@ -1,3 +0,0 @@
import RequestGlobalHeaderTable from './index.vue'
export { RequestGlobalHeaderTable }

View File

@@ -1,45 +0,0 @@
<template>
<div>
<n-tabs type="line" animated v-model:value="tabValue">
<n-tab v-for="item in tabs" :key="item" :name="item" :tab="item"> {{ item }} </n-tab>
</n-tabs>
<div class="go-mt-3">
<!-- 这里的 v-if 是为了处理打包 ts 错类型误 -->
<request-header-table
v-if="tabValue === RequestParamsTypeEnum.HEADER"
:editDisabled="editDisabled"
:target="requestParams[tabValue]"
@update="updateRequestParams"
></request-header-table>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, toRefs } from 'vue'
import { useTargetData } from '@/views/chart/ContentConfigurations/components/hooks/useTargetData.hook'
import { RequestHeaderTable } from '../RequestHeaderTable'
import { RequestParamsTypeEnum, RequestParamsObjType } from '@/enums/httpEnum'
defineProps({
editDisabled: {
type: Boolean,
default: true
}
})
const { chartEditStore } = useTargetData()
const { requestParams } = toRefs(chartEditStore.getRequestGlobalConfig)
const tabValue = ref<RequestParamsTypeEnum>(RequestParamsTypeEnum.HEADER)
const tabs = [RequestParamsTypeEnum.HEADER]
// 更新表格参数
const updateRequestParams = (paramsObj: RequestParamsObjType) => {
if (tabValue.value === RequestParamsTypeEnum.HEADER) {
requestParams.value[tabValue.value] = paramsObj
}
}
</script>
<style lang="scss" scoped></style>

View File

@@ -1,3 +0,0 @@
import RequestHeader from './index.vue'
export { RequestHeader }

View File

@@ -1,134 +0,0 @@
<template>
<n-space vertical>
<div style="width: 600px">
<n-tabs v-model:value="requestContentType" type="segment" size="small">
<n-tab :name="RequestContentTypeEnum.DEFAULT" tab="普通请求"> </n-tab>
<n-tab :name="RequestContentTypeEnum.SQL" tab="SQL 请求"> </n-tab>
</n-tabs>
</div>
<div v-show="requestContentType === RequestContentTypeEnum.DEFAULT">
<n-tabs type="line" animated v-model:value="tabValue">
<n-tab v-for="item in RequestParamsTypeEnum" :key="item" :name="item" :tab="item"> {{ item }} </n-tab>
</n-tabs>
<!-- 各个页面 -->
<div class="go-mt-3">
<div v-if="tabValue !== RequestParamsTypeEnum.BODY">
<request-header-table :target="requestParams[tabValue]" @update="updateRequestParams"></request-header-table>
</div>
<!-- 选择了 body -->
<div v-else>
<n-radio-group v-model:value="requestParamsBodyType" name="radiogroup">
<n-space>
<n-radio v-for="bodyEnum in RequestBodyEnumList" :key="bodyEnum" :value="bodyEnum">
{{ bodyEnum }}
</n-radio>
</n-space>
</n-radio-group>
<!-- none -->
<n-card class="go-mt-3 go-pb-3" v-if="requestParamsBodyType === RequestBodyEnum['NONE']">
<n-text depth="3">该请求没有 Body </n-text>
</n-card>
<!-- 具有对象属性时 -->
<template
v-else-if="
requestParamsBodyType === RequestBodyEnum['FORM_DATA'] ||
requestParamsBodyType === RequestBodyEnum['X_WWW_FORM_URLENCODED']
"
>
<request-header-table
class="go-mt-3"
:target="requestParams[RequestParamsTypeEnum.BODY][requestParamsBodyType]"
@update="updateRequestBodyTable"
></request-header-table>
</template>
<!-- json -->
<template v-else-if="requestParamsBodyType === RequestBodyEnum['JSON']">
<monaco-editor
v-model:modelValue="requestParams[RequestParamsTypeEnum.BODY][requestParamsBodyType]"
width="600px"
height="200px"
language="json"
/>
</template>
<!-- xml -->
<template v-else-if="requestParamsBodyType === RequestBodyEnum['XML']">
<monaco-editor
v-model:modelValue="requestParams[RequestParamsTypeEnum.BODY][requestParamsBodyType]"
width="600px"
height="200px"
language="html"
/>
</template>
</div>
</div>
</div>
<div v-show="requestContentType === RequestContentTypeEnum.SQL">
<template v-if="requestHttpType === RequestHttpEnum.GET">
<n-text>SQL 类型不支持 Get 请求,请使用其它方式</n-text>
</template>
<template v-else>
<setting-item-box name="键名">
<n-tag type="primary" :bordered="false" style="width: 40px; font-size: 16px"> sql </n-tag>
</setting-item-box>
<setting-item-box name="键值">
<monaco-editor v-model:modelValue="requestSQLContent['sql']" width="600px" height="200px" language="sql" />
</setting-item-box>
</template>
</div>
</n-space>
</template>
<script setup lang="ts">
import { ref, toRefs } from 'vue'
import { MonacoEditor } from '@/components/Pages/MonacoEditor'
import { SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { useTargetData } from '@/views/chart/ContentConfigurations/components/hooks/useTargetData.hook'
import { RequestHeaderTable } from '../RequestHeaderTable/index'
import {
RequestParamsTypeEnum,
RequestContentTypeEnum,
RequestParamsObjType,
RequestBodyEnumList,
RequestBodyEnum,
RequestHttpEnum
} from '@/enums/httpEnum'
const { targetData } = useTargetData()
const { requestHttpType, requestContentType, requestSQLContent, requestParams, requestParamsBodyType } = toRefs(targetData.value.request)
const tabValue = ref<RequestParamsTypeEnum>(RequestParamsTypeEnum.PARAMS)
// 更新参数表格数据
const updateRequestParams = (paramsObj: RequestParamsObjType) => {
if (tabValue.value !== RequestParamsTypeEnum.BODY) {
requestParams.value[tabValue.value] = paramsObj
}
}
// 更新参数表格数据
const updateRequestBodyTable = (paramsObj: RequestParamsObjType) => {
if (
tabValue.value === RequestParamsTypeEnum.BODY &&
// 仅有两种类型有 body
(requestParamsBodyType.value === RequestBodyEnum.FORM_DATA ||
requestParamsBodyType.value === RequestBodyEnum.X_WWW_FORM_URLENCODED)
) {
requestParams.value[RequestParamsTypeEnum.BODY][requestParamsBodyType.value] = paramsObj
}
}
</script>
<style lang="scss" scoped>
.select-type {
width: 300px;
}
</style>

View File

@@ -1,3 +0,0 @@
import RequestHeaderTable from './index.vue'
export { RequestHeaderTable }

View File

@@ -1,161 +0,0 @@
<template>
<n-table class="go-request-header-table-box" :single-line="false" size="small">
<thead>
<tr>
<th></th>
<th>Key</th>
<th>Value</th>
<th>操作</th>
<th>结果</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in tableArray.content" :key="index">
<td>
{{ index + 1 }}
</td>
<td>
<n-input v-model:value="item.key" :disabled="editDisabled" type="text" size="small" @blur="blur" />
</td>
<td>
<n-input v-model:value="item.value" :disabled="editDisabled" type="text" size="small" @blur="blur" />
</td>
<td>
<div style="width: 80px">
<n-button class="go-ml-2" type="primary" size="small" ghost :disabled="editDisabled" @click="add(index)"
>+</n-button
>
<n-button
class="go-ml-2"
type="warning"
size="small"
ghost
:disabled="index === 0 && editDisabled"
@click="remove(index)"
>
-
</n-button>
</div>
</td>
<td>
<n-button v-if="item.error" class="go-ml-2" text type="error"> 格式错误 </n-button>
<n-button v-else class="go-ml-2" text type="primary"> 格式通过 </n-button>
</td>
</tr>
</tbody>
</n-table>
</template>
<script setup lang="ts">
import { PropType, reactive, ref, toRefs, watch } from 'vue'
import { RequestParamsObjType } from '@/enums/httpEnum'
const emits = defineEmits(['update'])
const props = defineProps({
target: {
type: Object as PropType<RequestParamsObjType>,
required: true,
default: () => {}
},
editDisabled: {
type: Boolean,
required: false,
default: false
}
})
// 错误标识
const error = ref(false)
// 默认表格
const defaultItem = {
key: '',
value: '',
error: false
}
const tableArray = reactive<{
content: typeof defaultItem[]
}>({ content: [] })
// 失焦
const blur = () => {
let successNum = 0
tableArray.content.forEach(item => {
if ((item.key !== '' && item.value == '') || (item.key === '' && item.value !== '')) {
// 错误
item.error = true
} else {
// 正确
successNum++
item.error = false
}
})
// 验证是否全部通过
if (successNum == tableArray.content.length) {
// 转换数据成对象
const updateObj: any = {}
tableArray.content.forEach((e: typeof defaultItem) => {
if (e.key) updateObj[e.key] = e.value
})
emits('update', updateObj)
}
}
// 新增
const add = (index: number) => {
tableArray.content.splice(index + 1, 0, {
key: '',
value: '',
error: false
})
}
// 减少
const remove = (index: number) => {
if (tableArray.content.length !== 1) {
tableArray.content.splice(index, 1)
}
blur()
}
// 监听选项
watch(
() => props.target,
(target: RequestParamsObjType) => {
tableArray.content = []
for (const k in target) {
tableArray.content.push({
key: k,
value: target[k],
error: false
})
}
// 默认值
if (!tableArray.content.length) tableArray.content = [JSON.parse(JSON.stringify(defaultItem))]
},
{
immediate: true,
deep: true
}
)
</script>
<style lang="scss">
@include go('request-header-table-box') {
background-color: rgba(0, 0, 0, 0);
@include deep() {
.n-data-table .n-data-table-td {
background-color: rgba(0, 0, 0, 0);
}
.add-btn-box {
width: 100%;
display: flex;
justify-content: center;
.add-btn {
width: 300px;
}
}
}
}
</style>

View File

@@ -1,3 +0,0 @@
import RequestTargetConfig from './index.vue'
export { RequestTargetConfig }

View File

@@ -1,107 +0,0 @@
<template>
<!-- 组件配置 -->
<n-divider class="go-my-3" title-placement="left"></n-divider>
<setting-item-box
:itemRightStyle="{
gridTemplateColumns: '5fr 2fr 1fr'
}"
>
<template #name>
地址
<n-tooltip trigger="hover" v-if="isDev()">
<template #trigger>
<n-icon size="21" :depth="3">
<help-outline-icon></help-outline-icon>
</n-icon>
</template>
<ul class="go-pl-0">
开发环境使用 mock 数据请输入
<li v-for="item in apiList" :key="item.value">
<n-text type="info"> {{ item.value }} </n-text>
</li>
</ul>
</n-tooltip>
</template>
<setting-item name="请求方式 & URL 地址">
<n-input-group>
<n-select class="select-type-options" v-model:value="requestHttpType" :options="selectTypeOptions" />
<n-input v-model:value.trim="requestUrl" :min="1" placeholder="请输入地址去除前置URL">
<template #prefix>
<n-text>{{ requestOriginUrl }}</n-text>
<n-divider vertical />
</template>
</n-input>
</n-input-group>
<!-- 组件url -->
</setting-item>
<setting-item name="更新间隔,为 0 只会初始化">
<n-input-group>
<n-input-number
v-model:value.trim="requestInterval"
class="select-time-number"
min="0"
:show-button="false"
placeholder="默认使用全局数据"
>
</n-input-number>
<!-- 单位 -->
<n-select class="select-time-options" v-model:value="requestIntervalUnit" :options="selectTimeOptions" />
</n-input-group>
</setting-item>
</setting-item-box>
<setting-item-box name="选择方式" class="go-mt-0">
<request-header></request-header>
</setting-item-box>
</template>
<script setup lang="ts">
import { ref, toRefs } from 'vue'
import { SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { useTargetData } from '@/views/chart/ContentConfigurations/components/hooks/useTargetData.hook'
import { selectTypeOptions, selectTimeOptions } from '@/views/chart/ContentConfigurations/components/ChartData/index.d'
import { RequestHeader } from '../RequestHeader'
import { isDev } from '@/utils'
import { icon } from '@/plugins'
import { chartDataUrl, rankListUrl, scrollBoardUrl, numberFloatUrl, numberIntUrl, textUrl, imageUrl } from '@/api/mock'
const { HelpOutlineIcon } = icon.ionicons5
const { targetData, chartEditStore } = useTargetData()
const { requestOriginUrl } = toRefs(chartEditStore.getRequestGlobalConfig)
const { requestInterval, requestIntervalUnit, requestHttpType, requestUrl } = toRefs(targetData.value.request)
const apiList = [
{
value: `【图表】${chartDataUrl}`
},
{
value: `【文本】${textUrl}`
},
{
value: `【0~100 整数】${numberIntUrl}`
},
{
value: `【0~1小数】${numberFloatUrl}`
},
{
value: `【图片地址】${imageUrl}`
},
{
value: `【排名列表】${rankListUrl}`
},
{
value: `【滚动表格】${scrollBoardUrl}`
}
]
</script>
<style lang="scss" scoped>
.select-time-number {
width: 100%;
}
.select-time-options {
width: 100px;
}
.select-type-options {
width: 120px;
}
</style>

View File

@@ -1,3 +0,0 @@
import ChartDataRequest from './index.vue'
export { ChartDataRequest }

View File

@@ -1,75 +0,0 @@
<template>
<n-modal class="go-chart-data-request" v-model:show="modelShow" :mask-closable="false" @afterLeave="closeHandle">
<n-card :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 1000px; height: 800px">
<template #header></template>
<template #header-extra> </template>
<n-scrollbar style="max-height: 718px">
<div class="go-pr-3">
<n-space vertical>
<request-global-config></request-global-config>
<request-target-config></request-target-config>
</n-space>
</div>
</n-scrollbar>
<!-- 底部 -->
<template #action>
<n-space justify="space-between">
<div>
<n-text> {{ chartConfig.categoryName || rename }} </n-text>
<n-text> </n-text>
<n-tag type="primary" :bordered="false" style="border-radius: 5px"> {{ requestContentTypeObj[requestContentType] }} </n-tag>
</div>
<n-button type="primary" @click="closeHandle">确认</n-button>
</n-space>
</template>
</n-card>
</n-modal>
</template>
<script script lang="ts" setup>
import { toRefs } from 'vue'
import { RequestContentTypeEnum } from '@/enums/httpEnum'
import { useTargetData } from '../../../hooks/useTargetData.hook'
import { RequestGlobalConfig } from './components/RequestGlobalConfig'
import { RequestTargetConfig } from './components/RequestTargetConfig'
const emit = defineEmits(['update:modelShow'])
const { targetData } = useTargetData()
// 解构基础配置
const { chartConfig, rename } = toRefs(targetData.value)
const { requestContentType } = toRefs(targetData.value.request)
const requestContentTypeObj = {
[RequestContentTypeEnum.DEFAULT]: '普通请求',
[RequestContentTypeEnum.SQL]: 'SQL 请求'
}
defineProps({
modelShow: Boolean
})
const closeHandle = () => {
emit('update:modelShow', false)
}
</script>
<style lang="scss" scoped>
@include go('chart-data-request') {
&.n-card.n-modal,
.n-card {
@extend .go-background-filter;
}
.n-card-shallow {
background-color: rgba(0, 0, 0, 0) !important;
}
@include deep() {
& > .n-card__content {
padding-right: 0;
}
.n-card__content {
padding-bottom: 5px;
}
}
}
</style>

View File

@@ -1,4 +1,4 @@
import { RequestHttpEnum, RequestHttpIntervalEnum, RequestDataTypeEnum, SelectHttpTimeNameObj } from '@/enums/httpEnum' import { RequestHttpEnum, RequestDataTypeEnum } from '@/enums/httpEnum'
// 匹配结果 // 匹配结果
export enum DataResultEnum { export enum DataResultEnum {
@@ -24,81 +24,9 @@ export interface SelectCreateDataType {
disabled?: boolean disabled?: boolean
} }
// ajax 请求类型 // ajax 请求
export interface SelectHttpType { export interface SelectHttpType {
label: RequestHttpEnum label: RequestHttpEnum
value: RequestHttpEnum value: RequestHttpEnum
disabled?: boolean disabled?: boolean
style?: object
} }
// 类型选项
export const selectTypeOptions: SelectHttpType[] = [
{
label: RequestHttpEnum.GET,
value: RequestHttpEnum.GET,
style: {
color: 'greenyellow',
fontWeight: 'bold'
}
},
{
label: RequestHttpEnum.POST,
value: RequestHttpEnum.POST,
style: {
color: 'skyblue',
fontWeight: 'bold'
}
},
{
label: RequestHttpEnum.PUT,
value: RequestHttpEnum.PUT,
style: {
color: 'goldenrod',
fontWeight: 'bold'
}
},
{
label: RequestHttpEnum.PATCH,
value: RequestHttpEnum.PATCH,
style: {
color: 'violet',
fontWeight: 'bold'
}
},
{
label: RequestHttpEnum.DELETE,
value: RequestHttpEnum.DELETE,
disabled: true,
style: {
fontWeight: 'bold'
}
},
]
// ajax 请求间隔
export interface SelectHttpTimeType {
label: string
value: RequestHttpIntervalEnum
disabled?: boolean
}
// 时间选项
export const selectTimeOptions: SelectHttpTimeType[] = [
{
label: SelectHttpTimeNameObj[RequestHttpIntervalEnum.SECOND],
value: RequestHttpIntervalEnum.SECOND
},
{
label: SelectHttpTimeNameObj[RequestHttpIntervalEnum.MINUTE],
value: RequestHttpIntervalEnum.MINUTE
},
{
label: SelectHttpTimeNameObj[RequestHttpIntervalEnum.HOUR],
value: RequestHttpIntervalEnum.HOUR
},
{
label: SelectHttpTimeNameObj[RequestHttpIntervalEnum.DAY],
value: RequestHttpIntervalEnum.DAY
},
]

View File

@@ -2,14 +2,16 @@
<div class="go-chart-configurations-data" v-if="targetData"> <div class="go-chart-configurations-data" v-if="targetData">
<setting-item-box name="请求方式" :alone="true"> <setting-item-box name="请求方式" :alone="true">
<n-select <n-select
v-model:value="targetData.request.requestDataType" v-model:value="targetData.data.requestDataType"
:options="selectOptions" :options="selectOptions"
/> />
</setting-item-box> </setting-item-box>
<n-divider style="margin: 10px 0;"></n-divider>
<!-- 静态 --> <!-- 静态 -->
<chart-data-static <chart-data-static
v-if="targetData.request.requestDataType === RequestDataTypeEnum.STATIC" v-if="targetData.data.requestDataType === RequestDataTypeEnum.STATIC"
></chart-data-static> ></chart-data-static>
<!-- 动态 --> <!-- 动态 -->

View File

@@ -0,0 +1,13 @@
<template>
<div>
事件
</div>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>

View File

@@ -1,10 +1,6 @@
<template> <template>
<div class="go-edit-bottom"> <div class="go-edit-bottom">
<div class="go-flex-items-center"> <edit-history></edit-history>
<edit-history></edit-history>
<n-divider vertical />
<edit-data-sync></edit-data-sync>
</div>
<n-space class="bottom-ri"> <n-space class="bottom-ri">
<!-- 快捷键提示 --> <!-- 快捷键提示 -->
@@ -59,8 +55,7 @@
import { reactive, ref, toRefs, watchEffect } from 'vue' import { reactive, ref, toRefs, watchEffect } from 'vue'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { EditHistory } from '../EditHistory/index' import { EditHistory } from '../EditHistory/index'
import { EditShortcutKey } from '../EditShortcutKey/index' import EditShortcutKey from '../EditShortcutKey/index.vue'
import { EditDataSync } from '../EditDataSync/index'
import { useDesignStore } from '@/store/modules/designStore/designStore' import { useDesignStore } from '@/store/modules/designStore/designStore'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d' import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
@@ -141,13 +136,12 @@ watchEffect(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
$min-width: 500px; $min-width: 500px;
@include go('edit-bottom') { @include go('edit-bottom') {
width: 100%;
min-width: $min-width;
padding: 0 10px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 0 10px;
width: 100%;
min-width: $min-width;
height: 40px;
.bottom-ri { .bottom-ri {
position: relative; position: relative;
top: 15px; top: 15px;

View File

@@ -1,3 +0,0 @@
import EditDataSync from './index.vue'
export { EditDataSync }

View File

@@ -1,97 +0,0 @@
<template>
<div class="go-edit-data-sync go-flex-items-center">
<n-tooltip trigger="hover">
<template #trigger>
<n-text class="status-desc go-ml-2" :type="descType" depth="3">
{{ statusDesc }}
</n-text>
</template>
<span>{{saveInterval}}s 更新一次</span>
</n-tooltip>
<n-spin
v-show="statusDesc === statusDescObj[1]['text']"
class="go-ml-2"
size="small"
>
<template #icon>
<n-icon size="13">
<reload-icon />
</n-icon>
</template>
</n-spin>
</div>
</template>
<script lang="ts" setup>
import { ref, toRefs, watch } from 'vue'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { useDesignStore } from '@/store/modules/designStore/designStore'
import { SyncEnum } from '@/enums/editPageEnum'
import { icon } from '@/plugins'
import { saveInterval } from '@/settings/designSetting'
const { ReloadIcon } = icon.ionicons5
const chartEditStore = useChartEditStore()
const designStore = useDesignStore()
const { saveStatus } = toRefs(chartEditStore.getEditCanvas)
const themeColor = ref(designStore.getAppTheme)
const statusDesc = ref('')
const descType = ref('')
let setTimeoutIns: NodeJS.Timeout = setTimeout(() => {})
const statusDescObj = {
[SyncEnum.PENDING]: {
text: '等待自动同步',
type: '',
},
[SyncEnum.START]: {
text: '正在同步中',
type: 'success',
},
[SyncEnum.SUCCESS]: {
text: '同步成功!',
type: 'success',
},
[SyncEnum.FAILURE]: {
text: '同步失败!',
type: 'error',
},
}
watch(
() => saveStatus.value,
newData => {
clearTimeout(setTimeoutIns)
statusDesc.value = statusDescObj[newData]['text']
descType.value = statusDescObj[newData]['type']
// 3秒重置展示
setTimeoutIns = setTimeout(() => {
statusDesc.value = statusDescObj[SyncEnum.PENDING]['text']
descType.value = statusDescObj[SyncEnum.PENDING]['type']
}, 3000)
},
{
immediate: true,
}
)
</script>
<style lang="scss" scoped>
@include go('edit-data-sync') {
@include deep() {
.n-spin {
width: 13px;
height: 13px;
}
}
.status-desc {
cursor: default;
color: v-bind('themeColor');
font-size: 12px;
opacity: 0.8;
}
}
</style>

View File

@@ -10,7 +10,7 @@
> >
<template #trigger> <template #trigger>
<n-button <n-button
class="go-mr-1" class="mr-10"
secondary secondary
size="small" size="small"
:disabled="options.length === 0" :disabled="options.length === 0"
@@ -140,6 +140,9 @@ const handleClick = () => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.mr-10 {
margin-right: 10px;
}
.edit-history-popover { .edit-history-popover {
.btn-text { .btn-text {
font-size: 12px; font-size: 12px;

View File

@@ -100,9 +100,9 @@ const shortcutKeyOptions = [
mac: `${MacKeyboard.CTRL.toUpperCase()} + ${MacKeyboard.SHIFT.toUpperCase()} + Z `, mac: `${MacKeyboard.CTRL.toUpperCase()} + ${MacKeyboard.SHIFT.toUpperCase()} + Z `,
}, },
{ {
label: '保存', label: '多选',
win: `${WinKeyboard.CTRL.toUpperCase()} + S `, win: `${WinKeyboard.CTRL.toUpperCase()} + 🖱️ `,
mac: `${MacKeyboard.CTRL.toUpperCase()} + S `, mac: `${MacKeyboard.CTRL_SOURCE_KEY.toUpperCase()} + 🖱️ `,
}, },
] ]
const closeHandle = () => { const closeHandle = () => {

View File

@@ -1,3 +1,2 @@
import EditShortcutKey from './index.vue' import EditShortcutKey from './index.vue'
export { EditShortcutKey } export { EditShortcutKey }

View File

@@ -7,6 +7,7 @@ import { useSync } from '@/views/chart/hooks/useSync.hook'
export const useFile = () => { export const useFile = () => {
const importUploadFileListRef = ref() const importUploadFileListRef = ref()
const { updateComponent } = useSync() const { updateComponent } = useSync()
// 上传-前置 // 上传-前置
//@ts-ignore //@ts-ignore
const importBeforeUpload = ({ file }) => { const importBeforeUpload = ({ file }) => {

View File

@@ -68,7 +68,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue'; import { ref, computed, h } from 'vue';
import { useSettingStore } from '@/store/modules/settingStore/settingStore' import { useSettingStore } from '@/store/modules/settingStore/settingStore'
import { ToolsStatusEnum } from '@/store/modules/settingStore/settingStore.d' import { ToolsStatusEnum } from '@/store/modules/settingStore/settingStore.d'
import { exportHandle } from './utils' import { exportHandle } from './utils'

View File

@@ -9,7 +9,7 @@ export const exportHandle = () => {
// 导出数据 // 导出数据
downloadTextFile( downloadTextFile(
JSON.stringify(chartEditStore.getStorageInfo || {}, (k, v) => { JSON.stringify(chartEditStore.getStorageInfo || [], (k, v) => {
return v === undefined ? null : v return v === undefined ? null : v
}), }),
undefined, undefined,

View File

@@ -1,10 +1,7 @@
import { DragKeyEnum } from '@/enums/editPageEnum' import { DragKeyEnum, MouseEventButton, WinKeyboard, MacKeyboard } from '@/enums/editPageEnum'
import { createComponent } from '@/packages' import { createComponent } from '@/packages'
import { ConfigType } from '@/packages/index.d' import { ConfigType } from '@/packages/index.d'
import { import { CreateComponentType, PickCreateComponentType } from '@/packages/index.d'
CreateComponentType,
PickCreateComponentType,
} from '@/packages/index.d'
import { useContextMenu } from '@/views/chart/hooks/useContextMenu.hook' import { useContextMenu } from '@/views/chart/hooks/useContextMenu.hook'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d' import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
@@ -35,10 +32,7 @@ export const dragHandle = async (e: DragEvent) => {
// 创建新图表组件 // 创建新图表组件
let newComponent: CreateComponentType = await createComponent(dropData) let newComponent: CreateComponentType = await createComponent(dropData)
newComponent.setPosition( newComponent.setPosition(e.offsetX - newComponent.attr.w / 2, e.offsetY - newComponent.attr.h / 2)
e.offsetX - newComponent.attr.w / 2,
e.offsetY - newComponent.attr.h / 2
)
chartEditStore.addComponentList(newComponent, false, true) chartEditStore.addComponentList(newComponent, false, true)
chartEditStore.setTargetSelectChart(newComponent.id) chartEditStore.setTargetSelectChart(newComponent.id)
loadingFinish() loadingFinish()
@@ -57,10 +51,7 @@ export const dragoverHandle = (e: DragEvent) => {
} }
// * 不拦截默认行为点击 // * 不拦截默认行为点击
export const mousedownHandleUnStop = ( export const mousedownHandleUnStop = (e: MouseEvent, item?: CreateComponentType) => {
e: MouseEvent,
item?: CreateComponentType
) => {
if (item) { if (item) {
chartEditStore.setTargetSelectChart(item.id) chartEditStore.setTargetSelectChart(item.id)
return return
@@ -70,13 +61,42 @@ export const mousedownHandleUnStop = (
// * 移动图表 // * 移动图表
export const useMouseHandle = () => { export const useMouseHandle = () => {
// 点击事件(包含移动事件) // * Click 事件, 松开鼠标触发
const mouseClickHandle = (e: MouseEvent, item: CreateComponentType) => {
e.preventDefault()
e.stopPropagation()
// 若此时按下了 CTRL, 表示多选
if (
window.$KeyboardActive?.has(WinKeyboard.CTRL_SOURCE_KEY) ||
window.$KeyboardActive?.has(MacKeyboard.CTRL_SOURCE_KEY)
) {
chartEditStore.setTargetSelectChart(item.id, true)
}
}
// * 按下事件(包含移动事件)
const mousedownHandle = (e: MouseEvent, item: CreateComponentType) => { const mousedownHandle = (e: MouseEvent, item: CreateComponentType) => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
onClickOutSide() onClickOutSide()
// 按下左键 + CTRL
if (
e.buttons === MouseEventButton.LEFT &&
(window.$KeyboardActive?.has(WinKeyboard.CTRL_SOURCE_KEY) ||
window.$KeyboardActive?.has(MacKeyboard.CTRL_SOURCE_KEY))
)
return
// 按下右键 + 选中多个
if (e.buttons === MouseEventButton.RIGHT && chartEditStore.getTargetChart.selectId.length > 1) return
// 选中当前目标组件
chartEditStore.setTargetSelectChart(item.id) chartEditStore.setTargetSelectChart(item.id)
// 按下右键
if (e.buttons === MouseEventButton.RIGHT) return
const scale = chartEditStore.getEditCanvas.scale const scale = chartEditStore.getEditCanvas.scale
const width = chartEditStore.getEditCanvasConfig.width const width = chartEditStore.getEditCanvasConfig.width
const height = chartEditStore.getEditCanvasConfig.height const height = chartEditStore.getEditCanvasConfig.height
@@ -141,15 +161,11 @@ export const useMouseHandle = () => {
chartEditStore.setTargetHoverChart(undefined) chartEditStore.setTargetHoverChart(undefined)
} }
return { mousedownHandle, mouseenterHandle, mouseleaveHandle } return { mouseClickHandle, mousedownHandle, mouseenterHandle, mouseleaveHandle }
} }
// * 移动锚点 // * 移动锚点
export const useMousePointHandle = ( export const useMousePointHandle = (e: MouseEvent, point: string, attr: PickCreateComponentType<'attr'>) => {
e: MouseEvent,
point: string,
attr: PickCreateComponentType<'attr'>
) => {
e.stopPropagation() e.stopPropagation()
e.preventDefault() e.preventDefault()

View File

@@ -27,6 +27,7 @@
:index="index" :index="index"
:style="useComponentStyle(item.attr, index)" :style="useComponentStyle(item.attr, index)"
:item="item" :item="item"
@click="mouseClickHandle($event, item)"
@mousedown="mousedownHandle($event, item)" @mousedown="mousedownHandle($event, item)"
@mouseenter="mouseenterHandle($event, item)" @mouseenter="mouseenterHandle($event, item)"
@mouseleave="mouseleaveHandle($event, item)" @mouseleave="mouseleaveHandle($event, item)"
@@ -71,7 +72,6 @@ import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore
import { useLayout } from './hooks/useLayout.hook' import { useLayout } from './hooks/useLayout.hook'
import { useAddKeyboard } from '../hooks/useKeyboard.hook' import { useAddKeyboard } from '../hooks/useKeyboard.hook'
import { useSync } from '../hooks/useSync.hook'
import { dragHandle, dragoverHandle, useMouseHandle } from './hooks/useDrag.hook' import { dragHandle, dragoverHandle, useMouseHandle } from './hooks/useDrag.hook'
import { useComponentStyle, useSizeStyle } from './hooks/useStyle.hook' import { useComponentStyle, useSizeStyle } from './hooks/useStyle.hook'
@@ -83,13 +83,12 @@ import { EditTools } from './components/EditTools'
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const { handleContextMenu } = useContextMenu() const { handleContextMenu } = useContextMenu()
const { dataSyncFetch, intervalDataSyncUpdate } = useSync()
// 布局处理 // 布局处理
useLayout() useLayout()
// 点击事件 // 点击事件
const { mouseenterHandle, mouseleaveHandle, mousedownHandle } = useMouseHandle() const { mouseenterHandle, mouseleaveHandle, mousedownHandle, mouseClickHandle } = useMouseHandle()
// 主题色 // 主题色
const themeSetting = computed(() => { const themeSetting = computed(() => {
@@ -123,13 +122,9 @@ const rangeStyle = computed(() => {
} }
}) })
// 键盘事件
onMounted(() => { onMounted(() => {
// 键盘事件
useAddKeyboard() useAddKeyboard()
// 获取数据
dataSyncFetch()
// 定时更新数据
intervalDataSyncUpdate()
}) })
</script> </script>

View File

@@ -29,44 +29,26 @@
</template> </template>
<span>{{ item.title }}</span> <span>{{ item.title }}</span>
</n-tooltip> </n-tooltip>
<n-divider vertical />
<!-- 保存 -->
<n-tooltip placement="bottom" trigger="hover">
<template #trigger>
<div class="save-btn" >
<n-button size="small" type="primary" ghost @click="dataSyncUpdate()">
<template #icon>
<n-icon>
<SaveIcon></SaveIcon>
</n-icon>
</template>
</n-button>
</div>
</template>
<span>保存</span>
</n-tooltip>
</n-space> </n-space>
</n-space> </n-space>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { toRefs, ref, Ref, reactive, computed } from 'vue' import { toRefs, Ref, reactive, computed } from 'vue'
import { renderIcon, goDialog, goHome } from '@/utils' import { renderIcon, goDialog, goHome } from '@/utils'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { useRemoveKeyboard } from '../../hooks/useKeyboard.hook' import { useRemoveKeyboard } from '../../hooks/useKeyboard.hook'
import { useSync } from '../../hooks/useSync.hook'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore' import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore'
import { HistoryStackEnum } from '@/store/modules/chartHistoryStore/chartHistoryStore.d' import { HistoryStackEnum } from '@/store/modules/chartHistoryStore/chartHistoryStore.d'
import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore' import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore'
import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d' import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
const { LayersIcon, BarChartIcon, PrismIcon, HomeIcon, ArrowBackIcon, ArrowForwardIcon } = icon.ionicons5 const { LayersIcon, BarChartIcon, PrismIcon, HomeIcon, ArrowBackIcon, ArrowForwardIcon } = icon.ionicons5
const { SaveIcon } = icon.carbon
const { setItem } = useChartLayoutStore() const { setItem } = useChartLayoutStore()
const { dataSyncUpdate } = useSync()
const { getLayers, getCharts, getDetails } = toRefs(useChartLayoutStore()) const { getLayers, getCharts, getDetails } = toRefs(useChartLayoutStore())
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const chartHistoryStore = useChartHistoryStore() const chartHistoryStore = useChartHistoryStore()
@@ -148,7 +130,7 @@ const clickHistoryHandle = (item: ItemType<HistoryStackEnum>) => {
// 返回首页 // 返回首页
const goHomeHandle = () => { const goHomeHandle = () => {
goDialog({ goDialog({
message: '确定已保存了数据Ctrl / ⌘ + S并返回到首页吗', message: '返回将不会保存任何操作',
isMaskClosable: true, isMaskClosable: true,
onPositiveCallback: () => { onPositiveCallback: () => {
goHome() goHome()

View File

@@ -1,98 +1,29 @@
<template> <template>
<n-space> <n-space class="go-mt-0">
<n-button <n-button v-for="item in btnList" :key="item.title" ghost @click="item.event">
v-for="item in btnList"
:key="item.key"
:type="item.type()"
ghost
@click="item.event"
>
<template #icon> <template #icon>
<component :is="item.icon"></component> <component :is="item.icon"></component>
</template> </template>
<span>{{ item.title() }}</span> <span>{{ item.title }}</span>
</n-button> </n-button>
</n-space> </n-space>
<!-- 发布管理弹窗 -->
<n-modal v-model:show="modelShow" @afterLeave="closeHandle">
<n-list bordered class="go-system-setting">
<template #header>
<n-space justify="space-between">
<n-h3 class="go-mb-0">发布管理</n-h3>
<n-icon size="20" class="go-cursor-pointer" @click="closeHandle">
<close-icon></close-icon>
</n-icon>
</n-space>
</template>
<n-list-item>
<n-space :size="10">
<n-alert :show-icon="false" title="预览地址:" type="success">
{{ previewPath() }}
</n-alert>
<n-space vertical>
<n-button tertiary type="primary" @click="copyPreviewPath()">
复制地址
</n-button>
<n-button :type="release ? 'warning' : 'primary'" @click="sendHandle">
{{ release ? '取消发布' : '发布大屏' }}
</n-button>
</n-space>
</n-space>
</n-list-item>
<n-list-item>
<n-space :size="10">
<n-button @click="modelShowHandle">关闭弹窗</n-button>
</n-space>
</n-list-item>
</n-list>
</n-modal>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, shallowReactive, watchEffect } from 'vue' import { shallowReactive } from 'vue'
import { useRoute } from 'vue-router' import { renderIcon, goDialog, fetchPathByName, routerTurnByPath, setSessionStorage, getLocalStorage } from '@/utils'
import { useClipboard } from '@vueuse/core'
import { PreviewEnum } from '@/enums/pageEnum' import { PreviewEnum } from '@/enums/pageEnum'
import { StorageEnum } from '@/enums/storageEnum' import { StorageEnum } from '@/enums/storageEnum'
import { ResultEnum } from '@/enums/httpEnum' import { useRoute } from 'vue-router'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { ProjectInfoEnum } from '@/store/modules/chartEditStore/chartEditStore.d' import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
import { updateProjectApi } from '@/api/path'
import {
previewPath,
renderIcon,
fetchPathByName,
routerTurnByPath,
setSessionStorage,
getLocalStorage,
httpErrorHandle,
fetchRouteParamsLocation,
} from '@/utils'
import { icon } from '@/plugins' import { icon } from '@/plugins'
const { BrowsersOutlineIcon, SendIcon, CloseIcon } = icon.ionicons5 const { BrowsersOutlineIcon, SendIcon } = icon.ionicons5
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const previewPathRef = ref(previewPath())
const { copy, isSupported } = useClipboard({ source: previewPathRef })
const routerParamsInfo = useRoute() const routerParamsInfo = useRoute()
const modelShow = ref<boolean>(false)
const release = ref<boolean>(false)
watchEffect(() => {
release.value = chartEditStore.getProjectInfo.release || false
})
// 关闭弹窗
const closeHandle = () => {
modelShow.value = false
}
// 预览 // 预览
const previewHandle = () => { const previewHandle = () => {
const path = fetchPathByName(PreviewEnum.CHART_PREVIEW_NAME, 'href') const path = fetchPathByName(PreviewEnum.CHART_PREVIEW_NAME, 'href')
@@ -101,100 +32,55 @@ const previewHandle = () => {
// id 标识 // id 标识
const previewId = typeof id === 'string' ? id : id[0] const previewId = typeof id === 'string' ? id : id[0]
const storageInfo = chartEditStore.getStorageInfo const storageInfo = chartEditStore.getStorageInfo
const sessionStorageInfo = const sessionStorageInfo = getLocalStorage(StorageEnum.GO_CHART_STORAGE_LIST) || []
getLocalStorage(StorageEnum.GO_CHART_STORAGE_LIST) || []
if (sessionStorageInfo?.length) { if (sessionStorageInfo?.length) {
const repeateIndex = sessionStorageInfo.findIndex( const repeateIndex = sessionStorageInfo.findIndex((e: { id: string }) => e.id === previewId)
(e: { id: string }) => e.id === previewId
)
// 重复替换 // 重复替换
if (repeateIndex !== -1) { if (repeateIndex !== -1) {
sessionStorageInfo.splice(repeateIndex, 1, { sessionStorageInfo.splice(repeateIndex, 1, { id: previewId, ...storageInfo })
id: previewId,
...storageInfo,
})
setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, sessionStorageInfo) setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, sessionStorageInfo)
} else { } else {
sessionStorageInfo.push({ sessionStorageInfo.push({
id: previewId, id: previewId, ...storageInfo
...storageInfo,
}) })
setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, sessionStorageInfo) setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, sessionStorageInfo)
} }
} else { } else {
setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, [ setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, [{ id: previewId, ...storageInfo }])
{ id: previewId, ...storageInfo },
])
} }
// 跳转 // 跳转
routerTurnByPath(path, [previewId], undefined, true) routerTurnByPath(path, [previewId], undefined, true)
} }
// 模态弹窗
const modelShowHandle = () => {
modelShow.value = !modelShow.value
}
// 复制预览地址
const copyPreviewPath = (successText?: string, failureText?: string) => {
if (isSupported) {
copy()
window['$message'].success(successText || '复制成功!')
} else {
window['$message'].error(failureText || '复制失败!')
}
}
// 发布 // 发布
const sendHandle = async () => { const sendHandle = () => {
const res = (await updateProjectApi({ goDialog({
id: fetchRouteParamsLocation(), message: '想体验发布功能,请前往 master-fetch 分支查看: https://gitee.com/MTrun/go-view/tree/master-fetch',
// 反过来 positiveText: '了然',
state: release.value ? -1 : 1, closeNegativeText: true,
})) as unknown as MyResponseType onPositiveCallback: () => {}
})
if (res.code === ResultEnum.SUCCESS) {
modelShowHandle()
if (!release.value) {
copyPreviewPath('发布成功!已复制地址到剪贴板~', '发布成功!')
} else {
window['$message'].success(`已取消发布`)
}
chartEditStore.setProjectInfo(ProjectInfoEnum.RELEASE, !release.value)
} else {
httpErrorHandle()
}
} }
const btnList = shallowReactive([ const btnList = shallowReactive([
{ {
key: 'preview', select: true,
title: () => '预览', title: '预览',
type: () => 'default',
icon: renderIcon(BrowsersOutlineIcon), icon: renderIcon(BrowsersOutlineIcon),
event: previewHandle, event: previewHandle
}, },
{ {
key: 'release', select: true,
title: () => (release.value ? '已发布' : '发布'), title: '发布',
icon: renderIcon(SendIcon), icon: renderIcon(SendIcon),
type: () => (release.value ? 'primary' : 'default'), event: sendHandle
event: modelShowHandle,
},
])
</script>
<style lang="scss" scoped>
@include go('system-setting') {
@extend .go-background-filter;
min-width: 100px;
max-width: 60vw;
padding-bottom: 20px;
@include deep() {
.n-list-item:not(:last-child) {
border-bottom: 0;
}
} }
])
</script>
<style lang="scss" scoped>
.align-center {
margin-top: -4px;
} }
</style> </style>

View File

@@ -6,7 +6,9 @@
<n-text @click="handleFocus"> <n-text @click="handleFocus">
工作空间 - 工作空间 -
<n-button v-show="!focus" secondary round size="tiny"> <n-button v-show="!focus" secondary round size="tiny">
<span class="title">{{ comTitle }}</span> <span class="title">
{{ comTitle }}
</span>
</n-button> </n-button>
</n-text> </n-text>
@@ -26,31 +28,31 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, nextTick, computed, watchEffect } from 'vue' import { ref, nextTick, computed } from 'vue'
import { ResultEnum } from '@/enums/httpEnum' import { fetchRouteParams } from '@/utils'
import { fetchRouteParamsLocation, httpErrorHandle } from '@/utils'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { ProjectInfoEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
import { updateProjectApi } from '@/api/path'
import { useSync } from '../../hooks/useSync.hook'
import { icon } from '@/plugins' import { icon } from '@/plugins'
const chartEditStore = useChartEditStore()
const { dataSyncUpdate } = useSync()
const { FishIcon } = icon.ionicons5 const { FishIcon } = icon.ionicons5
const focus = ref<boolean>(false) const focus = ref<boolean>(false)
const inputInstRef = ref(null) const inputInstRef = ref(null)
const title = ref<string>(fetchRouteParamsLocation()) // 根据路由 id 参数获取项目信息
const fetchProhectInfoById = () => {
const routeParamsRes = fetchRouteParams()
if (!routeParamsRes) return
const { id } = routeParamsRes
if (id.length) {
return id[0]
}
return ''
}
const title = ref<string>(fetchProhectInfoById() || '')
watchEffect(() => {
title.value = chartEditStore.getProjectInfo.projectName || ''
})
const comTitle = computed(() => { const comTitle = computed(() => {
title.value = title.value && title.value.replace(/\s/g, "") title.value = title.value.replace(/\s/g, "");
return title.value.length ? title.value : fetchRouteParamsLocation() return title.value.length ? title.value : '新项目'
}) })
const handleFocus = () => { const handleFocus = () => {
@@ -60,22 +62,12 @@ const handleFocus = () => {
}) })
} }
const handleBlur = async () => { const handleBlur = () => {
focus.value = false focus.value = false
chartEditStore.setProjectInfo(ProjectInfoEnum.PROJECT_NAME, title.value || '')
const res = await updateProjectApi({
id: fetchRouteParamsLocation(),
projectName: title.value,
}) as unknown as MyResponseType
if(res.code === ResultEnum.SUCCESS) {
dataSyncUpdate()
} else {
httpErrorHandle()
}
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.title { .title {
font-size: 15px; font-size: 15px;
} }
</style> </style>

View File

@@ -6,82 +6,85 @@ import { icon } from '@/plugins'
import { MenuOptionsItemType } from './useContextMenu.hook.d' import { MenuOptionsItemType } from './useContextMenu.hook.d'
import { MenuEnum } from '@/enums/editPageEnum' import { MenuEnum } from '@/enums/editPageEnum'
const { const { CopyIcon, CutIcon, ClipboardOutlineIcon, TrashIcon, ChevronDownIcon, ChevronUpIcon } = icon.ionicons5
CopyIcon, const { UpToTopIcon, DownToBottomIcon, PaintBrushIcon, Carbon3DSoftwareIcon, Carbon3DCursorIcon } = icon.carbon
CutIcon,
ClipboardOutlineIcon,
TrashIcon,
ChevronDownIcon,
ChevronUpIcon,
} = icon.ionicons5
const { UpToTopIcon, DownToBottomIcon, PaintBrushIcon } = icon.carbon
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
// * 默认选项 // * 默认单组件选项
const defaultOptions: MenuOptionsItemType[] = [ const defaultOptions: MenuOptionsItemType[] = [
{ {
label: '复制', label: '复制',
key: MenuEnum.COPY, key: MenuEnum.COPY,
icon: renderIcon(CopyIcon), icon: renderIcon(CopyIcon),
fnHandle: chartEditStore.setCopy, fnHandle: chartEditStore.setCopy
}, },
{ {
label: '剪切', label: '剪切',
key: MenuEnum.CUT, key: MenuEnum.CUT,
icon: renderIcon(CutIcon), icon: renderIcon(CutIcon),
fnHandle: chartEditStore.setCut, fnHandle: chartEditStore.setCut
}, },
{ {
label: '粘贴', label: '粘贴',
key: MenuEnum.PARSE, key: MenuEnum.PARSE,
icon: renderIcon(ClipboardOutlineIcon), icon: renderIcon(ClipboardOutlineIcon),
fnHandle: chartEditStore.setParse, fnHandle: chartEditStore.setParse
}, },
{ {
type: 'divider', type: 'divider',
key: 'd1', key: 'd1'
}, },
{ {
label: '置顶', label: '置顶',
key: MenuEnum.TOP, key: MenuEnum.TOP,
icon: renderIcon(UpToTopIcon), icon: renderIcon(UpToTopIcon),
fnHandle: chartEditStore.setTop, fnHandle: chartEditStore.setTop
}, },
{ {
label: '置底', label: '置底',
key: MenuEnum.BOTTOM, key: MenuEnum.BOTTOM,
icon: renderIcon(DownToBottomIcon), icon: renderIcon(DownToBottomIcon),
fnHandle: chartEditStore.setBottom, fnHandle: chartEditStore.setBottom
}, },
{ {
label: '上移一层', label: '上移一层',
key: MenuEnum.UP, key: MenuEnum.UP,
icon: renderIcon(ChevronUpIcon), icon: renderIcon(ChevronUpIcon),
fnHandle: chartEditStore.setUp, fnHandle: chartEditStore.setUp
}, },
{ {
label: '下移一层', label: '下移一层',
key: MenuEnum.DOWN, key: MenuEnum.DOWN,
icon: renderIcon(ChevronDownIcon), icon: renderIcon(ChevronDownIcon),
fnHandle: chartEditStore.setDown, fnHandle: chartEditStore.setDown
}, },
{ {
type: 'divider', type: 'divider',
key: 'd2', key: 'd2'
}, },
{ {
label: '清空剪贴板', label: '清空剪贴板',
key: MenuEnum.CLEAR, key: MenuEnum.CLEAR,
icon: renderIcon(PaintBrushIcon), icon: renderIcon(PaintBrushIcon),
fnHandle: chartEditStore.setRecordChart, fnHandle: chartEditStore.setRecordChart
}, },
{ {
label: '删除', label: '删除',
key: MenuEnum.DELETE, key: MenuEnum.DELETE,
icon: renderIcon(TrashIcon), icon: renderIcon(TrashIcon),
fnHandle: chartEditStore.removeComponentList, fnHandle: chartEditStore.removeComponentList
}, }
]
// * 默认多选组件选项
const defaultMultiSelectionOptions: MenuOptionsItemType[] = [
{
label: '成组',
key: MenuEnum.COPY,
icon: renderIcon(Carbon3DSoftwareIcon),
fnHandle: chartEditStore.setCopy
}
] ]
// * 无数据传递拥有的选项 // * 无数据传递拥有的选项
@@ -126,7 +129,7 @@ const handleContextMenu = (
// 隐藏选项列表 // 隐藏选项列表
hideOptionsList?: MenuEnum[], hideOptionsList?: MenuEnum[],
// 挑选选项列表 // 挑选选项列表
pickOptionsList?: MenuEnum[], pickOptionsList?: MenuEnum[]
) => { ) => {
e.stopPropagation() e.stopPropagation()
e.preventDefault() e.preventDefault()
@@ -136,8 +139,13 @@ const handleContextMenu = (
} }
chartEditStore.setRightMenuShow(false) chartEditStore.setRightMenuShow(false)
// * 设置默认选项 // * 多选默认选项
menuOptions.value = defaultOptions if (chartEditStore.getTargetChart.selectId.length > 1) {
menuOptions.value = defaultMultiSelectionOptions
} else {
// * 单选默认选项
menuOptions.value = defaultOptions
}
if (!item) { if (!item) {
menuOptions.value = pickOption(menuOptions.value, defaultNoItemKeys) menuOptions.value = pickOption(menuOptions.value, defaultNoItemKeys)
@@ -163,7 +171,6 @@ const handleContextMenu = (
* @returns * @returns
*/ */
export const useContextMenu = () => { export const useContextMenu = () => {
// 设置默认项 // 设置默认项
menuOptions.value = defaultOptions menuOptions.value = defaultOptions
@@ -175,9 +182,7 @@ export const useContextMenu = () => {
// * 事件处理 // * 事件处理
const handleMenuSelect = (key: string) => { const handleMenuSelect = (key: string) => {
chartEditStore.setRightMenuShow(false) chartEditStore.setRightMenuShow(false)
const targetItem: MenuOptionsItemType[] = menuOptions.value.filter( const targetItem: MenuOptionsItemType[] = menuOptions.value.filter((e: MenuOptionsItemType) => e.key === key)
(e: MenuOptionsItemType) => e.key === key
)
menuOptions.value.forEach((e: MenuOptionsItemType) => { menuOptions.value.forEach((e: MenuOptionsItemType) => {
if (e.key === key) { if (e.key === key) {
@@ -189,12 +194,12 @@ export const useContextMenu = () => {
} }
}) })
} }
return { return {
menuOptions, menuOptions,
handleContextMenu, handleContextMenu,
onClickOutSide, onClickOutSide,
handleMenuSelect, handleMenuSelect,
mousePosition: chartEditStore.getMousePosition, mousePosition: chartEditStore.getMousePosition
} }
} }

View File

@@ -1,5 +1,4 @@
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { useSync } from './useSync.hook'
import { WinKeyboard, MacKeyboard, MenuEnum } from '@/enums/editPageEnum' import { WinKeyboard, MacKeyboard, MenuEnum } from '@/enums/editPageEnum'
import throttle from 'lodash/throttle' import throttle from 'lodash/throttle'
import debounce from 'lodash/debounce' import debounce from 'lodash/debounce'
@@ -7,7 +6,7 @@ import debounce from 'lodash/debounce'
import keymaster from 'keymaster' import keymaster from 'keymaster'
// Keymaster可以支持识别以下组合键shiftoptionaltctrlcontrolcommand和⌘ // Keymaster可以支持识别以下组合键shiftoptionaltctrlcontrolcommand和⌘
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const useSyncIns = useSync()
const winCtrlMerge = (e: string) => `${WinKeyboard.CTRL}+${e}` const winCtrlMerge = (e: string) => `${WinKeyboard.CTRL}+${e}`
const winShiftMerge = (e: string) => `${WinKeyboard.SHIFT}+${e}` const winShiftMerge = (e: string) => `${WinKeyboard.SHIFT}+${e}`
const winAltMerge = (e: string) => `${WinKeyboard.ALT}+${e}` const winAltMerge = (e: string) => `${WinKeyboard.ALT}+${e}`
@@ -23,7 +22,6 @@ export const winKeyboardValue = {
[MenuEnum.DELETE]: 'delete', [MenuEnum.DELETE]: 'delete',
[MenuEnum.BACK]: winCtrlMerge('z'), [MenuEnum.BACK]: winCtrlMerge('z'),
[MenuEnum.FORWORD]: winCtrlMerge(winShiftMerge('z')), [MenuEnum.FORWORD]: winCtrlMerge(winShiftMerge('z')),
[MenuEnum.SAVE]: winCtrlMerge('s'),
} }
// 这个 Ctrl 后面还是换成了 ⌘ // 这个 Ctrl 后面还是换成了 ⌘
@@ -43,7 +41,6 @@ export const macKeyboardValue = {
[MenuEnum.DELETE]: macCtrlMerge('backspace'), [MenuEnum.DELETE]: macCtrlMerge('backspace'),
[MenuEnum.BACK]: macCtrlMerge('z'), [MenuEnum.BACK]: macCtrlMerge('z'),
[MenuEnum.FORWORD]: macCtrlMerge(macShiftMerge('z')), [MenuEnum.FORWORD]: macCtrlMerge(macShiftMerge('z')),
[MenuEnum.SAVE]: macCtrlMerge('s'),
} }
// Win 快捷键列表 // Win 快捷键列表
@@ -60,8 +57,6 @@ const winKeyList: Array<string> = [
winKeyboardValue.back, winKeyboardValue.back,
winKeyboardValue.forward, winKeyboardValue.forward,
winKeyboardValue.save,
] ]
// Mac 快捷键列表 // Mac 快捷键列表
@@ -78,20 +73,18 @@ const macKeyList: Array<string> = [
macKeyboardValue.back, macKeyboardValue.back,
macKeyboardValue.forward, macKeyboardValue.forward,
macKeyboardValue.save,
] ]
// 处理键盘记录 // 处理键盘记录
const keyRecordHandle = () => { const keyRecordHandle = () => {
document.onkeydown = throttle((e: KeyboardEvent) => { document.onkeydown = (e: KeyboardEvent) => {
if(window.$KeyboardActive) window.$KeyboardActive.add(e.key.toLocaleLowerCase()) if(window.$KeyboardActive) window.$KeyboardActive.add(e.key.toLocaleLowerCase())
else window.$KeyboardActive = new Set([e.key]) else window.$KeyboardActive = new Set([e.key.toLocaleLowerCase()])
}, 200) }
document.onkeyup = throttle((e: KeyboardEvent) => { document.onkeyup = (e: KeyboardEvent) => {
if(window.$KeyboardActive) window.$KeyboardActive.delete(e.key.toLocaleLowerCase()) if(window.$KeyboardActive) window.$KeyboardActive.delete(e.key.toLocaleLowerCase())
}, 200) }
} }
// 初始化监听事件 // 初始化监听事件
@@ -140,11 +133,6 @@ export const useAddKeyboard = () => {
case keyboardValue.forward: case keyboardValue.forward:
keymaster(e, throttle(() => { chartEditStore.setForward(); return false }, 200)) keymaster(e, throttle(() => { chartEditStore.setForward(); return false }, 200))
break; break;
// 保存 ct+s
case keyboardValue.save:
keymaster(e, throttle(() => { useSyncIns.dataSyncUpdate(); return false }, 200))
break;
} }
} }
winKeyList.forEach((key: string) => { winKeyList.forEach((key: string) => {

View File

@@ -1,26 +1,14 @@
import { onUnmounted } from 'vue'; import { getUUID } from '@/utils'
import html2canvas from 'html2canvas'
import { getUUID, httpErrorHandle, fetchRouteParamsLocation, base64toFile } from '@/utils'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { EditCanvasTypeEnum, ChartEditStoreEnum, ProjectInfoEnum, ChartEditStorage } from '@/store/modules/chartEditStore/chartEditStore.d' import { ChartEditStoreEnum, ChartEditStorage } from '@/store/modules/chartEditStore/chartEditStore.d'
import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore' import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore'
import { useSystemStore } from '@/store/modules/systemStore/systemStore'
import { fetchChartComponent, fetchConfigComponent, createComponent } from '@/packages/index' import { fetchChartComponent, fetchConfigComponent, createComponent } from '@/packages/index'
import { CreateComponentType } from '@/packages/index.d' import { CreateComponentType } from '@/packages/index.d'
import { saveInterval } from '@/settings/designSetting'
import throttle from 'lodash/throttle'
// 接口状态
import { ResultEnum } from '@/enums/httpEnum'
// 接口
import { saveProjectApi, fetchProjectApi, uploadFile, updateProjectApi } from '@/api/path'
// 画布枚举
import { SyncEnum } from '@/enums/editPageEnum'
// 请求处理 // 请求处理
export const useSync = () => { export const useSync = () => {
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const chartHistoryStore = useChartHistoryStore() const chartHistoryStore = useChartHistoryStore()
const systemStore = useSystemStore()
/** /**
* * 组件动态注册 * * 组件动态注册
@@ -72,119 +60,7 @@ export const useSync = () => {
} }
} }
/**
* * 赋值全局数据
* @param projectData 项目数据
* @returns
*/
const updateStoreInfo = (projectData: {
id: string,
projectName: string,
indexImage: string,
remarks: string,
state: number
}) => {
const { projectName, remarks, indexImage, state } = projectData
// 名称
chartEditStore.setProjectInfo(ProjectInfoEnum.PROJECT_NAME, projectName)
// 描述
chartEditStore.setProjectInfo(ProjectInfoEnum.REMARKS, remarks)
// 缩略图
chartEditStore.setProjectInfo(ProjectInfoEnum.THUMBNAIL, indexImage)
// 发布
chartEditStore.setProjectInfo(ProjectInfoEnum.RELEASE, state === 1)
}
// * 数据获取
const dataSyncFetch = async () => {
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.START)
try {
const res = await fetchProjectApi({ projectId: fetchRouteParamsLocation() }) as unknown as MyResponseType
if (res.code === ResultEnum.SUCCESS) {
if (res.data) {
updateStoreInfo(res.data)
// 更新全局数据
await updateComponent(JSON.parse(res.data.content))
return
}
setTimeout(() => {
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.SUCCESS)
}, 1000)
return
}
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.FAILURE)
} catch (error) {
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.FAILURE)
httpErrorHandle()
}
}
// * 数据保存
const dataSyncUpdate = throttle(async () => {
if(!fetchRouteParamsLocation()) return
if(!systemStore.getFetchInfo.OSSUrl) {
window['$message'].error('数据保存失败,请刷新页面重试!')
return
}
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.START)
// 获取缩略图片
const range = document.querySelector('.go-edit-range') as HTMLElement
const ruler = document.getElementById('mb-ruler')
// 生成图片
const canvasImage: HTMLCanvasElement = await html2canvas(range, {
backgroundColor: null
})
// 上传预览图
let uploadParams = new FormData()
uploadParams.append('object', base64toFile(canvasImage.toDataURL(), `${fetchRouteParamsLocation()}_index_preview.png`))
const uploadRes = await uploadFile(systemStore.getFetchInfo.OSSUrl, uploadParams) as unknown as MyResponseType
// 保存预览图
if(uploadRes.code === ResultEnum.SUCCESS) {
await updateProjectApi({
id: fetchRouteParamsLocation(),
indexImage: uploadRes.data.objectContent.httpRequest.uri
})
}
// 保存数据
let params = new FormData()
params.append('projectId', fetchRouteParamsLocation())
params.append('content', JSON.stringify(chartEditStore.getStorageInfo || {}))
const res= await saveProjectApi(params) as unknown as MyResponseType
if (res.code === ResultEnum.SUCCESS) {
// 成功状态
setTimeout(() => {
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.SUCCESS)
}, 1000)
return
}
// 失败状态
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.FAILURE)
}, 3000)
// * 定时处理
const intervalDataSyncUpdate = () => {
// 定时获取数据
const syncTiming = setInterval(() => {
dataSyncUpdate()
}, saveInterval * 1000)
// 销毁
onUnmounted(() => {
clearInterval(syncTiming)
})
}
return { return {
updateComponent, updateComponent
updateStoreInfo,
dataSyncFetch,
dataSyncUpdate,
intervalDataSyncUpdate
} }
} }

View File

@@ -4,7 +4,7 @@
<img src="~@/assets/images/exception/403.svg" alt="" /> <img src="~@/assets/images/exception/403.svg" alt="" />
</div> </div>
<div class="text-center"> <div class="text-center">
<h1>抱歉你无权访问该页面</h1> <h1 class="text-base text-gray-500">抱歉你无权访问该页面</h1>
</div> </div>
<n-button type="primary" @click="goHome">回到首页</n-button> <n-button type="primary" @click="goHome">回到首页</n-button>
</div> </div>

View File

@@ -4,7 +4,7 @@
<img src="~@/assets/images/exception/404.svg" alt="" /> <img src="~@/assets/images/exception/404.svg" alt="" />
</div> </div>
<div class="text-center"> <div class="text-center">
<h1>抱歉你访问的页面不存在</h1> <h1 class="text-base text-gray-500">抱歉你访问的页面不存在</h1>
</div> </div>
<n-button type="primary" @click="goHome">回到首页</n-button> <n-button type="primary" @click="goHome">回到首页</n-button>
</div> </div>

View File

@@ -4,7 +4,7 @@
<img src="~@/assets/images/exception/500.svg" alt="" /> <img src="~@/assets/images/exception/500.svg" alt="" />
</div> </div>
<div class="text-center"> <div class="text-center">
<h1>抱歉服务器出错了</h1> <h1 class="text-base text-gray-500">抱歉服务器出错了</h1>
</div> </div>
<n-button type="primary" secondary @click="goHome">回到首页</n-button> <n-button type="primary" secondary @click="goHome">回到首页</n-button>
</div> </div>

View File

@@ -118,38 +118,45 @@
import { reactive, ref, onMounted } from 'vue' import { reactive, ref, onMounted } from 'vue'
import shuffle from 'lodash/shuffle' import shuffle from 'lodash/shuffle'
import { carouselInterval } from '@/settings/designSetting' import { carouselInterval } from '@/settings/designSetting'
import { useSystemStore } from '@/store/modules/systemStore/systemStore' import { useDesignStore } from '@/store/modules/designStore/designStore'
import { SystemStoreEnum } from '@/store/modules/systemStore/systemStore.d'
import { GoThemeSelect } from '@/components/GoThemeSelect' import { GoThemeSelect } from '@/components/GoThemeSelect'
import { GoLangSelect } from '@/components/GoLangSelect' import { GoLangSelect } from '@/components/GoLangSelect'
import { LayoutHeader } from '@/layout/components/LayoutHeader' import { LayoutHeader } from '@/layout/components/LayoutHeader'
import { LayoutFooter } from '@/layout/components/LayoutFooter' import { LayoutFooter } from '@/layout/components/LayoutFooter'
import { PageEnum } from '@/enums/pageEnum' import { PageEnum } from '@/enums/pageEnum'
import { StorageEnum } from '@/enums/storageEnum'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { routerTurnByName } from '@/utils' import { StorageEnum } from '@/enums/storageEnum'
import { loginApi } from '@/api/path' import { routerTurnByName, cryptoEncode, setLocalStorage } from '@/utils'
const { GO_LOGIN_INFO_STORE } = StorageEnum
const { PersonOutlineIcon, LockClosedOutlineIcon } = icon.ionicons5
interface FormState { interface FormState {
username: string username: string
password: string password: string
} }
const { GO_SYSTEM_STORE } = StorageEnum
const { PersonOutlineIcon, LockClosedOutlineIcon } = icon.ionicons5
const formRef = ref() const formRef = ref()
const loading = ref(false) const loading = ref(false)
const autoLogin = ref(true) const autoLogin = ref(true)
const show = ref(false) const show = ref(false)
const showBg = ref(false) const showBg = ref(false)
const systemStore = useSystemStore() const designStore = useDesignStore()
const t = window['$t'] const t = window['$t']
onMounted(() => {
setTimeout(() => {
show.value = true
}, 300)
setTimeout(() => {
showBg.value = true
}, 100)
})
const formInline = reactive({ const formInline = reactive({
username: 'admin', username: 'admin',
password: 'admin', password: '123456',
}) })
const rules = { const rules = {
@@ -189,55 +196,38 @@ const getImageUrl = (name: string, folder: string) => {
return new URL(`../../assets/images/${folder}/${name}.png`, import.meta.url).href return new URL(`../../assets/images/${folder}/${name}.png`, import.meta.url).href
} }
// 打乱图片顺序 // 打乱
const shuffleHandle = () => { const shuffleHandle = () => {
shuffleTimiing.value = setInterval(() => { shuffleTimiing.value = setInterval(() => {
bgList.value = shuffle(bgList.value) bgList.value = shuffle(bgList.value)
}, carouselInterval) }, carouselInterval)
} }
// 登录 // 点击事件
const handleSubmit = async (e: Event) => { const handleSubmit = (e: Event) => {
e.preventDefault() e.preventDefault()
formRef.value.validate(async (errors: any) => { formRef.value.validate(async (errors: any) => {
if (!errors) { if (!errors) {
const { username, password } = formInline const { username, password } = formInline
loading.value = true loading.value = true
// 提交请求 setLocalStorage(
const res = await loginApi({ GO_LOGIN_INFO_STORE,
username, cryptoEncode(
password JSON.stringify({
}) as unknown as MyResponseType username,
if(res.data) { password,
const { tokenValue } = res.data.token })
const { nickname, username, id } = res.data.userinfo )
)
// 存储到 pinia window['$message'].success(`${t('login.login_success')}!`)
systemStore.setItem(SystemStoreEnum.USER_INFO, { routerTurnByName(PageEnum.BASE_HOME_NAME, true)
userToken: tokenValue,
userId: id,
userName: username,
nickName: nickname,
})
window['$message'].success(t('login.login_success'))
routerTurnByName(PageEnum.BASE_HOME_NAME, true)
}
} else { } else {
window['$message'].error(t('login.login_message')) window['$message'].error(`${t('login.login_message')}!`)
} }
}) })
} }
onMounted(() => { onMounted(() => {
setTimeout(() => {
show.value = true
}, 300)
setTimeout(() => {
showBg.value = true
}, 100)
shuffleHandle() shuffleHandle()
}) })
</script> </script>

View File

@@ -8,7 +8,7 @@ export const useComInstall = (localStorageInfo: ChartEditStorageType) => {
// 注册组件(一开始无法获取window['$vue']) // 注册组件(一开始无法获取window['$vue'])
const intervalTiming = setInterval(() => { const intervalTiming = setInterval(() => {
if (window['$vue']?.component) { if (window['$vue'].component) {
clearInterval(intervalTiming) clearInterval(intervalTiming)
localStorageInfo.componentList.forEach(async (e: CreateComponentType) => { localStorageInfo.componentList.forEach(async (e: CreateComponentType) => {
if (!window['$vue'].component(e.chartConfig.chartKey)) { if (!window['$vue'].component(e.chartConfig.chartKey)) {

View File

@@ -4,7 +4,6 @@ import type { ChartEditStorageType } from '../index.d'
import { PreviewScaleEnum } from '@/enums/styleEnum' import { PreviewScaleEnum } from '@/enums/styleEnum'
export const useScale = (localStorageInfo: ChartEditStorageType) => { export const useScale = (localStorageInfo: ChartEditStorageType) => {
const entityRef = ref() const entityRef = ref()
const previewRef = ref() const previewRef = ref()
const width = ref(localStorageInfo.editCanvasConfig.width) const width = ref(localStorageInfo.editCanvasConfig.width)

View File

@@ -1,6 +1,5 @@
import { ChartEditStorage } from '@/store/modules/chartEditStore/chartEditStore.d' import { ChartEditStorage } from '@/store/modules/chartEditStore/chartEditStore.d'
export interface ChartEditStorageType extends ChartEditStorage { export interface ChartEditStorageType extends ChartEditStorage {
id: string, id: string
isRelease?: boolean
} }

View File

@@ -1,9 +1,100 @@
<template> <template>
<suspense> <div
<suspense-index></suspense-index> :class="`go-preview ${localStorageInfo.editCanvasConfig.previewScaleType}`"
</suspense> >
<template v-if="showEntity">
<!-- 实体区域 -->
<div ref="entityRef" class="go-preview-entity">
<!-- 缩放层 -->
<div ref="previewRef" class="go-preview-scale">
<!-- 展示层 -->
<div :style="previewRefStyle" v-if="show">
<!-- 渲染层 -->
<preview-render-list
:localStorageInfo="localStorageInfo"
></preview-render-list>
</div>
</div>
</div>
</template>
<template v-else>
<!-- 缩放层 -->
<div ref="previewRef" class="go-preview-scale">
<!-- 展示层 -->
<div :style="previewRefStyle" v-if="show">
<!-- 渲染层 -->
<preview-render-list
:localStorageInfo="localStorageInfo"
></preview-render-list>
</div>
</div>
</template>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import suspenseIndex from './suspenseIndex.vue' import { computed } from 'vue'
import { PreviewRenderList } from './components/PreviewRenderList'
import { getFilterStyle } from '@/utils'
import { getEditCanvasConfigStyle, getSessionStorageInfo } from './utils'
import { useComInstall } from './hooks/useComInstall.hook'
import { useScale } from './hooks/useScale.hook'
import { useStore } from './hooks/useStore.hook'
import { PreviewScaleEnum } from '@/enums/styleEnum'
import type { ChartEditStorageType } from './index.d'
const localStorageInfo: ChartEditStorageType =
getSessionStorageInfo() as ChartEditStorageType
const previewRefStyle = computed(() => {
return {
...getEditCanvasConfigStyle(localStorageInfo.editCanvasConfig),
...getFilterStyle(localStorageInfo.editCanvasConfig),
}
})
const showEntity = computed(() => {
const type = localStorageInfo.editCanvasConfig.previewScaleType
return (
type === PreviewScaleEnum.SCROLL_Y || type === PreviewScaleEnum.SCROLL_X
)
})
useStore(localStorageInfo)
const { entityRef, previewRef } = useScale(localStorageInfo)
const { show } = useComInstall(localStorageInfo)
</script> </script>
<style lang="scss" scoped>
@include go('preview') {
position: relative;
height: 100vh;
width: 100vw;
@include background-image('background-image');
&.fit,
&.full {
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
.go-preview-scale {
transform-origin: center center;
}
}
&.scrollY {
overflow-x: hidden;
.go-preview-scale {
transform-origin: left top;
}
}
&.scrollX {
overflow-y: hidden;
.go-preview-scale {
transform-origin: left top;
}
}
.go-preview-entity {
overflow: hidden;
}
}
</style>

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