Compare commits

..

132 Commits

Author SHA1 Message Date
奔跑的面条 9004a55b8c Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch-dev 2022-09-22 14:15:18 +08:00
奔跑的面条 c349e6384e Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch-dev 2022-09-21 19:56:56 +08:00
奔跑的面条 4e298efaa0 Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch-dev 2022-09-19 20:46:53 +08:00
奔跑的面条 c7cbb9b72a Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch-dev 2022-09-19 20:14:40 +08:00
奔跑的面条 bd5e06350d perf: 优化滤镜模糊问题,默认不开启 2022-09-19 17:51:12 +08:00
奔跑的面条 c311dd08e3 fix: 修改请求接口 token 名称 2022-09-18 17:45:59 +08:00
奔跑的面条 504a1f4703 build: 升级版本到2.0.7 2022-09-18 17:23:16 +08:00
奔跑的面条 a67cc9f876
!47 修复在编辑项目之前加载数据异常,自动保存导致项目数据清空问题。
Merge pull request !47 from 秋名山路霸/master-fetch
2022-09-18 07:53:47 +00:00
秋名山路霸 e0d2e8031d fix: 修复在编辑项目之前加载数据异常,自动保存导致项目数据清空问题。 2022-09-18 11:56:26 +08:00
奔跑的面条 bcec26374f build: 升级依赖,锁定TS 版本 2022-09-12 01:20:27 +08:00
奔跑的面条 a5981a4387 feat: 升级版本到 2.0.6 2022-09-12 00:49:22 +08:00
奔跑的面条 0d26dce512 fix: 合并1.0.9 2022-09-12 00:31:52 +08:00
奔跑的面条 6359ec15b3 feat: 新增移动撤回 2022-08-30 19:14:52 +08:00
奔跑的面条 32ca138e6a build: 升级版本到 2.0.5 2022-08-30 15:04:29 +08:00
奔跑的面条 171e0aa408 perf: 完善首页预览功能 2022-08-30 15:03:25 +08:00
奔跑的面条 52009a415b Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-08-30 12:19:57 +08:00
奔跑的面条 da809f13b3 fix: 尝试解决背景图片无法截图的问题 2022-08-29 21:35:51 +08:00
奔跑的面条 801aa1f82f
update LICENSE.
Signed-off-by: 奔跑的面条 <1262327911@qq.com>
2022-08-29 10:13:37 +00:00
奔跑的面条 72c26b71f3 perf: 优化参考线的展示方式 2022-08-21 14:52:30 +08:00
奔跑的面条 3b8181ae10 Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-08-21 14:49:32 +08:00
奔跑的面条 c2a9642393 Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-08-19 14:46:49 +08:00
奔跑的面条 3c04f81147 fix: 补充代码合并丢失的枚举 2022-08-19 11:43:23 +08:00
奔跑的面条 ec28ccf09c perf: 优化500错误页重定向 2022-08-19 11:34:38 +08:00
奔跑的面条 fa34300401 feat: 合并多选功能,解决冲突,升级版本到2.0.4 2022-08-19 10:44:44 +08:00
奔跑的面条 f506ccd2e4 Merge branch 'dev' into master-fetch 2022-08-05 08:42:22 +08:00
奔跑的面条 f379f7652c Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-07-26 16:12:31 +08:00
奔跑的面条 e8760c81ac Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-07-21 11:46:54 +08:00
奔跑的面条 a9bb294f60 feat: 合并 1.0.6 版本代码 2022-07-21 11:03:04 +08:00
奔跑的面条 0b5f35752b Merge branch 'dev' into master-fetch 2022-07-08 18:12:33 +08:00
奔跑的面条 7322817cb5 fix: 合并编辑功能的修改 2022-07-07 19:54:53 +08:00
奔跑的面条 437ad1bd9f Merge branch 'dev' into master-fetch 2022-07-07 13:14:53 +08:00
奔跑的面条 cd20e68480 fix: 补充合并代码丢失的icon图标 2022-07-06 22:12:24 +08:00
奔跑的面条 35968475b9 build: 合并dev 1.0.5 版本, 修改fetch分支为2.0.2 2022-07-06 21:58:28 +08:00
奔跑的面条 78eb3ccc3f Merge branch 'dev' into master-fetch 2022-06-27 21:42:24 +08:00
奔跑的面条 7cccbabca4 Merge branch 'dev' into master-fetch 2022-06-27 21:38:27 +08:00
奔跑的面条 cdccfa3e12 fix: 修改合并冲突错误的代码 2022-06-27 20:42:17 +08:00
奔跑的面条 e8c5455748 perf: 合并dev分支,解决组件无法更新数据的问题 2022-06-27 20:37:26 +08:00
奔跑的面条 8e40416e72 style: 去除多余代码 2022-06-24 11:44:26 +08:00
奔跑的面条 d058361e01 docs: 修改文档 2022-06-23 09:53:01 +08:00
奔跑的面条 2491f7ff57 feat: 新增保存按钮 2022-06-22 18:50:15 +08:00
奔跑的面条 c5908f6d92 build: 升级依赖 2022-06-20 15:13:50 +08:00
奔跑的面条 7e64d30258 build: 合并dev分支 2022-06-20 15:11:13 +08:00
奔跑的面条 437818abce feat: 新增排名列表字体大小控制功能 2022-06-17 14:13:14 +08:00
奔跑的面条 5197eb9292 build: 修改版本到 2.0.1 2022-06-16 10:48:42 +08:00
奔跑的面条 3cf01f0c51 fix: 解决打包之后无法加载页面的问题 2022-06-15 17:54:49 +08:00
奔跑的面条 db815661e8 feat: 新增 preview 模式,修改打包后路径指向 2022-06-15 17:48:16 +08:00
奔跑的面条 6edbb7c8a7 feat: 新增commitlint 2022-06-15 17:18:39 +08:00
奔跑的面条 799c445c4b docs: 更新文档 2022-06-15 17:15:29 +08:00
奔跑的面条 70fbf5de3e doc: 文档更新 2022-06-15 17:14:27 +08:00
奔跑的面条 73c7697443 feat: 新增渐变文本组件 2022-06-15 16:20:25 +08:00
奔跑的面条 8092edab81 build:升级依赖 2022-06-15 16:11:58 +08:00
奔跑的面条 ef4f45ddb0 fix: 解决npm,yarn 安装依赖报错的问题 2022-06-15 16:08:59 +08:00
奔跑的面条 3e969e4ae5 fix: 补充丢失的图片 2022-06-14 12:46:07 +08:00
奔跑的面条 bcd1dfd7bd feat:新增数字滚动组件动态获取数据功能 2022-06-14 12:31:46 +08:00
奔跑的面条 c1dfd78d18 feat: 新增表格滚动组件 2022-06-14 12:31:20 +08:00
奔跑的面条 39aa1645c9 fix:解决边框04展示不全的bug 2022-06-14 12:30:29 +08:00
奔跑的面条 d3afea8c5e feat:新增水球图设置项 2022-06-13 17:29:39 +08:00
奔跑的面条 2499cbdd44 fix: 修改列表页展示问题 2022-06-13 14:28:58 +08:00
奔跑的面条 3cc3714e94 feat: 新增环形图,新增NaiveUI-进度组件 2022-06-13 13:18:30 +08:00
奔跑的面条 be0aa6d099 feat: 新增进度条组件 2022-06-12 18:47:47 +08:00
奔跑的面条 34d27c1004 fix: 解决缩放比例展示不全的问题 2022-06-12 18:45:31 +08:00
奔跑的面条 85f3b4e9e3 chore: 优化拖拽锚点 2022-06-11 15:15:38 +08:00
奔跑的面条 0fb9a79df9 fix:修改请求地址为null时引起的异常bug 2022-06-11 14:37:22 +08:00
奔跑的面条 6b805fbdfb chore: 优化发布弹窗 2022-06-11 14:23:16 +08:00
奔跑的面条 abe76aeb59 build: 依赖基本升级 2022-06-11 14:15:28 +08:00
奔跑的面条 5f49bc1aa8 fix: 修改双折线图X轴无法变更的问题 2022-06-11 14:13:32 +08:00
奔跑的面条 71807be01d fix: 解决项目列表信息栏会换行的问题 2022-06-09 08:49:07 +08:00
奔跑的面条 e74f796203 fix:解决截图有白边的问题 2022-06-09 08:48:28 +08:00
奔跑的面条 041d7da9d8 fix: 修改复制失败的提示类型错误的问题 2022-06-06 10:43:14 +08:00
奔跑的面条 a99d949b29 chore: 新增路由白名单 2022-06-06 10:42:55 +08:00
奔跑的面条 c2a9e2a469 chore:优化搜索结果列表UI 2022-06-05 11:41:57 +08:00
奔跑的面条 80b05db764 chore:优化 dialog 的全局封装代码 2022-06-04 16:25:37 +08:00
奔跑的面条 340cab11b7 build:修改 fetch 版本号 2022-06-04 15:44:26 +08:00
奔跑的面条 712233c215 chore: 优化页面 UI 2022-06-03 20:21:35 +08:00
奔跑的面条 b9b915e913 fix: 修改自动复制粘贴的问题 2022-06-03 18:53:37 +08:00
奔跑的面条 044143571f fix: 新增发布页面处理 2022-06-03 14:48:58 +08:00
奔跑的面条 06fe805736 chore: 优化了标题展示和大小样式 2022-06-03 11:19:29 +08:00
奔跑的面条 077881e499 type: 修改类型错误 2022-06-01 23:00:28 +08:00
奔跑的面条 f655a57e61 fix:处理列表页标题过长的展示问题 2022-06-01 22:58:22 +08:00
奔跑的面条 0b04bf4929 type: 定义全局返回值类型 2022-06-01 22:41:11 +08:00
奔跑的面条 2fcd3b1132 build: 修改版本号 0.0.9 2022-06-01 22:28:46 +08:00
奔跑的面条 568fd6c105 chore:优化了路由写法,修改了错误页面的展示,新增未发布提示页面 2022-06-01 22:20:05 +08:00
奔跑的面条 221351ec11 feat: 新增动态预览功能 2022-06-01 22:19:03 +08:00
奔跑的面条 6ab34a0996 fix: 修改组件注册会报错的问题 2022-06-01 22:14:33 +08:00
奔跑的面条 6b551ae68b build:升级依赖包 2022-06-01 22:13:49 +08:00
奔跑的面条 2917cde2e5 fix: 解决列表图片展示缓存问题 2022-06-01 20:16:38 +08:00
奔跑的面条 294a4a6cdf fix: 解决获取数据,但是配置模块不完整的问题 2022-06-01 19:01:05 +08:00
奔跑的面条 1e678b7492 build: 修改请求地址 2022-05-31 11:19:17 +08:00
奔跑的面条 2ceca7287f fix: 修改oss接口不会动态更改的问题 2022-05-31 11:18:34 +08:00
奔跑的面条 7f4dd5295b chore:修改请求地址 2022-05-29 16:04:07 +08:00
奔跑的面条 ffd628fa82 docs: 修改文档说明 2022-05-29 15:23:04 +08:00
奔跑的面条 d639e445eb feat: 新增背景图文件上传保存 2022-05-29 14:54:35 +08:00
奔跑的面条 b263681dda chore: 去除生成预览图时的标尺 2022-05-28 17:58:07 +08:00
奔跑的面条 e16413b570 feat: 新增首页预览图展示 2022-05-28 16:39:27 +08:00
奔跑的面条 63b5186c3e Merge branch 'dev' into master-fetch 2022-05-28 15:58:06 +08:00
奔跑的面条 ba20316761 fix: 修改自动保存预览图无法存储的问题 2022-05-28 15:46:07 +08:00
奔跑的面条 437dd1c411 branch: 合并锚点样式修改 2022-05-28 12:46:32 +08:00
奔跑的面条 d0d5f5b77d feat: 保存预览图 2022-05-28 11:50:17 +08:00
奔跑的面条 efd9228cc9 fix: 解决打包后无法发送请求的bug 2022-05-28 00:32:32 +08:00
奔跑的面条 ebd6132385 feat: 新增上传文件接口 2022-05-27 20:09:48 +08:00
奔跑的面条 9098443c83 chore: 修改项目信息结构 2022-05-27 11:49:25 +08:00
奔跑的面条 423890a4df feat: 新增项目信息修改功能 2022-05-26 01:01:59 +08:00
奔跑的面条 fb2edeb7d2 fix: 修改导入组件id会重复的问题 2022-05-25 23:00:36 +08:00
奔跑的面条 b861587f01 feat: 新增快捷键展示 2022-05-24 18:17:41 +08:00
奔跑的面条 c3738fab45 faet: 新增保存快捷键 2022-05-24 18:16:33 +08:00
奔跑的面条 24fba75f28 feat: 新增自动同步功能 2022-05-24 17:42:49 +08:00
奔跑的面条 00b6b63e1e feat: 新增数据保存接口 2022-05-24 15:05:51 +08:00
奔跑的面条 5dab8fa7d9 Merge branch 'dev' into master-fetch 2022-05-24 12:28:14 +08:00
奔跑的面条 9a5d71fb5c feat: 新增获取项目数据功能,新增同步数据功能 2022-05-23 23:50:35 +08:00
奔跑的面条 c930efba0c Merge branch 'dev' into master-fetch 2022-05-23 16:06:13 +08:00
奔跑的面条 ff7c820b1f Merge branch 'dev' into master-fetch 2022-05-22 23:27:39 +08:00
奔跑的面条 4252725d9d feat: 新增获取项目数据接口 2022-05-22 22:11:56 +08:00
奔跑的面条 deeb3a472c feat:新增发布和取消发布接口 2022-05-22 16:38:22 +08:00
奔跑的面条 763173de44 style: 调整代码格式,去除多余代码 2022-05-22 15:39:30 +08:00
奔跑的面条 f46e6ad8c8 feat: 新增删除接口 2022-05-22 15:25:07 +08:00
奔跑的面条 09b31547e1 feat: 新增首页列表接口 2022-05-22 15:06:45 +08:00
奔跑的面条 dee2ff8dee Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-05-22 14:09:38 +08:00
奔跑的面条 7f315b95ce feat: 新增项目列表接口 2022-05-22 14:05:57 +08:00
奔跑的面条 093e7d1edb Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-05-22 13:36:30 +08:00
奔跑的面条 a4e18f8893 fix: 修改新建项目id错误问题 2022-05-21 21:04:10 +08:00
奔跑的面条 c754a36ee5 fix: 修改i18n错误提示 2022-05-21 19:46:44 +08:00
奔跑的面条 a07d4daed5 chore: 修改提示内容 2022-05-21 18:04:52 +08:00
奔跑的面条 39023832e3 feat: 新增创建接口,修改i8n部分内容 2022-05-21 18:03:15 +08:00
奔跑的面条 5b8dda60bd feat: 新增退出登录接口,新增全局接口封装,修改登录接口内容 2022-05-21 17:31:01 +08:00
奔跑的面条 f7ade54e93 Merge branch 'master' of https://gitee.com/MTrun/go-view into master-fetch 2022-05-21 14:01:53 +08:00
奔跑的面条 4ae7e9dd2e Merge branch 'dev' into master-fetch 2022-05-21 13:44:25 +08:00
奔跑的面条 7e237b508a feat: 新增登录接口请求 2022-05-20 16:12:27 +08:00
奔跑的面条 28bb82e579 fix: 修改plop的问题 2022-05-20 16:12:09 +08:00
奔跑的面条 bcabcb0831 Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch 2022-05-20 10:25:46 +08:00
奔跑的面条 c24b03f3da
update README.md. 2022-05-19 01:56:30 +00:00
奔跑的面条 63dd03e755
update README.md. 2022-05-16 10:38:39 +00:00
奔跑的面条 f3f1f57b01
update README.md. 2022-05-16 04:23:10 +00:00
87 changed files with 1770 additions and 768 deletions

12
.env
View File

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

View File

@ -1,51 +0,0 @@
version: '1.0'
name: branch-pipeline
displayName: BranchPipeline
stages:
- stage:
name: compile
displayName: 编译
steps:
- step: build@nodejs
name: build_nodejs
displayName: Nodejs 构建
# 支持8.16.2、10.17.0、12.16.1、14.16.0、15.12.0五个版本
nodeVersion: 14.16.0
# 构建命令:安装依赖 -> 清除上次打包产物残留 -> 执行构建 【请根据项目实际产出进行填写】
commands:
- npm install && rm -rf ./dist && npm run build
# 非必填字段开启后表示将构建产物暂存但不会上传到制品库中7天后自动清除
artifacts:
# 构建产物名字作为产物的唯一标识可向下传递支持自定义默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
- name: BUILD_ARTIFACT
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径
path:
- ./dist
- step: publish@general_artifacts
name: publish_general_artifacts
displayName: 上传制品
# 上游构建任务定义的产物名默认BUILD_ARTIFACT
dependArtifact: BUILD_ARTIFACT
# 上传到制品库时的制品命名默认output
artifactName: output
dependsOn: build_nodejs
- stage:
name: release
displayName: 发布
steps:
- step: publish@release_artifacts
name: publish_release_artifacts
displayName: '发布'
# 上游上传制品任务的产出
dependArtifact: output
# 发布制品版本号
version: '1.0.0.0'
# 是否开启版本号自增,默认开启
autoIncrement: true
triggers:
push:
branches:
exclude:
- master
include:
- .*

View File

@ -1,49 +0,0 @@
version: '1.0'
name: master-pipeline
displayName: MasterPipeline
stages:
- stage:
name: compile
displayName: 编译
steps:
- step: build@nodejs
name: build_nodejs
displayName: Nodejs 构建
# 支持8.16.2、10.17.0、12.16.1、14.16.0、15.12.0五个版本
nodeVersion: 14.16.0
# 构建命令:安装依赖 -> 清除上次打包产物残留 -> 执行构建 【请根据项目实际产出进行填写】
commands:
- npm install && rm -rf ./dist && npm run build
# 非必填字段开启后表示将构建产物暂存但不会上传到制品库中7天后自动清除
artifacts:
# 构建产物名字作为产物的唯一标识可向下传递支持自定义默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
- name: BUILD_ARTIFACT
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径
path:
- ./dist
- step: publish@general_artifacts
name: publish_general_artifacts
displayName: 上传制品
# 上游构建任务定义的产物名默认BUILD_ARTIFACT
dependArtifact: BUILD_ARTIFACT
# 上传到制品库时的制品命名默认output
artifactName: output
dependsOn: build_nodejs
- stage:
name: release
displayName: 发布
steps:
- step: publish@release_artifacts
name: publish_release_artifacts
displayName: '发布'
# 上游上传制品任务的产出
dependArtifact: output
# 发布制品版本号
version: '1.0.0.0'
# 是否开启版本号自增,默认开启
autoIncrement: true
triggers:
push:
branches:
include:
- master

View File

@ -1,36 +0,0 @@
version: '1.0'
name: pr-pipeline
displayName: PRPipeline
stages:
- stage:
name: compile
displayName: 编译
steps:
- step: build@nodejs
name: build_nodejs
displayName: Nodejs 构建
# 支持8.16.2、10.17.0、12.16.1、14.16.0、15.12.0五个版本
nodeVersion: 14.16.0
# 构建命令:安装依赖 -> 清除上次打包产物残留 -> 执行构建 【请根据项目实际产出进行填写】
commands:
- npm install && rm -rf ./dist && npm run build
# 非必填字段开启后表示将构建产物暂存但不会上传到制品库中7天后自动清除
artifacts:
# 构建产物名字作为产物的唯一标识可向下传递支持自定义默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
- name: BUILD_ARTIFACT
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径
path:
- ./dist
- step: publish@general_artifacts
name: publish_general_artifacts
displayName: 上传制品
# 上游构建任务定义的产物名默认BUILD_ARTIFACT
dependArtifact: BUILD_ARTIFACT
# 上传到制品库时的制品命名默认output
artifactName: output
dependsOn: build_nodejs
triggers:
pr:
branches:
include:
- master

195
README.md
View File

@ -2,21 +2,17 @@
![logo](readme/logo-t-y.png) ![logo](readme/logo-t-y.png)
GoView 是一个高效的拖拽式低代码数据可视化开发平台,将图表或页面元素封装为基础组件,无需编写代码即可制作数据大屏,减少心智负担。 **`master-fetch` 分支是带有后端接口请求的分支**
### 😶 纯 **前端** 分支: **`master`** **后端项目地址:[https://gitee.com/MTrun/go-view-serve](https://gitee.com/MTrun/go-view-serve)**
### 👻 携带 **后端** 请求分支: **`master-fetch`** **接口说明地址:[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)**
### 📚 GoView **文档** 地址:[http://www.mtruning.club:81/](http://www.mtruning.club:81/) ## 使用
项目纯前端-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)
### 🤯 后端项目 ### 🤯 后端项目
@ -24,124 +20,103 @@ GoView 是一个高效的拖拽式低代码数据可视化开发平台,将图
接口说明地址:[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) 接口说明地址:[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/go-view-canvas.png)
请求配置:
![项目截图](readme/go-view-fetch.png)
数据过滤:
![项目截图](readme/go-view-filter.png)
主题色:
![项目截图](readme/go-view-color.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
#建议使用 nrm 切换到淘宝源 https://registry.npmmirror.com/ # port
#pnpm VITE_DEV_PORT = '8080'
pnpm install
#yarn # development path
yarn install VITE_DEV_PATH = 'http://127.0.0.1:8080'
#npm # production path
npm install VITE_PRO_PATH = 'http://127.0.0.1:8080'
``` ```
## 启动 公共前缀修改:`src\settings\httpSetting.ts`
```shell ```shell
#pnpm // 请求前缀
pnpm dev export const axiosPre = '/api/goview'
# npm
npm run dev
#yarn
yarn dev
#Makefile
make dev
``` ```
## 编译 接口封装:`src\api\http.ts`
```shell ```ts
#pnpm import axiosInstance from './axios'
pnpm run build import { RequestHttpEnum, ContentTypeEnum } from '@/enums/httpEnum'
# npm export const get = (url: string, params?: object) => {
npm run build return axiosInstance({
url: url,
method: RequestHttpEnum.GET,
params: params,
})
}
#yarn export const post = (url: string, data?: object, headersType?: string) => {
yarn run build return axiosInstance({
url: url,
method: RequestHttpEnum.POST,
data: data,
headers: {
'Content-Type': headersType || ContentTypeEnum.JSON
}
})
}
#Makefile export const put = (url: string, data?: object, headersType?: string) => {
make dist return axiosInstance({
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

@ -1,9 +0,0 @@
/**
* 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": "1.1.0", "version": "2.0.7",
"scripts": { "scripts": {
"dev": "vite --host", "dev": "vite --host",
"build": "vue-tsc --noEmit && vite build", "build": "vue-tsc --noEmit && vite build",
@ -28,7 +28,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.9", "vue-i18n": "9.1.10",
"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",

View File

@ -53,7 +53,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.9 vue-i18n: 9.1.10
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
@ -80,7 +80,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.9_vue@3.2.37 vue-i18n: 9.1.10_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
@ -651,60 +651,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.9: /@intlify/core-base/9.1.10:
resolution: {integrity: sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==} resolution: {integrity: sha512-So9CNUavB/IsZ+zBmk2Cv6McQp6vc2wbGi1S0XQmJ8Vz+UFcNn9MFXAe9gY67PreIHrbLsLxDD0cwo1qsxM1Nw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
dependencies: dependencies:
'@intlify/devtools-if': 9.1.9 '@intlify/devtools-if': 9.1.10
'@intlify/message-compiler': 9.1.9 '@intlify/message-compiler': 9.1.10
'@intlify/message-resolver': 9.1.9 '@intlify/message-resolver': 9.1.10
'@intlify/runtime': 9.1.9 '@intlify/runtime': 9.1.10
'@intlify/shared': 9.1.9 '@intlify/shared': 9.1.10
'@intlify/vue-devtools': 9.1.9 '@intlify/vue-devtools': 9.1.10
dev: false dev: false
/@intlify/devtools-if/9.1.9: /@intlify/devtools-if/9.1.10:
resolution: {integrity: sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==} resolution: {integrity: sha512-SHaKoYu6sog3+Q8js1y3oXLywuogbH1sKuc7NSYkN3GElvXSBaMoCzW+we0ZSFqj/6c7vTNLg9nQ6rxhKqYwnQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
dependencies: dependencies:
'@intlify/shared': 9.1.9 '@intlify/shared': 9.1.10
dev: false dev: false
/@intlify/message-compiler/9.1.9: /@intlify/message-compiler/9.1.10:
resolution: {integrity: sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==} resolution: {integrity: sha512-+JiJpXff/XTb0EadYwdxOyRTB0hXNd4n1HaJ/a4yuV960uRmPXaklJsedW0LNdcptd/hYUZtCkI7Lc9J5C1gxg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
dependencies: dependencies:
'@intlify/message-resolver': 9.1.9 '@intlify/message-resolver': 9.1.10
'@intlify/shared': 9.1.9 '@intlify/shared': 9.1.10
source-map: 0.6.1 source-map: 0.6.1
dev: false dev: false
/@intlify/message-resolver/9.1.9: /@intlify/message-resolver/9.1.10:
resolution: {integrity: sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA==} resolution: {integrity: sha512-5YixMG/M05m0cn9+gOzd4EZQTFRUu8RGhzxJbR1DWN21x/Z3bJ8QpDYj6hC4FwBj5uKsRfKpJQ3Xqg98KWoA+w==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
dev: false dev: false
/@intlify/runtime/9.1.9: /@intlify/runtime/9.1.10:
resolution: {integrity: sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==} resolution: {integrity: sha512-7QsuByNzpe3Gfmhwq6hzgXcMPpxz8Zxb/XFI6s9lQdPLPe5Lgw4U1ovRPZTOs6Y2hwitR3j/HD8BJNGWpJnOFA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
dependencies: dependencies:
'@intlify/message-compiler': 9.1.9 '@intlify/message-compiler': 9.1.10
'@intlify/message-resolver': 9.1.9 '@intlify/message-resolver': 9.1.10
'@intlify/shared': 9.1.9 '@intlify/shared': 9.1.10
dev: false dev: false
/@intlify/shared/9.1.9: /@intlify/shared/9.1.10:
resolution: {integrity: sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw==} resolution: {integrity: sha512-Om54xJeo1Vw+K1+wHYyXngE8cAbrxZHpWjYzMR9wCkqbhGtRV5VLhVc214Ze2YatPrWlS2WSMOWXR8JktX/IgA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
dev: false dev: false
/@intlify/vue-devtools/9.1.9: /@intlify/vue-devtools/9.1.10:
resolution: {integrity: sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==} resolution: {integrity: sha512-5l3qYARVbkWAkagLu1XbDUWRJSL8br1Dj60wgMaKB0+HswVsrR6LloYZTg7ozyvM621V6+zsmwzbQxbVQyrytQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
dependencies: dependencies:
'@intlify/message-resolver': 9.1.9 '@intlify/message-resolver': 9.1.10
'@intlify/runtime': 9.1.9 '@intlify/runtime': 9.1.10
'@intlify/shared': 9.1.9 '@intlify/shared': 9.1.10
dev: false dev: false
/@jridgewell/gen-mapping/0.1.1: /@jridgewell/gen-mapping/0.1.1:
@ -5374,15 +5374,15 @@ packages:
- supports-color - supports-color
dev: true dev: true
/vue-i18n/9.1.9_vue@3.2.37: /vue-i18n/9.1.10_vue@3.2.37:
resolution: {integrity: sha512-JeRdNVxS2OGp1E+pye5XB6+M6BBkHwAv9C80Q7+kzoMdUDGRna06tjC0vCB/jDX9aWrl5swxOMFcyAr7or8XTA==} resolution: {integrity: sha512-jpr7gV5KPk4n+sSPdpZT8Qx3XzTcNDWffRlHV/cT2NUyEf+sEgTTmLvnBAibjOFJ0zsUyZlVTAWH5DDnYep+1g==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
peerDependencies: peerDependencies:
vue: ^3.0.0 vue: ^3.0.0
dependencies: dependencies:
'@intlify/core-base': 9.1.9 '@intlify/core-base': 9.1.10
'@intlify/shared': 9.1.9 '@intlify/shared': 9.1.10
'@intlify/vue-devtools': 9.1.9 '@intlify/vue-devtools': 9.1.10
'@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 { useDarkThemeHook, useThemeOverridesHook, useCode } from '@/hooks' import { useSystemInit, useDarkThemeHook, useThemeOverridesHook, useCode } from '@/hooks'
// //
const darkTheme = useDarkThemeHook() const darkTheme = useDarkThemeHook()
@ -28,4 +28,7 @@ const overridesTheme = useThemeOverridesHook()
// //
const hljsTheme = useCode() const hljsTheme = useCode()
//
useSystemInit()
</script> </script>

14
src/api/axios.config.ts Normal file
View File

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

View File

@ -1,19 +1,38 @@
import axios, { AxiosResponse, AxiosRequestConfig } from 'axios' import axios, { AxiosResponse, AxiosRequestConfig } from 'axios'
import { ResultEnum } from "@/enums/httpEnum" import { ResultEnum, RequestHttpHeaderEnum } from "@/enums/httpEnum"
import { ErrorPageNameMap } from "@/enums/pageEnum" import { PageEnum, ErrorPageNameMap } from "@/enums/pageEnum"
import { redirectErrorPage } from '@/utils' import { StorageEnum } from '@/enums/storageEnum'
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.DEV ? import.meta.env.VITE_DEV_PATH : import.meta.env.VITE_PRO_PATH, baseURL: `${import.meta.env.PROD ? import.meta.env.VITE_PRO_PATH : ''}${axiosPre}`,
timeout: ResultEnum.TIMEOUT, timeout: ResultEnum.TIMEOUT,
}) })
axiosInstance.interceptors.request.use( axiosInstance.interceptors.request.use(
(config: AxiosRequestConfig) => { (config: AxiosRequestConfig) => {
// 白名单校验
if (includes(fetchAllowList, config.url)) return config
// 获取 token
const info = getLocalStorage(StorageEnum.GO_SYSTEM_STORE)
// 重新登录
if (!info) {
routerTurnByName(PageEnum.BASE_LOGIN_NAME)
return config
}
const userInfo = info[SystemStoreEnum.USER_INFO]
config.headers = {
...config.headers,
[userInfo[SystemStoreUserInfoEnum.TOKEN_NAME]]: userInfo[SystemStoreUserInfoEnum.USER_TOKEN] || ''
}
return config return config
}, },
(error: AxiosRequestConfig) => { (err: AxiosRequestConfig) => {
Promise.reject(error) Promise.reject(err)
} }
) )
@ -21,13 +40,31 @@ 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 (ErrorPageNameMap.get(code)) redirectErrorPage(code) if (code === ResultEnum.SUCCESS) {
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) => {
window['$message'].error('接口异常,请检查!') httpErrorHandle()
Promise.reject(err) Promise.reject(err)
} }
) )

View File

@ -13,7 +13,7 @@ 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,
}) })
} }

2
src/api/path/index.ts Normal file
View File

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

View File

@ -0,0 +1,84 @@
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

@ -0,0 +1,33 @@
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();
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -40,8 +40,8 @@ export enum MenuEnum {
UN_GROUP = 'unGroup', UN_GROUP = 'unGroup',
// 后退 // 后退
BACK = 'back', BACK = 'back',
// 前进 FORWORD = 'forward',
FORWORD = 'forward' SAVE = 'save'
} }
// Win 键盘枚举 // Win 键盘枚举
@ -64,3 +64,15 @@ export enum MacKeyboard {
SHIFT_SOURCE_KEY = "⇧", SHIFT_SOURCE_KEY = "⇧",
ALT_SOURCE_KEY = "⌥" ALT_SOURCE_KEY = "⌥"
} }
// 同步状态枚举
export enum SyncEnum {
// 等待
PENDING,
// 开始
START,
// 成功
SUCCESS,
// 失败
FAILURE
}

View File

@ -1,13 +1,18 @@
/** // 模块 Path 前缀分类
* @description: export enum ModuleTypeEnum {
*/ 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,
TIMEOUT = 10042 TOKEN_OVERDUE = 886,
TIMEOUT = 10042,
} }
// 数据相关 // 数据相关
@ -26,9 +31,13 @@ export enum RequestContentTypeEnum {
SQL = 1 SQL = 1
} }
/** // 头部
* @description: export enum RequestHttpHeaderEnum {
*/ TOKEN = 'Token',
COOKIE = 'Cookie'
}
// 请求方法
export enum RequestHttpEnum { export enum RequestHttpEnum {
GET = 'get', GET = 'get',
POST = 'post', POST = 'post',
@ -109,9 +118,7 @@ export type RequestParams = {
} }
} }
/** // 常用的contentTyp类型
* @description: contentTyp类型
*/
export enum ContentTypeEnum { export enum ContentTypeEnum {
// json // json
JSON = 'application/json;charset=UTF-8', JSON = 'application/json;charset=UTF-8',

View File

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

View File

@ -2,3 +2,4 @@ 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

@ -0,0 +1,23 @@
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,6 +11,8 @@ const global = {
help: 'Help', help: 'Help',
contact: 'About Software', contact: 'About Software',
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',
@ -26,8 +28,14 @@ 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,6 +1,8 @@
export default { export default {
create_btn: 'Creat', create_btn: 'Creat',
create_tip: 'Please select a content for development', create_success: 'Creat Success!',
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,6 +11,8 @@ const global = {
help: '帮助中心', help: '帮助中心',
contact: '关于软件', contact: '关于软件',
logout: '退出登录', logout: '退出登录',
logout_success: '退出成功!',
logout_failure: '退出失败!',
// 系统设置 // 系统设置
sys_set: '系统设置', sys_set: '系统设置',
lang_set: '语言设置', lang_set: '语言设置',
@ -18,16 +20,27 @@ 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,6 +1,8 @@
export default { export default {
// aside // aside
create_btn: '新建', create_btn: '新建',
create_success: '新建成功!',
create_failure: '新建失败,请稍后重试!',
create_tip: '从哪里出发好呢?', create_tip: '从哪里出发好呢?',
project: '项目', project: '项目',
my: '我的', my: '我的',

View File

@ -8,7 +8,10 @@ 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,6 +53,7 @@ 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, ChevronUpOutline as ChevronUpOutlineIcon,
ChevronDownOutline as ChevronDownOutlineIcon, ChevronDownOutline as ChevronDownOutlineIcon,
Pulse as PulseIcon, Pulse as PulseIcon,
@ -86,6 +87,7 @@ 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, Carbon3DCursor as Carbon3DCursorIcon,
Carbon3DSoftware as Carbon3DSoftwareIcon, Carbon3DSoftware as Carbon3DSoftwareIcon,
Filter as FilterIcon, Filter as FilterIcon,
@ -200,6 +202,8 @@ const ionicons5 = {
PawIcon, PawIcon,
// 搜索(放大镜) // 搜索(放大镜)
SearchIcon, SearchIcon,
// 加载
ReloadIcon,
// 过滤器 // 过滤器
FilterIcon, FilterIcon,
// 向上 // 向上
@ -256,6 +260,8 @@ const carbon = {
FitToScreenIcon, FitToScreenIcon,
FitToHeightIcon, FitToHeightIcon,
FitToWidthIcon, FitToWidthIcon,
// 保存
SaveIcon,
// 成组 // 成组
Carbon3DCursorIcon, Carbon3DCursorIcon,
// 解组 // 解组

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 } from '@/router/constant'; import { ErrorPage404, ErrorPage403, ErrorPage500, Layout, RedirectHome, RedirectUnPublish } 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: '/login', path: PageEnum.BASE_LOGIN,
name: 'Login', name: PageEnum.BASE_LOGIN_NAME,
component: () => import('@/views/login/index.vue'), component: () => import('@/views/login/index.vue'),
meta: { meta: {
title: '登录', title: '登录',
@ -60,22 +60,21 @@ export const ReloadRoute: AppRouteRecordRaw = {
}, },
} }
export const RedirectRoute: AppRouteRecordRaw = { export const RedirectRoute: RouteRecordRaw[] = [
{
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: '/redirect/:path(.*)', path: PageEnum.REDIRECT_UN_PUBLISH,
name: PageEnum.REDIRECT_NAME, name: PageEnum.REDIRECT_UN_PUBLISH_NAME,
component: () => import('@/views/redirect/index.vue'), component: RedirectUnPublish,
meta: { meta: {
title: PageEnum.REDIRECT_NAME, title: PageEnum.REDIRECT_UN_PUBLISH_NAME,
hideBreadcrumb: true,
}, },
}, },
], ]
};

View File

@ -4,6 +4,10 @@ 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,9 +1,8 @@
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 } from '@/router/base' import { HttpErrorPage, LoginRoute, ReloadRoute, RedirectRoute } from '@/router/base'
import { Layout } from '@/router/constant' import { Layout } from '@/router/constant'
import modules from '@/router/modules' import modules from '@/router/modules'
@ -19,6 +18,7 @@ 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, RedirectRoute, ReloadRoute]; export const constantRouter: any[] = [LoginRoute, ...RootRoute, ReloadRoute];
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(''), history: createWebHashHistory(''),

View File

@ -1,7 +1,15 @@
import { Router } from 'vue-router'; import { Router } from 'vue-router';
import { PageEnum } from '@/enums/pageEnum' import { PageEnum, PreviewEnum } 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) => {
@ -12,10 +20,8 @@ export function createRouterGuards(router: Router) {
next({ name: PageEnum.ERROR_PAGE_NAME_404 }) next({ name: PageEnum.ERROR_PAGE_NAME_404 })
} }
if (!loginCheck()) { // @ts-ignore
if (to.name === PageEnum.BASE_LOGIN_NAME) { if (!routerAllowList.includes(to.name) && !loginCheck()) {
next()
}
next({ name: PageEnum.BASE_LOGIN_NAME }) next({ name: PageEnum.BASE_LOGIN_NAME })
} }
next() next()

View File

@ -55,9 +55,12 @@ 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 requestIntervalUnit = RequestHttpIntervalEnum.SECOND

View File

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

View File

@ -1,5 +1,6 @@
import { CreateComponentType, CreateComponentGroupType, FilterEnum } from '@/packages/index.d' import { CreateComponentType, CreateComponentGroupType, 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 { import {
RequestHttpEnum, RequestHttpEnum,
RequestContentTypeEnum, RequestContentTypeEnum,
@ -12,6 +13,29 @@ import {
import { PreviewScaleEnum } from '@/enums/styleEnum' import { PreviewScaleEnum } from '@/enums/styleEnum'
import type { ChartColorsNameType, GlobalThemeJsonType } from '@/settings/chartThemes/index' import type { ChartColorsNameType, GlobalThemeJsonType } from '@/settings/chartThemes/index'
// 项目数据枚举
export enum ProjectInfoEnum {
// ID
PROJECT_ID = "projectId",
// 名称
PROJECT_NAME = 'projectName',
// 描述
REMARKS = 'remarks',
// 缩略图
THUMBNAIL= 'thumbnail',
// 是否公开发布
RELEASE = 'release'
}
// 项目数据
export type ProjectInfoType = {
[ProjectInfoEnum.PROJECT_ID]: string,
[ProjectInfoEnum.PROJECT_NAME]: string,
[ProjectInfoEnum.REMARKS]: string,
[ProjectInfoEnum.THUMBNAIL]: string,
[ProjectInfoEnum.RELEASE]: boolean
}
// 编辑画布属性 // 编辑画布属性
export enum EditCanvasTypeEnum { export enum EditCanvasTypeEnum {
EDIT_LAYOUT_DOM = 'editLayoutDom', EDIT_LAYOUT_DOM = 'editLayoutDom',
@ -20,12 +44,13 @@ export enum EditCanvasTypeEnum {
SCALE = 'scale', SCALE = 'scale',
USER_SCALE = 'userScale', USER_SCALE = 'userScale',
LOCK_SCALE = 'lockScale', LOCK_SCALE = 'lockScale',
SAVE_STATUS = 'saveStatus',
IS_CREATE = 'isCreate', IS_CREATE = 'isCreate',
IS_DRAG = 'isDrag', IS_DRAG = 'isDrag',
IS_SELECT = 'isSelect' IS_SELECT = 'isSelect'
} }
// 编辑区域 // 编辑区域(临时)
export type EditCanvasType = { export type EditCanvasType = {
// 编辑区域 DOM // 编辑区域 DOM
[EditCanvasTypeEnum.EDIT_LAYOUT_DOM]: HTMLElement | null [EditCanvasTypeEnum.EDIT_LAYOUT_DOM]: HTMLElement | null
@ -42,11 +67,13 @@ export type EditCanvasType = {
[EditCanvasTypeEnum.IS_CREATE]: boolean [EditCanvasTypeEnum.IS_CREATE]: boolean
// 拖拽中 // 拖拽中
[EditCanvasTypeEnum.IS_DRAG]: boolean [EditCanvasTypeEnum.IS_DRAG]: boolean
// 保存状态
[EditCanvasTypeEnum.SAVE_STATUS]: SyncEnum
// 框选中 // 框选中
[EditCanvasTypeEnum.IS_SELECT]: boolean [EditCanvasTypeEnum.IS_SELECT]: boolean
} }
// 滤镜/背景色/宽高主题等 // 画布数据/滤镜/背景色/宽高主题等
export enum EditCanvasConfigEnum { export enum EditCanvasConfigEnum {
WIDTH = 'width', WIDTH = 'width',
HEIGHT = 'height', HEIGHT = 'height',
@ -58,7 +85,14 @@ export enum EditCanvasConfigEnum {
PREVIEW_SCALE_TYPE = 'previewScaleType' PREVIEW_SCALE_TYPE = 'previewScaleType'
} }
export interface EditCanvasConfigType { // 画布属性(需保存)
export type EditCanvasConfigType = {
// ID
[EditCanvasConfigEnum.PROJECT_ID]: string,
// 项目名称
[EditCanvasConfigEnum.PROJECT_NAME]: string,
// 项目描述
[EditCanvasConfigEnum.REMARKS]: string,
// 滤镜-启用 // 滤镜-启用
[FilterEnum.FILTERS_SHOW]: boolean [FilterEnum.FILTERS_SHOW]: boolean
// 滤镜-色相 // 滤镜-色相
@ -128,6 +162,7 @@ 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',
@ -178,6 +213,7 @@ export interface RequestConfigType extends RequestPublicConfigType {
// 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

View File

@ -10,14 +10,22 @@ import { requestInterval, previewScaleType, requestIntervalUnit } from '@/settin
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 { MenuEnum, SyncEnum } from '@/enums/editPageEnum'
import { import {
HistoryActionTypeEnum, getUUID,
HistoryItemType, loadingStart,
HistoryTargetTypeEnum loadingFinish,
} from '@/store/modules/chartHistoryStore/chartHistoryStore.d' loadingError,
import { MenuEnum } from '@/enums/editPageEnum' isString,
import { getUUID, loadingStart, loadingFinish, loadingError, isString, isArray } from '@/utils' isArray
} from '@/utils'
import { import {
ProjectInfoType,
ChartEditStoreEnum, ChartEditStoreEnum,
ChartEditStorage, ChartEditStorage,
ChartEditStoreType, ChartEditStoreType,
@ -36,6 +44,14 @@ const settingStore = useSettingStore()
export const useChartEditStore = defineStore({ export const useChartEditStore = defineStore({
id: 'useChartEditStore', id: 'useChartEditStore',
state: (): ChartEditStoreType => ({ state: (): ChartEditStoreType => ({
// 项目数据
projectInfo: {
projectId: '',
projectName: '',
remarks: '',
thumbnail: '',
release: false
},
// 画布属性 // 画布属性
editCanvas: { editCanvas: {
// 编辑区域 Dom // 编辑区域 Dom
@ -54,7 +70,9 @@ export const useChartEditStore = defineStore({
// 拖拽中 // 拖拽中
isDrag: false, isDrag: false,
// 框选中 // 框选中
isSelect: false isSelect: false,
// 同步中
saveStatus: SyncEnum.PENDING
}, },
// 右键菜单 // 右键菜单
rightMenuShow: false, rightMenuShow: false,
@ -129,6 +147,9 @@ export const useChartEditStore = defineStore({
componentList: [] componentList: []
}), }),
getters: { getters: {
getProjectInfo(): ProjectInfoType {
return this.projectInfo
},
getMousePosition(): MousePositionType { getMousePosition(): MousePositionType {
return this.mousePosition return this.mousePosition
}, },
@ -163,6 +184,10 @@ 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
@ -795,7 +820,7 @@ export const useChartEditStore = defineStore({
loadingFinish() loadingFinish()
} }
}, },
// ---------------- // * 页面缩放设置-----------------
// * 设置页面大小 // * 设置页面大小
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

@ -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_SYSTEM_SETTING_STORE } = StorageEnum const { GO_SETTING_STORE } = StorageEnum
const storageSetting: SettingStoreType = getLocalStorage( const storageSetting: SettingStoreType = getLocalStorage(
GO_SYSTEM_SETTING_STORE GO_SETTING_STORE
) )
// 全局设置 // 全局设置
@ -48,7 +48,7 @@ export const useSettingStore = defineStore({
this.$patch(state => { this.$patch(state => {
state[key] = value state[key] = value
}) })
setLocalStorage(GO_SYSTEM_SETTING_STORE, this.$state) setLocalStorage(GO_SETTING_STORE, this.$state)
} }
} }
}) })

View File

@ -0,0 +1,31 @@
export enum SystemStoreUserInfoEnum {
USER_TOKEN = 'userToken',
TOKEN_NAME = 'tokenName',
USER_ID = 'userId',
USER_NAME = 'userName',
NICK_NAME = 'nickName',
}
export interface UserInfoType {
[SystemStoreUserInfoEnum.USER_TOKEN]?: string,
[SystemStoreUserInfoEnum.TOKEN_NAME]?: 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

@ -0,0 +1,40 @@
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,3 +1,65 @@
/**
* * 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

6
src/utils/http.ts Normal file
View File

@ -0,0 +1,6 @@
/**
* *
*/
export const httpErrorHandle = () => {
window['$message'].error(window['$t']('http.error_message'))
}

View File

@ -7,3 +7,4 @@ 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,11 +1,12 @@
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { ResultEnum } from '@/enums/httpEnum' import { ResultEnum, RequestHttpHeaderEnum } from '@/enums/httpEnum'
import { ErrorPageNameMap, PageEnum } from '@/enums/pageEnum' import { ErrorPageNameMap, PageEnum, PreviewEnum } from '@/enums/pageEnum'
import { docPath, giteeSourceCodePath } from '@/settings/pathConst' import { docPath, giteeSourceCodePath } from '@/settings/pathConst'
import { cryptoDecode } from './crypto' import { SystemStoreEnum, SystemStoreUserInfoEnum } from '@/store/modules/systemStore/systemStore.d'
import { StorageEnum } from '@/enums/storageEnum' import { StorageEnum } from '@/enums/storageEnum'
import { clearLocalStorage, getLocalStorage } from './storage' import { clearLocalStorage, getLocalStorage, clearCookie } from './storage'
import router from '@/router' import router from '@/router'
import { logoutApi } from '@/api/path'
/** /**
* * * *
@ -101,11 +102,20 @@ export const reloadRoutePage = () => {
} }
/** /**
* * 退 * * 退
*/ */
export const logout = () => { export const logout = async () => {
clearLocalStorage(StorageEnum.GO_LOGIN_INFO_STORE) try {
const res = await logoutApi() as unknown as MyResponseType
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) routerTurnByName(PageEnum.BASE_LOGIN_NAME)
}
} catch (error) {
window['$message'].success(window['$t']('global.logout_failure'))
}
} }
/** /**
@ -153,6 +163,19 @@ 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
@ -162,15 +185,14 @@ export const goHome = () => {
} }
/** /**
* * login * *
* @return boolean * @return boolean
*/ */
export const loginCheck = () => { export const loginCheck = () => {
try { try {
const info = getLocalStorage(StorageEnum.GO_LOGIN_INFO_STORE) const info = getLocalStorage(StorageEnum.GO_SYSTEM_STORE)
if (!info) return false if (!info) return false
const decodeInfo = cryptoDecode(info) if (info[SystemStoreEnum.USER_INFO][SystemStoreUserInfoEnum.USER_TOKEN]) {
if (decodeInfo) {
return true return true
} }
return false return false
@ -178,3 +200,14 @@ export const loginCheck = () => {
return false return false
} }
} }
/**
* *
* @returns
*/
export const previewPath = (id?: string | number) => {
const { origin, pathname } = document.location
const path = fetchPathByName(PreviewEnum.CHART_PREVIEW_NAME, 'href')
const previewPath = `${origin}${pathname}${path}/${id || 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 @@
* @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 @@
* * * *
* @param name * @param name
*/ */
export const clearLocalStorage = (name: string) => { export const clearLocalStorage = (name: string) => {
window.localStorage.removeItem(name) window.localStorage.removeItem(name)
} }
@ -69,3 +69,41 @@ 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

@ -109,29 +109,6 @@ 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)
}
}
}
/** /**
* * * *
*/ */

View File

@ -128,16 +128,20 @@ 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 } from '@/utils' import { fileToUrl, loadAsyncComponent, fetchRouteParamsLocation } 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 { 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
@ -261,17 +265,34 @@ const switchSelectColorHandle = () => {
// //
const customRequest = (options: UploadCustomRequestOptions) => { const customRequest = (options: UploadCustomRequestOptions) => {
const { file } = options const { file } = options
nextTick(() => { nextTick(async () => {
if(!systemStore.getFetchInfo.OSSUrl) {
window['$message'].error('添加图片失败,请刷新页面重试!')
return
}
if (file.file) { if (file.file) {
const ImageUrl = fileToUrl(file.file) //
const newNameFile = new File(
[file.file],
`${fetchRouteParamsLocation()}_index_background.png`,
{ type: file.file.type }
)
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( chartEditStore.setEditCanvasConfig(
EditCanvasConfigEnum.BACKGROUND_IMAGE, EditCanvasConfigEnum.BACKGROUND_IMAGE,
ImageUrl uploadRes.data.objectContent.httpRequest.uri
) )
chartEditStore.setEditCanvasConfig( chartEditStore.setEditCanvasConfig(
EditCanvasConfigEnum.SELECT_COLOR, EditCanvasConfigEnum.SELECT_COLOR,
false false
) )
return
}
window['$message'].error('添加图片失败,请稍后重试!')
} else { } else {
window['$message'].error('添加图片失败,请稍后重试!') window['$message'].error('添加图片失败,请稍后重试!')
} }

View File

@ -1,6 +1,10 @@
<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">
<!-- 快捷键提示 --> <!-- 快捷键提示 -->
@ -55,7 +59,8 @@
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.vue' import { EditShortcutKey } from '../EditShortcutKey/index'
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'
@ -136,12 +141,13 @@ 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

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

View File

@ -0,0 +1,97 @@
<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

@ -116,9 +116,6 @@ const options = computed(() => {
</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

@ -93,20 +93,25 @@ const shortcutKeyOptions = [
win: `${WinKeyboard.CTRL.toUpperCase()} + ${WinKeyboard.SHIFT.toUpperCase()} + Z `, win: `${WinKeyboard.CTRL.toUpperCase()} + ${WinKeyboard.SHIFT.toUpperCase()} + Z `,
mac: `${MacKeyboard.CTRL.toUpperCase()} + ${MacKeyboard.SHIFT.toUpperCase()} + Z ` mac: `${MacKeyboard.CTRL.toUpperCase()} + ${MacKeyboard.SHIFT.toUpperCase()} + Z `
}, },
{
label: '保存',
win: `${WinKeyboard.CTRL.toUpperCase()} + S `,
mac: `${MacKeyboard.CTRL.toUpperCase()} + S `,
},
{ {
label: '多选', label: '多选',
win: `${WinKeyboard.CTRL.toUpperCase()} + 🖱️ `, win: `${WinKeyboard.CTRL.toUpperCase()} + 🖱️ `,
mac: `${MacKeyboard.CTRL_SOURCE_KEY.toUpperCase()} + 🖱️ ` mac: `${MacKeyboard.CTRL.toUpperCase()} + 🖱️ `
}, },
{ {
label: '创建分组', label: '创建分组',
win: `${WinKeyboard.CTRL.toUpperCase()} + G / 🖱️ `, win: `${WinKeyboard.CTRL.toUpperCase()} + G / 🖱️ `,
mac: `${MacKeyboard.CTRL_SOURCE_KEY.toUpperCase()} + G / 🖱️` mac: `${MacKeyboard.CTRL.toUpperCase()} + G / 🖱️`
}, },
{ {
label: '解除分组', label: '解除分组',
win: `${WinKeyboard.CTRL.toUpperCase()} + ${WinKeyboard.SHIFT.toUpperCase()} + G `, win: `${WinKeyboard.CTRL.toUpperCase()} + ${WinKeyboard.SHIFT.toUpperCase()} + G `,
mac: `${MacKeyboard.CTRL_SOURCE_KEY.toUpperCase()} + ${WinKeyboard.SHIFT.toUpperCase()} + G ` mac: `${MacKeyboard.CTRL.toUpperCase()} + ${WinKeyboard.SHIFT.toUpperCase()} + G `
} }
] ]
const closeHandle = () => { const closeHandle = () => {

View File

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

View File

@ -7,7 +7,6 @@ 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

@ -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

@ -88,6 +88,7 @@ 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, mousedownHandleUnStop, useMouseHandle } from './hooks/useDrag.hook' import { dragHandle, dragoverHandle, mousedownHandleUnStop, useMouseHandle } from './hooks/useDrag.hook'
import { useComponentStyle, useSizeStyle } from './hooks/useStyle.hook' import { useComponentStyle, useSizeStyle } from './hooks/useStyle.hook'
@ -101,6 +102,7 @@ import { EditTools } from './components/EditTools'
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const { handleContextMenu } = useContextMenu() const { handleContextMenu } = useContextMenu()
const { dataSyncFetch, intervalDataSyncUpdate } = useSync()
// //
useLayout() useLayout()
@ -171,9 +173,13 @@ const rangeStyle = computed(() => {
} }
}) })
//
onMounted(() => { onMounted(() => {
//
useAddKeyboard() useAddKeyboard()
//
dataSyncFetch()
//
intervalDataSyncUpdate()
}) })
</script> </script>

View File

@ -29,26 +29,44 @@
</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, reactive, computed } from 'vue' import { toRefs, ref, 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()
@ -130,7 +148,7 @@ const clickHistoryHandle = (item: ItemType<HistoryStackEnum>) => {
// //
const goHomeHandle = () => { const goHomeHandle = () => {
goDialog({ goDialog({
message: '返回将不会保存任何操作', message: '确定已保存了数据Ctrl / ⌘ + S并返回到首页吗',
isMaskClosable: true, isMaskClosable: true,
onPositiveCallback: () => { onPositiveCallback: () => {
goHome() goHome()

View File

@ -1,29 +1,98 @@
<template> <template>
<n-space class="go-mt-0"> <n-space>
<n-button v-for="item in btnList" :key="item.title" ghost @click="item.event"> <n-button
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 { shallowReactive } from 'vue' import { ref, shallowReactive, watchEffect } from 'vue'
import { renderIcon, goDialog, fetchPathByName, routerTurnByPath, setSessionStorage, getLocalStorage } from '@/utils' import { useRoute } from 'vue-router'
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 { useRoute } from 'vue-router' import { ResultEnum } from '@/enums/httpEnum'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d' import { ProjectInfoEnum } 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 } = icon.ionicons5 const { BrowsersOutlineIcon, SendIcon, CloseIcon } = 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')
@ -32,55 +101,100 @@ 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 = getLocalStorage(StorageEnum.GO_CHART_STORAGE_LIST) || [] const sessionStorageInfo =
getLocalStorage(StorageEnum.GO_CHART_STORAGE_LIST) || []
if (sessionStorageInfo?.length) { if (sessionStorageInfo?.length) {
const repeateIndex = sessionStorageInfo.findIndex((e: { id: string }) => e.id === previewId) const repeateIndex = sessionStorageInfo.findIndex(
(e: { id: string }) => e.id === previewId
)
// //
if (repeateIndex !== -1) { if (repeateIndex !== -1) {
sessionStorageInfo.splice(repeateIndex, 1, { id: previewId, ...storageInfo }) sessionStorageInfo.splice(repeateIndex, 1, {
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, ...storageInfo id: previewId,
...storageInfo,
}) })
setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, sessionStorageInfo) setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, sessionStorageInfo)
} }
} else { } else {
setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, [{ id: previewId, ...storageInfo }]) setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, [
{ 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 = () => { const sendHandle = async () => {
goDialog({ const res = (await updateProjectApi({
message: '想体验发布功能,请前往 master-fetch 分支查看: https://gitee.com/MTrun/go-view/tree/master-fetch', id: fetchRouteParamsLocation(),
positiveText: '了然', //
closeNegativeText: true, state: release.value ? -1 : 1,
onPositiveCallback: () => {} })) as unknown as MyResponseType
})
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([
{ {
select: true, key: 'preview',
title: '预览', title: () => '预览',
type: () => 'default',
icon: renderIcon(BrowsersOutlineIcon), icon: renderIcon(BrowsersOutlineIcon),
event: previewHandle event: previewHandle,
}, },
{ {
select: true, key: 'release',
title: '发布', title: () => (release.value ? '已发布' : '发布'),
icon: renderIcon(SendIcon), icon: renderIcon(SendIcon),
event: sendHandle type: () => (release.value ? 'primary' : 'default'),
} event: modelShowHandle,
},
]) ])
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.align-center { @include go('system-setting') {
margin-top: -4px; @extend .go-background-filter;
min-width: 100px;
max-width: 60vw;
padding-bottom: 20px;
@include deep() {
.n-list-item:not(:last-child) {
border-bottom: 0;
}
}
} }
</style> </style>

View File

@ -6,9 +6,7 @@
<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"> <span class="title">{{ comTitle }}</span>
{{ comTitle }}
</span>
</n-button> </n-button>
</n-text> </n-text>
@ -29,31 +27,31 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, nextTick, computed } from 'vue' import { ref, nextTick, computed, watchEffect } from 'vue'
import { fetchRouteParams } from '@/utils' import { ResultEnum } from '@/enums/httpEnum'
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)
// id const title = ref<string>(fetchRouteParamsLocation())
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.replace(/\s/g, ""); title.value = title.value && title.value.replace(/\s/g, "")
return title.value.length ? title.value : '新项目' return title.value.length ? title.value : fetchRouteParamsLocation()
}) })
const handleFocus = () => { const handleFocus = () => {
@ -63,8 +61,18 @@ const handleFocus = () => {
}) })
} }
const handleBlur = () => { const handleBlur = async () => {
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>

View File

@ -1,4 +1,5 @@
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'
@ -6,7 +7,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}`
@ -22,6 +23,7 @@ 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'),
[MenuEnum.GROUP]: winCtrlMerge('g'), [MenuEnum.GROUP]: winCtrlMerge('g'),
[MenuEnum.UN_GROUP]: winCtrlMerge(winShiftMerge('g')), [MenuEnum.UN_GROUP]: winCtrlMerge(winShiftMerge('g')),
} }
@ -43,6 +45,7 @@ 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'),
[MenuEnum.GROUP]: macCtrlMerge('g'), [MenuEnum.GROUP]: macCtrlMerge('g'),
[MenuEnum.UN_GROUP]: macCtrlMerge(macShiftMerge('g')), [MenuEnum.UN_GROUP]: macCtrlMerge(macShiftMerge('g')),
} }
@ -62,6 +65,7 @@ const winKeyList: Array<string> = [
winKeyboardValue.back, winKeyboardValue.back,
winKeyboardValue.forward, winKeyboardValue.forward,
winKeyboardValue.save,
winKeyboardValue.group, winKeyboardValue.group,
winKeyboardValue.unGroup, winKeyboardValue.unGroup,
] ]
@ -81,6 +85,7 @@ const macKeyList: Array<string> = [
macKeyboardValue.back, macKeyboardValue.back,
macKeyboardValue.forward, macKeyboardValue.forward,
macKeyboardValue.save,
macKeyboardValue.group, macKeyboardValue.group,
macKeyboardValue.unGroup, macKeyboardValue.unGroup,
] ]
@ -156,6 +161,11 @@ export const useAddKeyboard = () => {
case keyboardValue.unGroup: case keyboardValue.unGroup:
keymaster(e, throttle(() => { chartEditStore.setUnGroup(); return false }, throttleTime)) keymaster(e, throttle(() => { chartEditStore.setUnGroup(); return false }, throttleTime))
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,8 +1,19 @@
import { getUUID } from '@/utils' import { onUnmounted } from 'vue';
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 { ChartEditStoreEnum, ChartEditStorage } from '@/store/modules/chartEditStore/chartEditStore.d' import { EditCanvasTypeEnum, ChartEditStoreEnum, ProjectInfoEnum, 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 { 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'
import { CreateComponentType, CreateComponentGroupType, ConfigType } from '@/packages/index.d' import { CreateComponentType, CreateComponentGroupType, ConfigType } from '@/packages/index.d'
import { PublicGroupConfigClass } from '@/packages/public/publicConfig' import { PublicGroupConfigClass } from '@/packages/public/publicConfig'
@ -10,6 +21,7 @@ import { PublicGroupConfigClass } from '@/packages/public/publicConfig'
export const useSync = () => { export const useSync = () => {
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const chartHistoryStore = useChartHistoryStore() const chartHistoryStore = useChartHistoryStore()
const systemStore = useSystemStore()
/** /**
* * * *
@ -105,7 +117,130 @@ export const useSync = () => {
} }
} }
/**
* *
* @param projectData
* @returns
*/
const updateStoreInfo = (projectData: {
id: string,
projectName: string,
indexImage: string,
remarks: string,
state: number
}) => {
const { id, projectName, remarks, indexImage, state } = projectData
// ID
chartEditStore.setProjectInfo(ProjectInfoEnum.PROJECT_ID, id)
// 名称
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
}else {
chartEditStore.setProjectInfo(ProjectInfoEnum.PROJECT_ID, fetchRouteParamsLocation())
}
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
}
let projectId = chartEditStore.getProjectInfo[ProjectInfoEnum.PROJECT_ID];
if(projectId === null || projectId === ''){
window['$message'].error('数据初未始化成功,请刷新页面!')
return
}
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.START)
// 获取缩略图片
const range = document.querySelector('.go-edit-range') as HTMLElement
// 生成图片
const canvasImage: HTMLCanvasElement = await html2canvas(range, {
backgroundColor: null,
allowTaint: true,
useCORS: true
})
// 上传预览图
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', projectId)
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 class="text-base text-gray-500">抱歉你无权访问该页面</h1> <h1>抱歉你无权访问该页面</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 class="text-base text-gray-500">抱歉你访问的页面不存在</h1> <h1>抱歉你访问的页面不存在</h1>
</div> </div>
<n-button type="primary" @click="goHome">回到首页</n-button> <n-button type="primary" @click="goHome">回到首页</n-button>
</div> </div>

View File

@ -4,9 +4,9 @@
<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 class="text-base text-gray-500">抱歉服务器出错了</h1> <h1>抱歉服务器出错了</h1>
</div> </div>
<n-button type="primary" secondary @click="goHome">回到首页</n-button> <n-button type="primary" secondary @click="goLogin">重新登录</n-button>
</div> </div>
</template> </template>
@ -14,8 +14,8 @@
import { PageEnum } from '@/enums/pageEnum' import { PageEnum } from '@/enums/pageEnum'
import { routerTurnByName } from '@/utils' import { routerTurnByName } from '@/utils'
function goHome() { function goLogin() {
routerTurnByName(PageEnum.BASE_HOME_NAME) routerTurnByName(PageEnum.BASE_LOGIN_NAME)
} }
</script> </script>

View File

@ -118,45 +118,38 @@
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 { useDesignStore } from '@/store/modules/designStore/designStore' import { useSystemStore } from '@/store/modules/systemStore/systemStore'
import { SystemStoreUserInfoEnum, 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 { icon } from '@/plugins'
import { StorageEnum } from '@/enums/storageEnum' import { StorageEnum } from '@/enums/storageEnum'
import { routerTurnByName, cryptoEncode, setLocalStorage } from '@/utils' import { icon } from '@/plugins'
const { GO_LOGIN_INFO_STORE } = StorageEnum import { routerTurnByName } from '@/utils'
import { loginApi } from '@/api/path'
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 designStore = useDesignStore() const systemStore = useSystemStore()
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: '123456', password: 'admin',
}) })
const rules = { const rules = {
@ -196,38 +189,57 @@ 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 = (e: Event) => { const handleSubmit = async (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( //
GO_LOGIN_INFO_STORE, const res = await loginApi({
cryptoEncode(
JSON.stringify({
username, username,
password, password
}) as unknown as MyResponseType
if(res.data) {
const { tokenValue, tokenName } = res.data.token
const { nickname, username, id } = res.data.userinfo
// pinia
systemStore.setItem(SystemStoreEnum.USER_INFO, {
[SystemStoreUserInfoEnum.USER_TOKEN]: tokenValue,
[SystemStoreUserInfoEnum.TOKEN_NAME]: tokenName,
[SystemStoreUserInfoEnum.USER_ID]: id,
[SystemStoreUserInfoEnum.USER_NAME]: username,
[SystemStoreUserInfoEnum.NICK_NAME]: nickname,
t
}) })
)
) window['$message'].success(t('login.login_success'))
window['$message'].success(`${t('login.login_success')}!`)
routerTurnByName(PageEnum.BASE_HOME_NAME, true) 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)
const intComponent = (target: CreateComponentType) => { const intComponent = (target: CreateComponentType) => {

View File

@ -4,6 +4,7 @@ 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,5 +1,6 @@
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,91 +1,9 @@
<template> <template>
<div :class="`go-preview ${localStorageInfo.editCanvasConfig.previewScaleType}`"> <suspense>
<template v-if="showEntity"> <suspense-index></suspense-index>
<!-- 实体区域 --> </suspense>
<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 { computed } from 'vue' import suspenseIndex from './suspenseIndex.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.filterShow ? localStorageInfo.editCanvasConfig : undefined)
}
})
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>

View File

@ -0,0 +1,110 @@
<template>
<div
:class="`go-preview ${localStorageInfo.editCanvasConfig.previewScaleType}`"
>
<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>
<script setup lang="ts">
import { computed } from 'vue'
import { PreviewRenderList } from './components/PreviewRenderList'
import { getFilterStyle, routerTurnByName, getSessionStorage } from '@/utils'
import { getEditCanvasConfigStyle, getSessionStorageInfo } from './utils'
import { PageEnum } from '@/enums/pageEnum'
import { StorageEnum } from '@/enums/storageEnum'
import { useScale } from './hooks/useScale.hook'
import { useStore } from './hooks/useStore.hook'
import { PreviewScaleEnum } from '@/enums/styleEnum'
import { useComInstall } from './hooks/useComInstall.hook'
import type { ChartEditStorageType } from './index.d'
const storageList: ChartEditStorageType[] = getSessionStorage(
StorageEnum.GO_CHART_STORAGE_LIST
)
const localStorageInfo = await getSessionStorageInfo() as ChartEditStorageType
// @ts-ignore
if(localStorageInfo.isRelease === false) {
routerTurnByName(PageEnum.REDIRECT_UN_PUBLISH_NAME, true, false)
}
const previewRefStyle = computed(() => {
return {
...getEditCanvasConfigStyle(localStorageInfo.editCanvasConfig),
...getFilterStyle(localStorageInfo.editCanvasConfig.filterShow ? localStorageInfo.editCanvasConfig : undefined),
}
})
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>
<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>

View File

@ -1,26 +1,40 @@
import { getSessionStorage } from '@/utils' import { getSessionStorage, fetchRouteParamsLocation, httpErrorHandle } from '@/utils'
import { ResultEnum } from '@/enums/httpEnum'
import { StorageEnum } from '@/enums/storageEnum' import { StorageEnum } from '@/enums/storageEnum'
import { ChartEditStorage } from '@/store/modules/chartEditStore/chartEditStore.d' import { ChartEditStorage } from '@/store/modules/chartEditStore/chartEditStore.d'
import { fetchProjectApi } from '@/api/path'
export interface ChartEditStorageType extends ChartEditStorage { export interface ChartEditStorageType extends ChartEditStorage {
id: string id: string
} }
// 根据路由 id 获取存储数据的信息 // 根据路由 id 获取存储数据的信息
export const getSessionStorageInfo = () => { export const getSessionStorageInfo = async () => {
const urlHash = document.location.hash const id = fetchRouteParamsLocation()
const toPathArray = urlHash.split('/')
const id = toPathArray && toPathArray[toPathArray.length - 1]
const storageList: ChartEditStorageType[] = getSessionStorage( const storageList: ChartEditStorageType[] = getSessionStorage(
StorageEnum.GO_CHART_STORAGE_LIST StorageEnum.GO_CHART_STORAGE_LIST
) )
if(!storageList) return // 是否本地预览
if (!storageList || storageList.findIndex(e => e.id === id.toString()) === -1) {
// 接口调用
const res = await fetchProjectApi({ projectId: id }) as unknown as MyResponseType
if (res.code === ResultEnum.SUCCESS) {
const { content, state } = res.data
if (state === -1) {
// 跳转未发布页
return { isRelease: false }
}
return { ...JSON.parse(content), id }
} else {
httpErrorHandle()
}
} else {
// 本地读取
for (let i = 0; i < storageList.length; i++) { for (let i = 0; i < storageList.length; i++) {
if (id.toString() === storageList[i]['id']) { if (id.toString() === storageList[i]['id']) {
return storageList[i] return storageList[i]
} }
} }
}
} }

View File

@ -7,7 +7,7 @@
<mac-os-control-btn <mac-os-control-btn
class="top-btn" class="top-btn"
:hidden="['remove']" :hidden="['remove']"
@close="deleteHanlde" @close="deleteHandle"
@resize="resizeHandle" @resize="resizeHandle"
></mac-os-control-btn> ></mac-os-control-btn>
</div> </div>
@ -17,9 +17,7 @@
object-fit="contain" object-fit="contain"
height="180" height="180"
preview-disabled preview-disabled
:src=" :src="`${cardData.image}?time=${new Date().getTime()}`"
requireUrl('project/moke-20211219181327.png')
"
:alt="cardData.title" :alt="cardData.title"
:fallback-src="requireErrorImg()" :fallback-src="requireErrorImg()"
></n-image> ></n-image>
@ -27,8 +25,8 @@
</div> </div>
<template #action> <template #action>
<div class="go-flex-items-center list-footer" justify="space-between"> <div class="go-flex-items-center list-footer" justify="space-between">
<n-text class="go-ellipsis-1" :title="cardData.title"> <n-text class="go-ellipsis-1">
{{ cardData.title || '' }} {{ cardData.title || cardData.id || '未命名' }}
</n-text> </n-text>
<!-- 工具 --> <!-- 工具 -->
<div class="go-flex-items-center list-footer-ri"> <div class="go-flex-items-center list-footer-ri">
@ -75,9 +73,9 @@
</n-tooltip> </n-tooltip>
</template> </template>
</n-space> </n-space>
</div>
<!-- end --> <!-- end -->
</div> </div>
</div>
</template> </template>
</n-card> </n-card>
</div> </div>
@ -100,17 +98,12 @@ const {
SendIcon SendIcon
} = icon.ionicons5 } = icon.ionicons5
const emit = defineEmits(['delete', 'resize', 'edit']) const emit = defineEmits(['preview', 'delete', 'resize', 'edit', 'release'])
const props = defineProps({ const props = defineProps({
cardData: Object as PropType<Chartype> cardData: Object as PropType<Chartype>
}) })
// url
const requireUrl = (name: string) => {
return new URL(`../../../../../assets/images/${name}`, import.meta.url).href
}
const fnBtnList = reactive([ const fnBtnList = reactive([
{ {
label: renderLang('global.r_edit'), label: renderLang('global.r_edit'),
@ -133,12 +126,14 @@ const selectOptions = ref([
{ {
label: renderLang('global.r_copy'), label: renderLang('global.r_copy'),
key: 'copy', key: 'copy',
icon: renderIcon(CopyIcon) icon: renderIcon(CopyIcon),
disabled: true
}, },
{ {
label: renderLang('global.r_rename'), label: renderLang('global.r_rename'),
key: 'rename', key: 'rename',
icon: renderIcon(PencilIcon) icon: renderIcon(PencilIcon),
disabled: true
}, },
{ {
type: 'divider', type: 'divider',
@ -148,13 +143,14 @@ const selectOptions = ref([
label: props.cardData?.release label: props.cardData?.release
? renderLang('global.r_unpublish') ? renderLang('global.r_unpublish')
: renderLang('global.r_publish'), : renderLang('global.r_publish'),
key: 'send', key: 'release',
icon: renderIcon(SendIcon) icon: renderIcon(SendIcon)
}, },
{ {
label: renderLang('global.r_download'), label: renderLang('global.r_download'),
key: 'download', key: 'download',
icon: renderIcon(DownloadIcon) icon: renderIcon(DownloadIcon),
disabled: true
}, },
{ {
type: 'divider', type: 'divider',
@ -169,8 +165,14 @@ const selectOptions = ref([
const handleSelect = (key: string) => { const handleSelect = (key: string) => {
switch (key) { switch (key) {
case 'preview':
previewHandle()
break
case 'delete': case 'delete':
deleteHanlde() deleteHandle()
break
case 'release':
releaseHandle()
break break
case 'edit': case 'edit':
editHandle() editHandle()
@ -178,8 +180,13 @@ const handleSelect = (key: string) => {
} }
} }
//
const previewHandle = () => {
emit('preview', props.cardData)
}
// //
const deleteHanlde = () => { const deleteHandle = () => {
emit('delete', props.cardData) emit('delete', props.cardData)
} }
@ -188,6 +195,11 @@ const editHandle = () => {
emit('edit', props.cardData) emit('edit', props.cardData)
} }
//
const releaseHandle = () => {
emit('release', props.cardData)
}
// //
const resizeHandle = () => { const resizeHandle = () => {
emit('resize', props.cardData) emit('resize', props.cardData)

View File

@ -1,58 +1,122 @@
import { ref } from 'vue' import { ref, reactive } from 'vue';
import { goDialog } from '@/utils' import { goDialog, httpErrorHandle } from '@/utils'
import { DialogEnum } from '@/enums/pluginEnum' import { DialogEnum } from '@/enums/pluginEnum'
import { ChartList } from '../../..' import { projectListApi, deleteProjectApi, changeProjectReleaseApi } from '@/api/path'
import { Chartype, ChartList } from '../../../index.d'
import { ResultEnum } from '@/enums/httpEnum'
// 数据初始化 // 数据初始化
export const useDataListInit = () => { export const useDataListInit = () => {
const list = ref<ChartList>([
{
id: 1,
title: '物料1-假数据不可用',
release: true,
label: '官方案例'
},
{
id: 2,
title: '物料2-假数据不可用',
release: false,
label: '官方案例'
},
{
id: 3,
title: '物料3-假数据不可用',
release: false,
label: '官方案例'
},
{
id: 4,
title: '物料4-假数据不可用',
release: false,
label: '官方案例'
},
{
id: 5,
title: '物料5-假数据不可用',
release: false,
label: '官方案例'
}
])
// 删除 const loading = ref(true)
const deleteHandle = (cardData: object, index: number) => {
const paginat = reactive({
// 当前页数
page: 1,
// 每页值
limit: 12,
// 总数
count: 10,
})
const list = ref<ChartList>([])
// 数据请求
const fetchList = async () => {
loading.value = true
const res = await projectListApi({
page: paginat.page,
limit: paginat.limit
}) as any
if (res.data) {
const { count } = res
paginat.count = count
list.value = res.data.map((e: any) => {
const { id, projectName, state, createTime, indexImage, createUserId } = e
return {
id: id,
title: projectName,
createId: createUserId,
time: createTime,
image: indexImage,
release: state !== -1
}
})
setTimeout(() => {
loading.value = false
}, 500)
return
}
httpErrorHandle()
}
// 修改页数
const changePage = (_page: number) => {
paginat.page = _page
fetchList()
}
// 修改大小
const changeSize = (_size: number) => {
paginat.limit = _size
fetchList()
}
// 删除处理
const deleteHandle = (cardData: Chartype) => {
goDialog({ goDialog({
type: DialogEnum.DELETE, type: DialogEnum.DELETE,
promise: true, promise: true,
onPositiveCallback: () => onPositiveCallback: () => new Promise(res => {
new Promise(res => setTimeout(() => res(1), 1000)), res(deleteProjectApi({
promiseResCallback: (e: any) => { ids: cardData.id
window.$message.success('删除成功') }))
list.value.splice(index, 1) }),
promiseResCallback: (res: any) => {
if (res.code === ResultEnum.SUCCESS) {
window['$message'].success(window['$t']('global.r_delete_success'))
fetchList()
return
}
httpErrorHandle()
} }
}) })
} }
// 发布处理
const releaseHandle = async (cardData: Chartype, index: number) => {
const { id, release } = cardData
const res = await changeProjectReleaseApi({
id: id,
// [-1未发布, 1发布]
state: !release ? 1 : -1
}) as unknown as MyResponseType
if (res.code === ResultEnum.SUCCESS) {
list.value = []
fetchList()
// 发布 -> 未发布
if (release) {
window['$message'].success(window['$t']('global.r_unpublish_success'))
return
}
// 未发布 -> 发布
window['$message'].success(window['$t']('global.r_publish_success'))
return
}
httpErrorHandle()
}
// 立即请求
fetchList()
return { return {
loading,
paginat,
list, list,
fetchList,
releaseHandle,
changeSize,
changePage,
deleteHandle deleteHandle
} }
} }

View File

@ -1,7 +1,7 @@
import { ref, Ref } from 'vue' import { ref } from 'vue'
import { ChartEnum } from '@/enums/pageEnum' import { ChartEnum } from '@/enums/pageEnum'
import { fetchPathByName, routerTurnByPath } from '@/utils' import { fetchPathByName, routerTurnByPath, openNewWindow, previewPath } from '@/utils'
import { Chartype } from '../../..' import { Chartype } from '../../../index.d'
export const useModalDataInit = () => { export const useModalDataInit = () => {
const modalShow = ref<boolean>(false) const modalShow = ref<boolean>(false)
const modalData = ref<Chartype | null>(null) const modalData = ref<Chartype | null>(null)
@ -12,25 +12,31 @@ export const useModalDataInit = () => {
modalData.value = null modalData.value = null
} }
// 打开 modal // 缩放处理
const resizeHandle = (cardData: Chartype) => { const resizeHandle = (cardData: Chartype) => {
if(!cardData) return if (!cardData) return
modalShow.value = true modalShow.value = true
modalData.value = cardData modalData.value = cardData
} }
// 打开 modal // 编辑处理
const editHandle = (cardData: Chartype) => { const editHandle = (cardData: Chartype) => {
if(!cardData) return if (!cardData) return
const path = fetchPathByName(ChartEnum.CHART_HOME_NAME, 'href') const path = fetchPathByName(ChartEnum.CHART_HOME_NAME, 'href')
routerTurnByPath(path, [cardData.id], undefined, true) routerTurnByPath(path, [cardData.id], undefined, true)
} }
// 预览处理
const previewHandle = (cardData: Chartype) => {
openNewWindow(previewPath(cardData.id))
}
return { return {
modalData, modalData,
modalShow, modalShow,
closeModal, closeModal,
resizeHandle, resizeHandle,
editHandle editHandle,
previewHandle
} }
} }

View File

@ -1,28 +1,40 @@
<template> <template>
<div class="go-items-list"> <div class="go-items-list">
<n-grid <!-- 加载 -->
:x-gap="20" <div v-show="loading">
:y-gap="20" <go-loading></go-loading>
cols="2 s:2 m:3 l:4 xl:4 xxl:4" </div>
responsive="screen" <!-- 列表 -->
> <div v-show="!loading">
<n-grid :x-gap="20" :y-gap="20" cols="2 s:2 m:3 l:4 xl:4 xxl:4" responsive="screen">
<n-grid-item v-for="(item, index) in list" :key="item.id"> <n-grid-item v-for="(item, index) in list" :key="item.id">
<project-items-card <project-items-card
:cardData="item" :cardData="item"
@preview="previewHandle"
@resize="resizeHandle" @resize="resizeHandle"
@delete="deleteHandle($event, index)" @delete="deleteHandle(item)"
@release="releaseHandle(item, index)"
@edit="editHandle" @edit="editHandle"
></project-items-card> ></project-items-card>
</n-grid-item> </n-grid-item>
</n-grid> </n-grid>
</div>
<!-- 分页 -->
<div class="list-pagination"> <div class="list-pagination">
<n-pagination <n-pagination
:item-count="10" :page="paginat.page"
:page-sizes="[10, 20, 30, 40]" :page-size="paginat.limit"
:item-count="paginat.count"
:page-sizes="[12, 24, 36, 48]"
@update:page="changePage"
@update:page-size="changeSize"
show-size-picker show-size-picker
/> />
</div> </div>
</div> </div>
<!-- model -->
<project-items-modal-card <project-items-modal-card
v-if="modalData" v-if="modalData"
v-model:modalShow="modalShow" v-model:modalShow="modalShow"
@ -40,9 +52,8 @@ import { useModalDataInit } from './hooks/useModal.hook'
import { useDataListInit } from './hooks/useData.hook' import { useDataListInit } from './hooks/useData.hook'
const { CopyIcon, EllipsisHorizontalCircleSharpIcon } = icon.ionicons5 const { CopyIcon, EllipsisHorizontalCircleSharpIcon } = icon.ionicons5
const { list, deleteHandle } = useDataListInit() const { modalData, modalShow, closeModal, previewHandle, resizeHandle, editHandle } = useModalDataInit()
const { modalData, modalShow, closeModal, resizeHandle, editHandle } = const { loading, paginat, list, changeSize, changePage, releaseHandle, deleteHandle } = useDataListInit()
useModalDataInit()
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -51,7 +62,7 @@ $contentHeight: 250px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
min-height: calc(100vh - #{$--header-height} * 2 - 2px); min-height: calc(100vh - #{$--header-height} - 40px - 2px);
.list-content { .list-content {
position: relative; position: relative;
height: $contentHeight; height: $contentHeight;

View File

@ -11,7 +11,7 @@
<n-space class="list-content-top go-px-0" justify="center"> <n-space class="list-content-top go-px-0" justify="center">
<n-space> <n-space>
<n-text> <n-text>
{{ cardData?.title || '' }} {{ cardData?.title || cardData?.id || '未命名' }}
</n-text> </n-text>
</n-space> </n-space>
</n-space> </n-space>
@ -26,9 +26,7 @@
<!-- 中间 --> <!-- 中间 -->
<div class="list-content-img"> <div class="list-content-img">
<img <img
:src=" :src="cardData?.image"
requireUrl('project/moke-20211219181327.png')
"
:alt="cardData?.title" :alt="cardData?.title"
/> />
</div> </div>
@ -75,10 +73,11 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive } from 'vue' import { reactive, PropType } from 'vue'
import { renderIcon, renderLang } from '@/utils' import { renderIcon, renderLang } from '@/utils'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { MacOsControlBtn } from '@/components/Tips/MacOsControlBtn' import { MacOsControlBtn } from '@/components/Tips/MacOsControlBtn'
import { Chartype } from '../../index.d'
const { HammerIcon } = icon.ionicons5 const { HammerIcon } = icon.ionicons5
@ -86,14 +85,9 @@ const emit = defineEmits(['close', 'edit'])
const props = defineProps({ const props = defineProps({
modalShow: Boolean, modalShow: Boolean,
cardData: Object cardData: Object as PropType<Chartype>
}) })
// url
const requireUrl = (name: string) => {
return new URL(`../../../../../assets/images/${name}`, import.meta.url).href
}
const fnBtnList = reactive([ const fnBtnList = reactive([
{ {
label: renderLang('global.r_edit'), label: renderLang('global.r_edit'),
@ -124,12 +118,14 @@ const closeHandle = () => {
<style lang="scss" scoped> <style lang="scss" scoped>
$padding: 30px; $padding: 30px;
$contentHeight: calc(80vh); $contentHeight: calc(80vh);
$imageHeight: calc(80vh - 110px);
$contentWidth: calc(82vw); $contentWidth: calc(82vw);
@include go('modal-box') { @include go('modal-box') {
width: $contentWidth; width: $contentWidth;
height: $contentHeight;
.list-content { .list-content {
margin-top: 28px; margin-top: 20px;
border-radius: $--border-radius-base; border-radius: $--border-radius-base;
overflow: hidden; overflow: hidden;
@include background-image('background-point'); @include background-image('background-point');
@ -144,8 +140,9 @@ $contentWidth: calc(82vw);
} }
&-img { &-img {
@extend .go-flex-center; @extend .go-flex-center;
padding: 6px 0;
img { img {
max-height: $contentHeight; height: $imageHeight;
min-height: 200px; min-height: 200px;
max-width: 100%; max-width: 100%;
@extend .go-border-radius; @extend .go-border-radius;

View File

@ -2,7 +2,10 @@ export type Chartype = {
id: number | string id: number | string
title: string // 标题 title: string // 标题
label: string // 标签 label: string // 标签
release: boolean // 0未发布 | 1已发布 time: string, // 时间
image: string, // 预览图地址
createId: string, // 创建者
release: boolean // false 未发布 | true 已发布
} }
export type ChartList = Chartype[] export type ChartList = Chartype[]

View File

@ -10,6 +10,6 @@ import { ProjectItemsList } from './components/ProjectItemsList'
<style lang="scss" scoped> <style lang="scss" scoped>
@include go(project-items) { @include go(project-items) {
padding: 30px 20px; padding: 20px 20px;
} }
</style> </style>

View File

@ -18,7 +18,7 @@
:disabled="item.disabled" :disabled="item.disabled"
v-for="item in typeList" v-for="item in typeList"
:key="item.key" :key="item.key"
@click="btnHandle" @click="btnHandle(item.key)"
> >
<component :is="item.title"></component> <component :is="item.title"></component>
<template #icon> <template #icon>
@ -35,10 +35,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch, reactive } from 'vue' import { watch } from 'vue'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { PageEnum, ChartEnum } from '@/enums/pageEnum' import { PageEnum, ChartEnum } from '@/enums/pageEnum'
import { ResultEnum } from '@/enums/httpEnum'
import { fetchPathByName, routerTurnByPath, renderLang, getUUID } from '@/utils' import { fetchPathByName, routerTurnByPath, renderLang, getUUID } from '@/utils'
import { createProjectApi } from '@/api/path'
const { FishIcon, CloseIcon } = icon.ionicons5 const { FishIcon, CloseIcon } = icon.ionicons5
const { StoreIcon, ObjectStorageIcon } = icon.carbon const { StoreIcon, ObjectStorageIcon } = icon.carbon
@ -48,7 +50,7 @@ const props = defineProps({
show: Boolean show: Boolean
}) })
const typeList = reactive([ const typeList = [
{ {
title: renderLang('project.new_project'), title: renderLang('project.new_project'),
key: ChartEnum.CHART_HOME_NAME, key: ChartEnum.CHART_HOME_NAME,
@ -67,7 +69,7 @@ const typeList = reactive([
icon: StoreIcon, icon: StoreIcon,
disabled: true disabled: true
} }
]) ]
// @on-after-leave // @on-after-leave
watch(props, newValue => { watch(props, newValue => {
@ -82,11 +84,32 @@ const closeHandle = () => {
} }
// //
const btnHandle = (key: string) => { const btnHandle = async (key: string) => {
closeHandle() switch (key) {
const id = getUUID() case ChartEnum.CHART_HOME_NAME:
try {
//
const res = await createProjectApi({
//
projectName: getUUID(),
// remarks
remarks: null,
//
indexImage: null,
}) as unknown as MyResponseType
if(res.code === ResultEnum.SUCCESS) {
window['$message'].success(window['$t']('project.create_success'))
const { id } = res.data
const path = fetchPathByName(ChartEnum.CHART_HOME_NAME, 'href') const path = fetchPathByName(ChartEnum.CHART_HOME_NAME, 'href')
routerTurnByPath(path, [id], undefined, true) routerTurnByPath(path, [id], undefined, true)
closeHandle()
}
} catch (error) {
window['$message'].error(window['$t']('project.create_failure'))
}
break;
}
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -0,0 +1,35 @@
<template>
<div class="go-redirect-un-publish">
<div class="text-center">
<img src="~@/assets/images/exception/nodata.svg" alt="" />
</div>
<div class="text-center">
<h1>当前项目暂未发布</h1>
</div>
</div>
</template>
<style lang="scss" scoped>
@include go(redirect-un-publish) {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
width: 100vw;
height: 100vh;
overflow: hidden;
padding: 100px 0;
@include background-image('background-image');
.text-center {
h1 {
color: #666;
padding: 20px 0;
}
}
img {
width: 350px;
margin: 0 auto;
}
}
</style>

View File

@ -1,9 +1,11 @@
<template> <template>
<div class="go-redirect">
<n-empty description="你什么也找不到"> <n-empty description="你什么也找不到">
<template #extra> <template #extra>
<n-button size="small" @click="goHome">看看别的</n-button> <n-button size="small" @click="goHome">看看别的</n-button>
</template> </template>
</n-empty> </n-empty>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onBeforeMount } from 'vue' import { onBeforeMount } from 'vue'
@ -14,3 +16,16 @@ const goHome = () => {
router.replace({ path: '/' }) router.replace({ path: '/' })
} }
</script> </script>
<style lang="scss" scoped>
@include go(redirect) {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
width: 100vw;
height: 100vh;
overflow: hidden;
padding: 100px 0;
@include background-image('background-image');
}
</style>

1
test
View File

@ -1 +0,0 @@
11

7
types/global.d.ts vendored
View File

@ -9,4 +9,11 @@ interface Window {
$KeyboardActive?: Set<string> $KeyboardActive?: Set<string>
} }
declare interface MyResponseType {
code: number;
msg: string;
data: any;
}
declare type Recordable<T = any> = Record<string, T> declare type Recordable<T = any> = Record<string, T>

2
types/vite-env.d.ts vendored
View File

@ -1,8 +1,6 @@
/// <reference types="vite/client" /> /// <reference types="vite/client" />
interface ImportMetaEnv { interface ImportMetaEnv {
// 标题
VITE_GLOB_APP_TITLE: string;
// 端口 // 端口
VITE_DEV_PORT: string; VITE_DEV_PORT: string;
// 开发地址 // 开发地址

View File

@ -1,8 +1,9 @@
import { defineConfig } from 'vite' import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import { resolve } from 'path' import { resolve } from 'path'
import { OUTPUT_DIR, brotliSize, chunkSizeWarningLimit, terserOptions, rollupOptions } from './build/constant' import { OUTPUT_DIR, brotliSize, chunkSizeWarningLimit, terserOptions, rollupOptions } from './build/constant'
import viteCompression from 'vite-plugin-compression' import viteCompression from 'vite-plugin-compression'
import { axiosPre } from './src/settings/httpSetting'
import { viteMockServe } from 'vite-plugin-mock' import { viteMockServe } from 'vite-plugin-mock'
import monacoEditorPlugin from 'vite-plugin-monaco-editor' import monacoEditorPlugin from 'vite-plugin-monaco-editor'
@ -10,8 +11,8 @@ function pathResolve(dir: string) {
return resolve(process.cwd(), '.', dir) return resolve(process.cwd(), '.', dir)
} }
export default defineConfig({ export default ({ mode }) => defineConfig({
base: '/', base: process.env.NODE_ENV === 'production' ? './' : '/',
// 路径重定向 // 路径重定向
resolve: { resolve: {
alias: [ alias: [
@ -35,6 +36,21 @@ export default defineConfig({
} }
} }
}, },
// 开发服务器配置
server: {
host: true,
open: true,
port: 3000,
proxy: {
[axiosPre]: {
// @ts-ignore
target: loadEnv(mode, process.cwd()).VITE_DEV_PATH,
changeOrigin: true,
ws: true,
secure: true,
}
}
},
plugins: [ plugins: [
vue(), vue(),
monacoEditorPlugin({ monacoEditorPlugin({