diff --git a/README.md b/README.md index c664b7c0..1be180fb 100644 --- a/README.md +++ b/README.md @@ -1,100 +1,201 @@ -## 总览 +<p align="center"> + <a + href="https://www.goviewlink.com?channel=mayun" + target="_blank" + style=" + padding: 10px 20px; + display: inline-block; + border-radius: 10px; + "> + <img src="readme/GoViewPro.png" alt="go-view" /> + </a> +</p> -**`master-fetch` 分支是带有后端接口请求的分支** +<p align="center"> + <img src="readme/logo-t-y.png" alt="go-view" /> +</p> -**后端项目地址:[https://gitee.com/MTrun/go-view-serve](https://gitee.com/MTrun/go-view-serve)** +<h4 align="center">开源、精美、便捷的「数据可视化」低代码开发平台</h4> -**接口说明地址:[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)** +#### 长期赞助商 -## 使用 +<div> + <div align="center" style="column-gap: 20px;"> + <a + href="http://www.ccflow.org/?from=goviewGitee" + target="_blank" + style=" + padding: 10px 20px; + display: inline-block; + border-radius: 10px; + background: #f9f9f9; + "> + <img src="readme/sponsors/ccflow-banner.png" alt="go-view" style="width: 250px;" width="250px" /> + </a> + <span> </span> + <a + href="https://fastbee.cn/" + target="_blank" + style=" + padding: 10px 20px; + display: inline-block; + border-radius: 10px; + background: #f9f9f9; + "> + <img src="readme/sponsors/fb-banner.gif" alt="go-view" style="width: 250px;" width="250px"/> + </a> + <br/> + <br/> + <a + href="https://www.qeasy.cloud/" + target="_blank" + style=" + padding: 10px 20px; + display: inline-block; + border-radius: 10px; + background: #f9f9f9; + "> + <img src="readme/sponsors/qyy-banner.png" alt="go-view" style="width: 250px;" width="250px"/> + </a> + <span> </span> + <a + href="http://doc.zyplayer.com/#/integrate/zyplayer-doc?utm=goview" + target="_blank" + style=" + padding: 10px 20px; + display: inline-block; + border-radius: 10px; + background: #f9f9f9; + "> + <img src="readme/sponsors/zyplayer-banner.png" alt="go-view" style="width: 250px;" width="250px"/> + </a> + <br/> + <br/> + <a + href="https://gitee.com/dandiankeji/icampus" + target="_blank" + style=" + padding: 10px 20px; + display: inline-block; + border-radius: 10px; + background: #f9f9f9; + "> + <img src="readme/sponsors/dandian-banner.png" alt="go-view" style="width: 250px;" width="250px"/> + </a> + <a + href="https://www.mingdao.com?s=utm_88&utm_source=Goview&utm_medium=banner&utm_campaign=gitee&utm_content=IT%E8%B5%8B%E8%83%BD%E4%B8%9A%E5%8A%A1" + target="_blank" + style=" + padding: 10px 20px; + display: inline-block; + border-radius: 10px; + background: #f9f9f9; + "> + <img src="readme/sponsors/mdy-banner.png" alt="go-view" style="width: 270px;" width="270px"/> + </a> + </div> +</div> -所有的接口地址位置:`src\api\path\*` +#### 😶 **纯前端** 分支: **`master`** -接口地址修改:`.env` +#### 👻 携带 **后端** 请求分支: **`master-fetch`** -```shell -# port -VITE_DEV_PORT = '8080' +#### 📚 GoView **文档** 地址:[https://www.mtruning.club/](https://www.mtruning.club/) -# development path -VITE_DEV_PATH = 'http://127.0.0.1:8080' +项目纯前端-Demo 地址:[https://vue.mtruning.club/](https://vue.mtruning.club/) -# production path -VITE_PRO_PATH = 'http://127.0.0.1:8080' -``` +项目带后端-Demo 地址:[https://demo.mtruning.club/](https://demo.mtruning.club/) -公共前缀修改:`src\settings\httpSetting.ts` +Cloud IDE 代码在线预览地址:[https://idegitee.com/dromara/go-view](https://idegitee.com/dromara/go-view) -```shell -// 请求前缀 -export const axiosPre = '/api/goview' -``` +#### 🤯 后端项目看这里! -接口封装:`src\api\http.ts` +后端地址(社区实现,仅供参考): -```ts -import axiosInstance from './axios' -import { RequestHttpEnum, ContentTypeEnum } from '@/enums/httpEnum' +- `JAVA` [https://gitee.com/MTrun/go-view-serve](https://gitee.com/MTrun/go-view-serve) (当前使用) +- `.NET` [https://gitee.com/sun_xiang_yu/go-view-dotnet](https://gitee.com/sun_xiang_yu/go-view-dotnet) +- `NODE` [https://gitee.com/qwdingyu/led](https://gitee.com/qwdingyu/led) +- `Docker 镜像` [https://gitee.com/AHEAD4/go-view-docker](https://gitee.com/AHEAD4/go-view-docker) +- `接口文档`[https://docs.apipost.cn](https://docs.apipost.cn/preview/5aa85d10a59d66ce/ddb813732007ad2b?target_id=84dbc5b0-158f-4bcb-8f74-793ac604ada3) (不是最新, 以前端代码为准) -export const get = (url: string, params?: object) => { - return axiosInstance({ - url: url, - method: RequestHttpEnum.GET, - params: params, - }) -} +#### 整体介绍 -export const post = (url: string, data?: object, headersType?: string) => { - return axiosInstance({ - url: url, - method: RequestHttpEnum.POST, - data: data, - headers: { - 'Content-Type': headersType || ContentTypeEnum.JSON - } - }) -} +- 框架:基于 `Vue3` 框架编写,使用 `hooks` 写法抽离部分逻辑,使代码结构更加清晰; -export const put = (url: string, data?: object, headersType?: string) => { - return axiosInstance({ - url: url, - method: RequestHttpEnum.PUT, - data: data, - headers: { - 'Content-Type': headersType || ContentTypeEnum.JSON - } - }) -} +- 类型:使用 `TypeScript` 进行类型约束,减少未知错误发生概率,可以大胆修改逻辑内容; -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 +- 存储:拥有本地记忆,部分配置项采用 `storage` 存储本地,提升使用体验; - case RequestHttpEnum.POST: - return post +- 封装:项目进行了详细的工具类封装如:路由、存储、加/解密、文件处理、主题、NaiveUI 全局方法、组件等 - case RequestHttpEnum.PUT: - return put +- 入选 NaiveUI 社区精选资源推荐:[查看 NaiveUI 推荐列表](https://www.naiveui.com/zh-CN/light/docs/community) - case RequestHttpEnum.DELETE: - return del +说明文档: + - default: - return get - } -} +工作台: + -``` +请求配置: + + +数据过滤: + + +高级事件编辑: + + +自定义组件颜色: + + +快捷主页: + + +主题色: + + +亮白主题: + + +主要技术栈为: + +| 名称 | 版本 | 名称 | 版本 | +| ------------------- | ----- | ----------- | ------ | +| Vue | 3.2.x | TypeScript4 | 4.6.x | +| Vite | 4.2.x | NaiveUI | 2.34.x | +| ECharts | 5.3.x | Pinia | 2.0.x | +| 详见 `package.json` | 😁 | 🥰 | 🤗 | + +开发环境: + +| 名称 | 版本 | 名称 | 版本 | +| ---- | ------- | ------- | ----- | +| node | 16.16.x | npm | 8.5.x | +| pnpm | 7.1.x | windows | 11 | + +已完成图表: + +| 分类 | 名称 | 名称 | 名称 | 名称 | +| ------ | ---------------- | ---------- | -------------- | ------------------------ | +| 图表 | 柱状图 | 横向柱状图 | 折线图 | 单/多 折线面积图(渐变色) | +| \* | 饼图 | 环形图 | 水球图 | 雷达图 | +| \* | NaiveUI 多种进度 | 散点图 | 对数回归散点图 | 热力图 | +| \* | 漏斗图 | 中国地图 | 高德地图 | 🦊 | +| 信息 | 文字 | 渐变文字 | 词云 | 嵌套网页 | +| \* | 图片 | 视频 | 😺 | 🐯 | +| 列表 | 滚动排名列表 | 滚动表格 | 🐮 | 🐐 | +| 小组件 | 边框-01~13 | 装饰-01~05 | 数字翻牌 | 通用时间 | +| \* | 数字计数 | 倒计时 | 时钟 | 🦁 | + +## 浏览器支持 + +开发和测试平台均在 `Google` 和最新版 `EDGE` 上完成,暂未测试 `IE11` 等其它浏览器,如有需求请自行测试与兼容。 + +## 安装 + +请查看文档:[https://www.mtruning.club/](https://www.mtruning.club/) ## 代码提交 @@ -110,10 +211,14 @@ export const http = (type?: RequestHttpEnum) => { * style: 不影响程序逻辑的代码修改 * chore: 不属于以上类型的其他类型(日常事务) -## 交流 +## 交流群 -QQ 群:1030129384 +QQ 群:881415392 - +<img width="260px" src="readme/go-view-qq.jpg" alt="QQ群" style="border-radius: 20px" /> + +## Pro 部分功能展示 + +体验地址: <a href="https://ai.goviewlink.com/" target="_blank">https://ai.goviewlink.com/</a>  diff --git a/package.json b/package.json index fddf3c69..fe72a706 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "go-view", - "version": "2.2.3", + "version": "1.2.7", "engines": { "node": ">=12.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 29444219..ed6c8d28 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,5 +1,9 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + dependencies: '@amap/amap-jsapi-loader': specifier: ^1.0.1 @@ -24,7 +28,7 @@ dependencies: version: 4.1.1 axios: specifier: ^1.4.0 - version: registry.npmmirror.com/axios@1.4.0 + version: 1.4.0 color: specifier: ^4.2.3 version: 4.2.3 @@ -1699,6 +1703,19 @@ packages: resolution: {integrity: sha512-p4DO/JXwjs8klJyJL8Q2oM4ks5fUTze/h5k10oPPKMiLe1fj3G1QMzPHNmN1Py4ycOk7WlO2DcGXv1qiESJCZA==} dev: false + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: false + + /axios@0.27.2: + resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} + dependencies: + follow-redirects: 1.15.1 + form-data: 4.0.0 + transitivePeerDependencies: + - debug + dev: false + /babel-plugin-import@1.13.5: resolution: {integrity: sha512-IkqnoV+ov1hdJVofly9pXRJmeDm9EtROfrc5i6eII0Hix2xMs5FEm8FG3ExMvazbnZBbgHIt6qdO8And6lCloQ==} dependencies: @@ -4331,6 +4348,10 @@ packages: asap: 2.0.6 dev: true + /proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: false + /pug-attrs@3.0.0: resolution: {integrity: sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==} dependencies: diff --git a/preview.yml b/preview.yml new file mode 100644 index 00000000..1a26db2c --- /dev/null +++ b/preview.yml @@ -0,0 +1,10 @@ +# preview.yml +autoOpen: true # 打开工作空间时是否自动开启所有应用的预览 +apps: + - port: 3000 # 应用的端口 + run: npm i --registry=https://registry.npmmirror.com && npm run dev # 应用的启动命令 + command: # 使用此命令启动服务,且不执行run + root: ./ # 应用的启动目录 + name: GoView # 应用名称 + description: 开源、精美、便捷的「数据可视化」低代码开发平台 # 应用描述 + autoOpen: true # 打开工作空间时是否自动开启预览(优先级高于根级 autoOpen) \ No newline at end of file diff --git a/readme/GoViewPro.png b/readme/GoViewPro.png new file mode 100644 index 00000000..75ef89cf Binary files /dev/null and b/readme/GoViewPro.png differ diff --git a/readme/go-view-doc.png b/readme/go-view-doc.png new file mode 100644 index 00000000..0e5187c5 Binary files /dev/null and b/readme/go-view-doc.png differ diff --git a/readme/go-view-echarts-color.png b/readme/go-view-echarts-color.png new file mode 100644 index 00000000..73eb2125 Binary files /dev/null and b/readme/go-view-echarts-color.png differ diff --git a/readme/go-view-event.png b/readme/go-view-event.png new file mode 100644 index 00000000..8752bce3 Binary files /dev/null and b/readme/go-view-event.png differ diff --git a/readme/go-view-indexpage.png b/readme/go-view-indexpage.png new file mode 100644 index 00000000..ba0dc202 Binary files /dev/null and b/readme/go-view-indexpage.png differ diff --git a/readme/go-view-qq.jpg b/readme/go-view-qq.jpg new file mode 100644 index 00000000..20435951 Binary files /dev/null and b/readme/go-view-qq.jpg differ diff --git a/readme/go-view-qq.png b/readme/go-view-qq.png deleted file mode 100644 index daeed712..00000000 Binary files a/readme/go-view-qq.png and /dev/null differ diff --git a/readme/go-view-theme.png b/readme/go-view-theme.png new file mode 100644 index 00000000..0f21f1a4 Binary files /dev/null and b/readme/go-view-theme.png differ diff --git a/readme/logo-poster.png b/readme/logo-poster.png index 1a6a32b4..94258aa6 100644 Binary files a/readme/logo-poster.png and b/readme/logo-poster.png differ diff --git a/readme/sponsors/ccflow-banner.png b/readme/sponsors/ccflow-banner.png new file mode 100644 index 00000000..7f537a70 Binary files /dev/null and b/readme/sponsors/ccflow-banner.png differ diff --git a/readme/sponsors/dandian-banner.png b/readme/sponsors/dandian-banner.png new file mode 100644 index 00000000..77b8a4a6 Binary files /dev/null and b/readme/sponsors/dandian-banner.png differ diff --git a/readme/sponsors/fb-banner.gif b/readme/sponsors/fb-banner.gif new file mode 100644 index 00000000..811bbc37 Binary files /dev/null and b/readme/sponsors/fb-banner.gif differ diff --git a/readme/sponsors/mdy-banner.png b/readme/sponsors/mdy-banner.png new file mode 100644 index 00000000..d9a55d1d Binary files /dev/null and b/readme/sponsors/mdy-banner.png differ diff --git a/readme/sponsors/qyy-banner.png b/readme/sponsors/qyy-banner.png new file mode 100644 index 00000000..04e4fefe Binary files /dev/null and b/readme/sponsors/qyy-banner.png differ diff --git a/readme/sponsors/zyplayer-banner.png b/readme/sponsors/zyplayer-banner.png new file mode 100644 index 00000000..40dcf2a4 Binary files /dev/null and b/readme/sponsors/zyplayer-banner.png differ diff --git a/src/api/http.ts b/src/api/http.ts index 71c152ae..58d1a994 100644 --- a/src/api/http.ts +++ b/src/api/http.ts @@ -150,7 +150,6 @@ export const customizeHttp = (targetParams: RequestConfigType, globalParams: Req params = translateStr(params) // form 类型处理 let formData: FormData = new FormData() - formData.set('default', 'defaultData') // 类型处理 switch (requestParamsBodyType) { diff --git a/src/assets/images/chart/charts/bar_line.png b/src/assets/images/chart/charts/bar_line.png new file mode 100644 index 00000000..441fff93 Binary files /dev/null and b/src/assets/images/chart/charts/bar_line.png differ diff --git a/src/assets/images/chart/decorates/fullScreen.png b/src/assets/images/chart/decorates/fullScreen.png new file mode 100644 index 00000000..04167ed6 Binary files /dev/null and b/src/assets/images/chart/decorates/fullScreen.png differ diff --git a/src/assets/images/chart/informations/inputs_pagination.png b/src/assets/images/chart/informations/inputs_pagination.png new file mode 100644 index 00000000..e326ef00 Binary files /dev/null and b/src/assets/images/chart/informations/inputs_pagination.png differ diff --git a/src/assets/images/chart/tables/tables_basic.png b/src/assets/images/chart/tables/tables_basic.png new file mode 100644 index 00000000..16525ca1 Binary files /dev/null and b/src/assets/images/chart/tables/tables_basic.png differ diff --git a/src/components/Pages/ChartItemSetting/GlobalSetting.vue b/src/components/Pages/ChartItemSetting/GlobalSetting.vue index 81f9d3d7..68e3fb00 100644 --- a/src/components/Pages/ChartItemSetting/GlobalSetting.vue +++ b/src/components/Pages/ChartItemSetting/GlobalSetting.vue @@ -391,8 +391,12 @@ const visualMap = computed(() => { // 监听legend color颜色改变type = scroll的颜色 watch(() => legend.value && legend.value.textStyle.color, (newVal) => { if (legend.value && newVal) { - legend.value.pageTextStyle.color = newVal - } + if (!legend.value.pageTextStyle) { + legend.value.pageTextStyle = { color: newVal } + } else { + legend.value.pageTextStyle.color = newVal + } + } }, { immediate: true, deep: true, diff --git a/src/components/Pages/ChartItemSetting/StylesSetting.vue b/src/components/Pages/ChartItemSetting/StylesSetting.vue index 27275449..efb1d653 100644 --- a/src/components/Pages/ChartItemSetting/StylesSetting.vue +++ b/src/components/Pages/ChartItemSetting/StylesSetting.vue @@ -69,6 +69,22 @@ </setting-item> </setting-item-box> + <!-- 预设滤镜 --> + <div v-if="presetImageList.length" class="preset-filter"> + <n-image + class="preset-img" + width="46" + preview-disabled + object-fit="scale-down" + v-for="(item, index) in presetImageList" + :key="index" + :class="{ 'active-preset': item.hueRotate === chartStyles.hueRotate }" + :style="{ filter: `hue-rotate(${item.hueRotate}deg)` }" + :src="item.src" + @click="() => (chartStyles.hueRotate = item.hueRotate)" + ></n-image> + </div> + <!-- 混合模式 --> <setting-item-box v-if="!isCanvas" :alone="true"> <template #name> @@ -149,10 +165,12 @@ </template> <script setup lang="ts"> -import { PropType } from 'vue' +import { ref, PropType } from 'vue' import { PickCreateComponentType, BlendModeEnumList } from '@/packages/index.d' import { SettingItemBox, SettingItem, CollapseItem } from '@/components/Pages/ChartItemSetting' import { icon } from '@/plugins' +import logoImg from '@/assets/logo.png' +import { useDesignStore } from '@/store/modules/designStore/designStore' const props = defineProps({ isGroup: { @@ -175,10 +193,48 @@ const { HelpOutlineIcon } = icon.ionicons5 const sliderFormatTooltip = (v: string) => { return `${(parseFloat(v) * 100).toFixed(0)}%` } + // 角度格式化 const degFormatTooltip = (v: string) => { return `${v}deg` } + +// 预设滤镜 +interface presetImageData { + index: number + src: string + hueRotate: number +} + +const presetImageList = ref([] as presetImageData[]) +for (let i = 1; i <= 12; i++) { + presetImageList.value.push({ + index: i, + src: logoImg, + hueRotate: i * 30 + }) +} </script> -<style lang="scss" scoped></style> +<style lang="scss" scoped> +// 预设滤镜 +.preset-filter { + margin: 20px 0 10px 0; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + .preset-img { + margin-bottom: 10px; + padding: 2px; + border-radius: 6px; + transition: 0.2s all; + cursor: pointer; + &:hover { + box-shadow: 0 0 0 2px #66a9c9; + } + } + .active-preset { + box-shadow: 0 0 0 2px #66a9c9; + } +} +</style> diff --git a/src/hooks/useChartDataFetch.hook.ts b/src/hooks/useChartDataFetch.hook.ts index c39cc34e..5fc84554 100644 --- a/src/hooks/useChartDataFetch.hook.ts +++ b/src/hooks/useChartDataFetch.hook.ts @@ -90,7 +90,7 @@ export const useChartDataFetch = ( // 普通初始化与组件交互处理监听 watch( - () => targetComponent.request, + () => targetComponent.request.requestParams, () => { fetchFn() }, @@ -105,7 +105,11 @@ export const useChartDataFetch = ( // 单位 const unit = targetInterval && targetInterval.value ? targetUnit.value : globalUnit.value // 开启轮询 - if (time) fetchInterval = setInterval(fetchFn, intervalUnitHandle(time, unit)) + if (time) { + fetchInterval = setInterval(fetchFn, intervalUnitHandle(time, unit)) + } else { + fetchFn() + } } // eslint-disable-next-line no-empty } catch (error) { @@ -114,10 +118,11 @@ export const useChartDataFetch = ( } if (isPreview()) { - // 判断是否是数据池类型 targetComponent.request.requestDataType === RequestDataTypeEnum.Pond ? addGlobalDataInterface(targetComponent, useChartEditStore, updateCallback || echartsUpdateHandle) : requestIntervalFn() + } else { + requestIntervalFn() } return { vChartRef } } diff --git a/src/hooks/useChartDataPondFetch.hook.ts b/src/hooks/useChartDataPondFetch.hook.ts index 24ac7660..a506f274 100644 --- a/src/hooks/useChartDataPondFetch.hook.ts +++ b/src/hooks/useChartDataPondFetch.hook.ts @@ -1,4 +1,4 @@ -import { toRaw } from 'vue' +import { toRaw, watch, computed, ComputedRef } from 'vue' import { customizeHttp } from '@/api/http' import { CreateComponentType } from '@/packages/index.d' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' @@ -20,7 +20,7 @@ const mittDataPondMap = new Map<string, DataPondMapType[]>() // 创建单个数据项轮询接口 const newPondItemInterval = ( requestGlobalConfig: RequestGlobalConfigType, - requestDataPondItem: RequestDataPondItemType, + requestDataPondItem: ComputedRef<RequestDataPondItemType>, dataPondMapItem?: DataPondMapType[] ) => { if (!dataPondMapItem) return @@ -31,8 +31,7 @@ const newPondItemInterval = ( // 请求 const fetchFn = async () => { try { - const res = await customizeHttp(toRaw(requestDataPondItem.dataPondRequestConfig), toRaw(requestGlobalConfig)) - + const res = await customizeHttp(toRaw(requestDataPondItem.value.dataPondRequestConfig), toRaw(requestGlobalConfig)) if (res) { try { // 遍历更新回调函数 @@ -49,19 +48,32 @@ const newPondItemInterval = ( } } + watch( + () => requestDataPondItem.value.dataPondRequestConfig.requestParams.Params, + () => { + fetchFn() + }, + { + immediate: false, + deep: true + } + ) + + // 立即调用 fetchFn() - const targetInterval = requestDataPondItem.dataPondRequestConfig.requestInterval - const targetUnit = requestDataPondItem.dataPondRequestConfig.requestIntervalUnit + + const targetInterval = requestDataPondItem.value.dataPondRequestConfig.requestInterval + const targetUnit = requestDataPondItem.value.dataPondRequestConfig.requestIntervalUnit const globalRequestInterval = requestGlobalConfig.requestInterval const globalUnit = requestGlobalConfig.requestIntervalUnit // 定时时间 - const time = targetInterval ? targetInterval : globalRequestInterval + const time = targetInterval ? targetInterval : globalRequestInterval // 单位 - const unit = targetInterval ? targetUnit : globalUnit + const unit = targetInterval ? targetUnit : globalUnit // 开启轮询 if (time) fetchInterval = setInterval(fetchFn, intervalUnitHandle(time, unit)) } @@ -96,13 +108,16 @@ export const useChartDataPondFetch = () => { } // 初始化数据池 - const initDataPond = (requestGlobalConfig: RequestGlobalConfigType) => { - const { requestDataPond } = requestGlobalConfig + const initDataPond = (useChartEditStore: ChartEditStoreType) => { + const { requestGlobalConfig } = useChartEditStore() + const chartEditStore = useChartEditStore() // 根据 mapId 查找对应的数据池配置 for (let pondKey of mittDataPondMap.keys()) { - const requestDataPondItem = requestDataPond.find(item => item.dataPondId === pondKey) + const requestDataPondItem = computed(() => { + return requestGlobalConfig.requestDataPond.find(item => item.dataPondId === pondKey) + }) as ComputedRef<RequestDataPondItemType> if (requestDataPondItem) { - newPondItemInterval(requestGlobalConfig, requestDataPondItem, mittDataPondMap.get(pondKey)) + newPondItemInterval(chartEditStore.requestGlobalConfig, requestDataPondItem, mittDataPondMap.get(pondKey)) } } } diff --git a/src/hooks/useChartInteract.hook.ts b/src/hooks/useChartInteract.hook.ts index efa2ae73..dce0d25b 100644 --- a/src/hooks/useChartInteract.hook.ts +++ b/src/hooks/useChartInteract.hook.ts @@ -1,4 +1,5 @@ import { toRefs } from 'vue' +import { isPreview } from '@/utils' import { CreateComponentType } from '@/packages/index.d' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' @@ -12,6 +13,7 @@ export const useChartInteract = ( param: { [T: string]: any }, interactEventOn: string ) => { + if (!isPreview()) return const chartEditStore = useChartEditStore() const { interactEvents } = chartConfig.events const fnOnEvent = interactEvents.filter(item => { @@ -20,20 +22,37 @@ export const useChartInteract = ( if (fnOnEvent.length === 0) return fnOnEvent.forEach(item => { - const index = chartEditStore.fetchTargetIndex(item.interactComponentId) - if (index === -1) return - const { Params, Header } = toRefs(chartEditStore.componentList[index].request.requestParams) - Object.keys(item.interactFn).forEach(key => { - if (Params.value[key]) { - Params.value[key] = param[item.interactFn[key]] - } - if (Header.value[key]) { - Header.value[key] = param[item.interactFn[key]] - } - }) + + const globalConfigPindAprndex = chartEditStore.requestGlobalConfig.requestDataPond.findIndex(cItem => + cItem.dataPondId === item.interactComponentId + ) + if (globalConfigPindAprndex !== -1) { + const { Params, Header } = toRefs(chartEditStore.requestGlobalConfig.requestDataPond[globalConfigPindAprndex].dataPondRequestConfig.requestParams) + + Object.keys(item.interactFn).forEach(key => { + if (key in Params.value) { + Params.value[key] = param[item.interactFn[key]] + } + if (key in Header.value) { + Header.value[key] = param[item.interactFn[key]] + } + }) + } else { + const index = chartEditStore.fetchTargetIndex(item.interactComponentId) + if (index === -1) return + const { Params, Header } = toRefs(chartEditStore.componentList[index].request.requestParams) + + Object.keys(item.interactFn).forEach(key => { + if (key in Params.value) { + Params.value[key] = param[item.interactFn[key]] + } + if (key in Header.value) { + Header.value[key] = param[item.interactFn[key]] + } + }) + } }) } - // 联动事件触发的 type 变更时,清除当前绑定内容 export const clearInteractEvent = (chartConfig: CreateComponentType) => { diff --git a/src/hooks/useLifeHandler.hook.ts b/src/hooks/useLifeHandler.hook.ts index 2412a79a..d17184d0 100644 --- a/src/hooks/useLifeHandler.hook.ts +++ b/src/hooks/useLifeHandler.hook.ts @@ -48,10 +48,10 @@ export const useLifeHandler = (chartConfig: CreateComponentType | CreateComponen try { return new Function(` return ( - async function(mouseEvent){ + async function(components,mouseEvent){ ${fnStr} } - )`)() + )`)().bind(undefined,components) } catch (error) { console.error(error) } diff --git a/src/hooks/usePreviewScale.hook.ts b/src/hooks/usePreviewScale.hook.ts index 066f6b66..9d8bcb78 100644 --- a/src/hooks/usePreviewScale.hook.ts +++ b/src/hooks/usePreviewScale.hook.ts @@ -1,218 +1,218 @@ -import throttle from 'lodash/throttle' - -// 拆出来是为了更好的分离单独复用 - -// * 屏幕缩放适配(两边留白) -export const usePreviewFitScale = ( - width: number, - height: number, - scaleDom: HTMLElement | null, - callback?: (scale: { - width: number; - height: number; - }) => void -) => { - // * 画布尺寸(px) - const baseWidth = width - const baseHeight = height - - // * 默认缩放值 - const scale = { - width: 1, - height: 1, - } - - // * 需保持的比例 - const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5)) - const calcRate = () => { - // 当前屏幕宽高比 - const currentRate = parseFloat( - (window.innerWidth / window.innerHeight).toFixed(5) - ) - if (scaleDom) { - if (currentRate > baseProportion) { - // 表示更宽 - scale.width = parseFloat(((window.innerHeight * baseProportion) / baseWidth).toFixed(5)) - scale.height = parseFloat((window.innerHeight / baseHeight).toFixed(5)) - scaleDom.style.transform = `scale(${scale.width}, ${scale.height})` - } else { - // 表示更高 - scale.height = parseFloat(((window.innerWidth / baseProportion) / baseHeight).toFixed(5)) - scale.width = parseFloat((window.innerWidth / baseWidth).toFixed(5)) - scaleDom.style.transform = `scale(${scale.width}, ${scale.height})` - } - if (callback) callback(scale) - } - } - - const resize = throttle(() => { - calcRate() - }, 200) - - // * 改变窗口大小重新绘制 - const windowResize = () => { - window.addEventListener('resize', resize) - } - - // * 改变窗口大小重新绘制 - const unWindowResize = () => { - window.removeEventListener('resize', resize) - } - - return { - calcRate, - windowResize, - unWindowResize, - } -} - -// * X轴撑满,Y轴滚动条 -export const usePreviewScrollYScale = ( - width: number, - height: number, - scaleDom: HTMLElement | null, - callback?: (scale: { - width: number; - height: number; - }) => void -) => { - // * 画布尺寸(px) - const baseWidth = width - const baseHeight = height - - // * 默认缩放值 - const scale = { - width: 1, - height: 1, - } - - // * 需保持的比例 - const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5)) - const calcRate = () => { - if (scaleDom) { - scale.height = parseFloat(((window.innerWidth / baseProportion) / baseHeight).toFixed(5)) - scale.width = parseFloat((window.innerWidth / baseWidth).toFixed(5)) - scaleDom.style.transform = `scale(${scale.width}, ${scale.height})` - if (callback) callback(scale) - } - } - - const resize = throttle(() => { - calcRate() - }, 200) - - // * 改变窗口大小重新绘制 - const windowResize = () => { - window.addEventListener('resize', resize) - } - - // * 改变窗口大小重新绘制 - const unWindowResize = () => { - window.removeEventListener('resize', resize) - } - - return { - calcRate, - windowResize, - unWindowResize, - } -} - -// * Y轴撑满,X轴滚动条 -export const usePreviewScrollXScale = ( - width: number, - height: number, - scaleDom: HTMLElement | null, - callback?: (scale: { - width: number; - height: number; - }) => void -) => { - // * 画布尺寸(px) - const baseWidth = width - const baseHeight = height - - // * 默认缩放值 - const scale = { - height: 1, - width: 1, - } - - // * 需保持的比例 - const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5)) - const calcRate = () => { - if (scaleDom) { - scale.width = parseFloat(((window.innerHeight * baseProportion) / baseWidth).toFixed(5)) - scale.height = parseFloat((window.innerHeight / baseHeight).toFixed(5)) - scaleDom.style.transform = `scale(${scale.width}, ${scale.height})` - if (callback) callback(scale) - } - } - - const resize = throttle(() => { - calcRate() - }, 200) - - // * 改变窗口大小重新绘制 - const windowResize = () => { - window.addEventListener('resize', resize) - } - - // * 改变窗口大小重新绘制 - const unWindowResize = () => { - window.removeEventListener('resize', resize) - } - - return { - calcRate, - windowResize, - unWindowResize, - } -} - -// * 变形内容,宽高铺满 -export const usePreviewFullScale = ( - width: number, - height: number, - scaleDom: HTMLElement | null, - callback?: (scale: { - width: number; - height: number; - }) => void -) => { - - // * 默认缩放值 - const scale = { - width: 1, - height: 1, - } - - const calcRate = () => { - if (scaleDom) { - scale.width = parseFloat((window.innerWidth / width).toFixed(5)) - scale.height = parseFloat((window.innerHeight / height).toFixed(5)) - scaleDom.style.transform = `scale(${scale.width}, ${scale.height})` - if (callback) callback(scale) - } - } - - const resize = throttle(() => { - calcRate() - }, 200) - - // * 改变窗口大小重新绘制 - const windowResize = () => { - window.addEventListener('resize', resize) - } - - // * 改变窗口大小重新绘制 - const unWindowResize = () => { - window.removeEventListener('resize', resize) - } - - return { - calcRate, - windowResize, - unWindowResize, - } +import throttle from 'lodash/throttle' + +// 拆出来是为了更好的分离单独复用 + +// * 屏幕缩放适配(两边留白) +export const usePreviewFitScale = ( + width: number, + height: number, + scaleDom: HTMLElement | null, + callback?: (scale: { + width: number; + height: number; + }) => void +) => { + // * 画布尺寸(px) + const baseWidth = width + const baseHeight = height + + // * 默认缩放值 + const scale = { + width: 1, + height: 1, + } + + // * 需保持的比例 + const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5)) + const calcRate = () => { + // 当前屏幕宽高比 + const currentRate = parseFloat( + (window.innerWidth / window.innerHeight).toFixed(5) + ) + if (scaleDom) { + if (currentRate > baseProportion) { + // 表示更宽 + scale.width = parseFloat(((window.innerHeight * baseProportion) / baseWidth).toFixed(5)) + scale.height = parseFloat((window.innerHeight / baseHeight).toFixed(5)) + scaleDom.style.transform = `scale(${scale.width}, ${scale.height})` + } else { + // 表示更高 + scale.height = parseFloat(((window.innerWidth / baseProportion) / baseHeight).toFixed(5)) + scale.width = parseFloat((window.innerWidth / baseWidth).toFixed(5)) + scaleDom.style.transform = `scale(${scale.width}, ${scale.height})` + } + if (callback) callback(scale) + } + } + + const resize = throttle(() => { + calcRate() + }, 200) + + // * 改变窗口大小重新绘制 + const windowResize = () => { + window.addEventListener('resize', resize) + } + + // * 卸载监听 + const unWindowResize = () => { + window.removeEventListener('resize', resize) + } + + return { + calcRate, + windowResize, + unWindowResize, + } +} + +// * X轴撑满,Y轴滚动条 +export const usePreviewScrollYScale = ( + width: number, + height: number, + scaleDom: HTMLElement | null, + callback?: (scale: { + width: number; + height: number; + }) => void +) => { + // * 画布尺寸(px) + const baseWidth = width + const baseHeight = height + + // * 默认缩放值 + const scale = { + width: 1, + height: 1, + } + + // * 需保持的比例 + const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5)) + const calcRate = () => { + if (scaleDom) { + scale.height = parseFloat(((window.innerWidth / baseProportion) / baseHeight).toFixed(5)) + scale.width = parseFloat((window.innerWidth / baseWidth).toFixed(5)) + scaleDom.style.transform = `scale(${scale.width}, ${scale.height})` + if (callback) callback(scale) + } + } + + const resize = throttle(() => { + calcRate() + }, 200) + + // * 改变窗口大小重新绘制 + const windowResize = () => { + window.addEventListener('resize', resize) + } + + // * 卸载监听 + const unWindowResize = () => { + window.removeEventListener('resize', resize) + } + + return { + calcRate, + windowResize, + unWindowResize, + } +} + +// * Y轴撑满,X轴滚动条 +export const usePreviewScrollXScale = ( + width: number, + height: number, + scaleDom: HTMLElement | null, + callback?: (scale: { + width: number; + height: number; + }) => void +) => { + // * 画布尺寸(px) + const baseWidth = width + const baseHeight = height + + // * 默认缩放值 + const scale = { + height: 1, + width: 1, + } + + // * 需保持的比例 + const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5)) + const calcRate = () => { + if (scaleDom) { + scale.width = parseFloat(((window.innerHeight * baseProportion) / baseWidth).toFixed(5)) + scale.height = parseFloat((window.innerHeight / baseHeight).toFixed(5)) + scaleDom.style.transform = `scale(${scale.width}, ${scale.height})` + if (callback) callback(scale) + } + } + + const resize = throttle(() => { + calcRate() + }, 200) + + // * 改变窗口大小重新绘制 + const windowResize = () => { + window.addEventListener('resize', resize) + } + + // * 卸载监听 + const unWindowResize = () => { + window.removeEventListener('resize', resize) + } + + return { + calcRate, + windowResize, + unWindowResize, + } +} + +// * 变形内容,宽高铺满 +export const usePreviewFullScale = ( + width: number, + height: number, + scaleDom: HTMLElement | null, + callback?: (scale: { + width: number; + height: number; + }) => void +) => { + + // * 默认缩放值 + const scale = { + width: 1, + height: 1, + } + + const calcRate = () => { + if (scaleDom) { + scale.width = parseFloat((window.innerWidth / width).toFixed(5)) + scale.height = parseFloat((window.innerHeight / height).toFixed(5)) + scaleDom.style.transform = `scale(${scale.width}, ${scale.height})` + if (callback) callback(scale) + } + } + + const resize = throttle(() => { + calcRate() + }, 200) + + // * 改变窗口大小重新绘制 + const windowResize = () => { + window.addEventListener('resize', resize) + } + + // * 卸载监听 + const unWindowResize = () => { + window.removeEventListener('resize', resize) + } + + return { + calcRate, + windowResize, + unWindowResize, + } } \ No newline at end of file diff --git a/src/i18n/en/project.ts b/src/i18n/en/project.ts index dae8146d..6695cd5e 100644 --- a/src/i18n/en/project.ts +++ b/src/i18n/en/project.ts @@ -1,8 +1,6 @@ export default { - create_btn: 'Creat', - create_success: 'Creat Success!', - create_failure: 'Failed to create, please try again later!', - create_tip: 'Please select a content for development!', + create_btn: 'Create', + create_tip: 'Please select a content for development', project: 'Project', my: 'My', new_project: 'New Project', diff --git a/src/packages/components/Charts/Bars/BarLine/config.ts b/src/packages/components/Charts/Bars/BarLine/config.ts new file mode 100644 index 00000000..4c6c1805 --- /dev/null +++ b/src/packages/components/Charts/Bars/BarLine/config.ts @@ -0,0 +1,75 @@ +import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public' +import { BarLineConfig } from './index' +import { CreateComponentType } from '@/packages/index.d' +import cloneDeep from 'lodash/cloneDeep' +import dataJson from './data.json' + +export const includes = ['legend', 'xAxis', 'yAxis', 'grid'] +// 柱状折线组合图 分别定义series +// 写死name可以定义legend显示的名称 +export const barSeriesItem = { + type: 'bar', + barWidth: 15, + label: { + show: true, + position: 'top', + color: '#fff', + fontSize: 12 + }, + itemStyle: { + color: null, + borderRadius: 2 + } +} + +export const lineSeriesItem = { + type: 'line', + symbol: 'circle', + label: { + show: true, + position: 'top', + color: '#fff', + fontSize: 12 + }, + symbolSize: 5, //设定实心点的大小 + itemStyle: { + color: '#FFE47A', + borderWidth: 1 + }, + lineStyle: { + type: 'solid', + width: 3, + color: null + } +} + +export const option = { + tooltip: { + show: true, + trigger: 'axis', + axisPointer: { + show: true, + type: 'shadow' + } + }, + legend: { + data: null + }, + xAxis: { + show: true, + type: 'category' + }, + yAxis: { + show: true, + type: 'value' + }, + dataset: { ...dataJson }, + series: [barSeriesItem, lineSeriesItem] +} + +export default class Config extends PublicConfigClass implements CreateComponentType { + public key = BarLineConfig.key + public chartConfig = cloneDeep(BarLineConfig) + // 图表配置项 + public option = echartOptionProfixHandle(option, includes) +} diff --git a/src/packages/components/Charts/Bars/BarLine/config.vue b/src/packages/components/Charts/Bars/BarLine/config.vue new file mode 100644 index 00000000..6a76ebdb --- /dev/null +++ b/src/packages/components/Charts/Bars/BarLine/config.vue @@ -0,0 +1,93 @@ +<template> + <!-- Echarts 全局设置 --> + <global-setting :optionData="optionData"></global-setting> + <CollapseItem + v-for="(item, index) in seriesList" + :key="index" + :name="`${item.type == 'bar' ? '柱状图' : '折线图'}`" + :expanded="true" + > + <SettingItemBox name="图形" v-if="item.type == 'bar'"> + <SettingItem name="宽度"> + <n-input-number + v-model:value="item.barWidth" + :min="1" + :max="100" + size="small" + placeholder="自动计算" + ></n-input-number> + </SettingItem> + <SettingItem name="圆角"> + <n-input-number v-model:value="item.itemStyle.borderRadius" :min="0" size="small"></n-input-number> + </SettingItem> + </SettingItemBox> + <SettingItemBox name="线条" v-if="item.type == 'line'"> + <SettingItem name="宽度"> + <n-input-number + v-model:value="item.lineStyle.width" + :min="1" + :max="100" + size="small" + placeholder="自动计算" + ></n-input-number> + </SettingItem> + <SettingItem name="类型"> + <n-select v-model:value="item.lineStyle.type" size="small" :options="lineConf.lineStyle.type"></n-select> + </SettingItem> + </SettingItemBox> + <SettingItemBox name="实心点" v-if="item.type == 'line'"> + <SettingItem name="大小"> + <n-input-number + v-model:value="item.symbolSize" + :min="1" + :max="100" + size="small" + placeholder="自动计算" + ></n-input-number> + </SettingItem> + </SettingItemBox> + <setting-item-box name="标签"> + <setting-item> + <n-space> + <n-switch v-model:value="item.label.show" size="small" /> + <n-text>展示标签</n-text> + </n-space> + </setting-item> + <setting-item name="大小"> + <n-input-number v-model:value="item.label.fontSize" size="small" :min="1"></n-input-number> + </setting-item> + <setting-item name="tip颜色"> + <n-color-picker size="small" :modes="['hex']" v-model:value="item.label.color"></n-color-picker> + </setting-item> + <setting-item name="位置"> + <n-select + v-model:value="item.label.position" + :options="[ + { label: 'top', value: 'top' }, + { label: 'left', value: 'left' }, + { label: 'right', value: 'right' }, + { label: 'bottom', value: 'bottom' } + ]" + /> + </setting-item> + </setting-item-box> + </CollapseItem> +</template> + +<script setup lang="ts"> +import { PropType, computed } from 'vue' +import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' +import { lineConf } from '@/packages/chartConfiguration/echarts' +import { GlobalThemeJsonType } from '@/settings/chartThemes' + +const props = defineProps({ + optionData: { + type: Object as PropType<GlobalThemeJsonType>, + required: true + } +}) + +const seriesList = computed(() => { + return props.optionData.series +}) +</script> diff --git a/src/packages/components/Charts/Bars/BarLine/data.json b/src/packages/components/Charts/Bars/BarLine/data.json new file mode 100644 index 00000000..4b6ee3b4 --- /dev/null +++ b/src/packages/components/Charts/Bars/BarLine/data.json @@ -0,0 +1,40 @@ +{ + "dimensions": ["product", "data1", "data2"], + "source": [ + { + "product": "1月", + "data1": 104, + "data2": 30 + }, + { + "product": "2月", + "data1": 56, + "data2": 56 + }, + { + "product": "3月", + "data1": 136, + "data2": 36 + }, + { + "product": "4月", + "data1": 86, + "data2": 6 + }, + { + "product": "5月", + "data1": 98, + "data2": 10 + }, + { + "product": "6月", + "data1": 86, + "data2": 70 + }, + { + "product": "7月", + "data1": 77, + "data2": 57 + } + ] +} diff --git a/src/packages/components/Charts/Bars/BarLine/index.ts b/src/packages/components/Charts/Bars/BarLine/index.ts new file mode 100644 index 00000000..03ff28ff --- /dev/null +++ b/src/packages/components/Charts/Bars/BarLine/index.ts @@ -0,0 +1,16 @@ +// 公共类型声明 +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d' +// 当前[信息模块]分类声明 +import { ChatCategoryEnum,ChatCategoryEnumName } from '../../index.d' + +export const BarLineConfig: ConfigType = { + key: 'BarLine', + chartKey: 'VBarLine', + conKey: 'VCBarLine', + title: '柱状图 & 折线图', + category: ChatCategoryEnum.BAR, + categoryName: ChatCategoryEnumName.BAR, + package: PackagesCategoryEnum.CHARTS, + chartFrame: ChartFrameEnum.ECHARTS, + image: 'bar_line.png' +} \ No newline at end of file diff --git a/src/packages/components/Charts/Bars/BarLine/index.vue b/src/packages/components/Charts/Bars/BarLine/index.vue new file mode 100644 index 00000000..2f221a55 --- /dev/null +++ b/src/packages/components/Charts/Bars/BarLine/index.vue @@ -0,0 +1,73 @@ +<template> + <v-chart + ref="vChartRef" + :init-options="initOptions" + :theme="themeColor" + :option="option" + :manual-update="isPreview()" + autoresize + ></v-chart> +</template> + +<script setup lang="ts"> +import { ref, computed, watch, PropType, nextTick } from 'vue' +import VChart from 'vue-echarts' +import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook' +import { use } from 'echarts/core' +import { CanvasRenderer } from 'echarts/renderers' +//引入柱状图 折线图 +import { BarChart, LineChart } from 'echarts/charts' +import config, { includes, barSeriesItem, lineSeriesItem } from './config' +import { mergeTheme } from '@/packages/public/chart' +import { useChartDataFetch } from '@/hooks' +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' +import { isPreview } from '@/utils' +import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components' + +const props = defineProps({ + themeSetting: { + type: Object, + required: true + }, + themeColor: { + type: Object, + required: true + }, + chartConfig: { + type: Object as PropType<config>, + required: true + } +}) + +const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting) + +use([DatasetComponent, CanvasRenderer, BarChart, LineChart, GridComponent, TooltipComponent, LegendComponent]) + +const replaceMergeArr = ref<string[]>() + +const option = computed(() => { + return mergeTheme(props.chartConfig.option, props.themeSetting, includes) +}) + +watch( + () => props.chartConfig.option.dataset, + (newData, oldData) => { + if (newData.dimensions.length !== oldData.dimensions.length) { + const seriesArr = [] + for (let i = 0; i < newData.dimensions.length - 1; i++) { + seriesArr.push(barSeriesItem, lineSeriesItem) + } + replaceMergeArr.value = ['series'] + props.chartConfig.option.series = seriesArr + nextTick(() => { + replaceMergeArr.value = [] + }) + } + }, + { + deep: false + } +) + +const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore) +</script> diff --git a/src/packages/components/Charts/Bars/index.ts b/src/packages/components/Charts/Bars/index.ts index a101acbd..4ab9a776 100644 --- a/src/packages/components/Charts/Bars/index.ts +++ b/src/packages/components/Charts/Bars/index.ts @@ -1,5 +1,6 @@ -import { BarCommonConfig } from './BarCommon/index' -import { BarCrossrangeConfig } from './BarCrossrange/index' -import { CapsuleChartConfig } from './CapsuleChart/index' - -export default [BarCommonConfig, BarCrossrangeConfig, CapsuleChartConfig] +import { BarCommonConfig } from './BarCommon/index' +import { BarCrossrangeConfig } from './BarCrossrange/index' +import { CapsuleChartConfig } from './CapsuleChart/index' +import { BarLineConfig } from './BarLine/index' + +export default [BarCommonConfig, BarCrossrangeConfig, BarLineConfig, CapsuleChartConfig] diff --git a/src/packages/components/Charts/Lines/LineLinearSingle/config.ts b/src/packages/components/Charts/Lines/LineLinearSingle/config.ts index abcb2a31..12b54782 100644 --- a/src/packages/components/Charts/Lines/LineLinearSingle/config.ts +++ b/src/packages/components/Charts/Lines/LineLinearSingle/config.ts @@ -33,6 +33,10 @@ export const option = { width: 3, color: { type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, colorStops: [ { offset: 0, diff --git a/src/packages/components/Charts/Maps/MapBase/config.ts b/src/packages/components/Charts/Maps/MapBase/config.ts index 888fe6b7..dd020e50 100644 --- a/src/packages/components/Charts/Maps/MapBase/config.ts +++ b/src/packages/components/Charts/Maps/MapBase/config.ts @@ -84,7 +84,10 @@ export const option = { shadowColor: '#E1FFFF', shadowBlur: 10 }, - data: [] + data: [], + encode: { + value: 2 + } }, { name: '区域', diff --git a/src/packages/components/Charts/Mores/Process/index.vue b/src/packages/components/Charts/Mores/Process/index.vue index 5faf79bf..f4ce6212 100644 --- a/src/packages/components/Charts/Mores/Process/index.vue +++ b/src/packages/components/Charts/Mores/Process/index.vue @@ -3,7 +3,7 @@ :type="type" :height="h" :processing="processing" - :percentage="option.dataset" + :percentage="dataset" :indicator-placement="indicatorPlacement" :color="color" :rail-color="railColor" @@ -15,7 +15,7 @@ fontSize: `${indicatorTextSize}px` }" > - {{ option.dataset }} {{ unit }} + {{ dataset }} {{ unit }} </n-text> </n-progress> </template> diff --git a/src/packages/components/Charts/Pies/PieCircle/config.vue b/src/packages/components/Charts/Pies/PieCircle/config.vue index a3206946..f343aa23 100644 --- a/src/packages/components/Charts/Pies/PieCircle/config.vue +++ b/src/packages/components/Charts/Pies/PieCircle/config.vue @@ -7,6 +7,22 @@ </n-input-number> </SettingItem> </SettingItemBox> + <!-- 中心标题 --> + <SettingItemBox v-if="config.title" name="标题"> + <SettingItem name="颜色"> + <n-color-picker size="small" :modes="['hex']" v-model:value="config.title.textStyle.color"></n-color-picker> + </SettingItem> + <SettingItem name="字体大小"> + <n-input-number + v-model:value="config.title.textStyle.fontSize" + :min="0" + :step="1" + size="small" + placeholder="字体大小" + > + </n-input-number> + </SettingItem> + </SettingItemBox> <!-- Echarts 全局设置 --> <SettingItemBox name="进度条"> <SettingItem name="颜色"> @@ -31,24 +47,8 @@ ></n-color-picker> </SettingItem> </SettingItemBox> - <!-- 中心标题 --> - <SettingItemBox v-if="config.title" name="标题"> - <SettingItem name="颜色"> - <n-color-picker size="small" :modes="['hex']" v-model:value="config.title.textStyle.color"></n-color-picker> - </SettingItem> - <SettingItem name="字体大小"> - <n-input-number - v-model:value="config.title.textStyle.fontSize" - :min="0" - :step="1" - size="small" - placeholder="字体大小" - > - </n-input-number> - </SettingItem> - </SettingItemBox> <!-- 其他样式 --> - <SettingItemBox name="轨道样式"> + <SettingItemBox name="轨道"> <SettingItem name="颜色"> <n-color-picker size="small" :modes="['hex']" v-model:value="item.data[1].itemStyle.color"></n-color-picker> </SettingItem> @@ -69,6 +69,18 @@ v-model:value="item.data[1].itemStyle.shadowColor" ></n-color-picker> </SettingItem> + <SettingItem name="轨道宽度"> + <n-select + v-model:value="item.radius[0]" + size="small" + :options="[ + { label: '窄', value: '75%' }, + { label: '中', value: '60%' }, + { label: '宽', value: '45%' }, + { label: '更宽', value: '30%' } + ]" + /> + </SettingItem> </SettingItemBox> </CollapseItem> </template> diff --git a/src/packages/components/Charts/Pies/PieCircle/index.vue b/src/packages/components/Charts/Pies/PieCircle/index.vue index 26bda2a7..bf6250cd 100644 --- a/src/packages/components/Charts/Pies/PieCircle/index.vue +++ b/src/packages/components/Charts/Pies/PieCircle/index.vue @@ -41,7 +41,7 @@ const option = reactive({ const dataHandle = (newData: any) => { const d = parseFloat(`${newData}`) * 100 let config = props.chartConfig.option - config.title.text = d.toFixed(2) + '%' + config.title.text = `${+d.toFixed(2)}%` config.series[0].data[0].value[0] = d config.series[0].data[1].value[0] = 100 - d option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes) @@ -68,7 +68,7 @@ watch( useChartDataFetch(props.chartConfig, useChartEditStore, (resData: number) => { let d = parseFloat(`${resData}`) * 100 // @ts-ignore - option.value.title.text = d.toFixed(2) + '%' + option.value.title.text = `${+d.toFixed(2)}%` // @ts-ignore option.value.series[0].data[0].value[0] = d // @ts-ignore diff --git a/src/packages/components/Charts/index.d.ts b/src/packages/components/Charts/index.d.ts index c033c296..d7cc2714 100644 --- a/src/packages/components/Charts/index.d.ts +++ b/src/packages/components/Charts/index.d.ts @@ -14,5 +14,6 @@ export enum ChatCategoryEnumName { LINE = '折线图', SCATTER = '散点图', MAP = '地图', + COMBINATION = '组合图', MORE = '更多' } diff --git a/src/packages/components/Decorates/Mores/FlipperNumber/index.vue b/src/packages/components/Decorates/Mores/FlipperNumber/index.vue index 9e5c0ca2..0c4ae010 100644 --- a/src/packages/components/Decorates/Mores/FlipperNumber/index.vue +++ b/src/packages/components/Decorates/Mores/FlipperNumber/index.vue @@ -63,7 +63,7 @@ watch( () => props.chartConfig.option, newVal => { try { - updateDatasetHandler((newVal as OptionType).dataset) + updateDatasetHandler((newVal as any as OptionType).dataset) } catch (error) { console.log(error) } diff --git a/src/packages/components/Decorates/Mores/FullScreen/config.ts b/src/packages/components/Decorates/Mores/FullScreen/config.ts new file mode 100644 index 00000000..b21d443d --- /dev/null +++ b/src/packages/components/Decorates/Mores/FullScreen/config.ts @@ -0,0 +1,18 @@ +import { PublicConfigClass } from '@/packages/public' +import { CreateComponentType } from '@/packages/index.d' +import { chartInitConfig } from '@/settings/designSetting' +import { FullScreenConfig } from './index' +import cloneDeep from 'lodash/cloneDeep' + +export const option = { + border: 6, + bgColor: '#84a5e9', + borderColor: '#84a5e9' +} + +export default class Config extends PublicConfigClass implements CreateComponentType { + public key = FullScreenConfig.key + public attr = { ...chartInitConfig, w: 150, h: 150, zIndex: -1 } + public chartConfig = cloneDeep(FullScreenConfig) + public option = cloneDeep(option) +} diff --git a/src/packages/components/Decorates/Mores/FullScreen/config.vue b/src/packages/components/Decorates/Mores/FullScreen/config.vue new file mode 100644 index 00000000..450752d7 --- /dev/null +++ b/src/packages/components/Decorates/Mores/FullScreen/config.vue @@ -0,0 +1,28 @@ +<template> + <CollapseItem name="全屏按钮" expanded> + <SettingItemBox name="按钮"> + <SettingItem name="背景色"> + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.bgColor"></n-color-picker> + </SettingItem> + <SettingItem name="边框色"> + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.borderColor"></n-color-picker> + </SettingItem> + <SettingItem name="边框大小"> + <n-input-number v-model:value="optionData.border" size="small" :step="0.5" :min="0"></n-input-number> + </SettingItem> + </SettingItemBox> + </CollapseItem> +</template> + +<script setup lang="ts"> +import { PropType } from 'vue' +import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' +import { option } from './config' + +const props = defineProps({ + optionData: { + type: Object as PropType<typeof option>, + required: true + } +}) +</script> diff --git a/src/packages/components/Decorates/Mores/FullScreen/index.ts b/src/packages/components/Decorates/Mores/FullScreen/index.ts new file mode 100644 index 00000000..19e37683 --- /dev/null +++ b/src/packages/components/Decorates/Mores/FullScreen/index.ts @@ -0,0 +1,14 @@ +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d' +import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d' + +export const FullScreenConfig: ConfigType = { + key: 'FullScreen', + chartKey: 'VFullScreen', + conKey: 'VCFullScreen', + title: '全屏按钮', + category: ChatCategoryEnum.MORE, + categoryName: ChatCategoryEnumName.MORE, + package: PackagesCategoryEnum.DECORATES, + chartFrame: ChartFrameEnum.STATIC, + image: 'fullScreen.png' +} diff --git a/src/packages/components/Decorates/Mores/FullScreen/index.vue b/src/packages/components/Decorates/Mores/FullScreen/index.vue new file mode 100644 index 00000000..89590bb0 --- /dev/null +++ b/src/packages/components/Decorates/Mores/FullScreen/index.vue @@ -0,0 +1,111 @@ +<template> + <svg @click="toggleFullscreen" v-if="!isFullscreen" viewBox="0 0 1024 1024"> + <path + d="M665.6 1017.6c-19.2 0-38.4-19.2-38.4-38.4s19.2-38.4 38.4-38.4h268.8l6.4-268.8c0-19.2 19.2-38.4 38.4-38.4s38.4 19.2 38.4 38.4v294.4c0 32-25.6 51.2-51.2 51.2h-300.8zM51.2 396.8c-19.2 0-38.4-19.2-38.4-38.4V64C12.8 32 38.4 12.8 64 12.8h294.4c19.2 0 38.4 19.2 38.4 38.4s-19.2 38.4-38.4 38.4H89.6v268.8c0 19.2-19.2 38.4-38.4 38.4zM64 1017.6c-32 0-51.2-25.6-51.2-51.2v-294.4c0-19.2 19.2-38.4 38.4-38.4s38.4 19.2 38.4 38.4v217.6l198.4-198.4c6.4-6.4 19.2-12.8 25.6-12.8s19.2 6.4 25.6 12.8c6.4 6.4 12.8 19.2 12.8 25.6 0 12.8-6.4 19.2-12.8 25.6l-198.4 198.4h217.6c19.2 0 38.4 19.2 38.4 38.4s-19.2 38.4-38.4 38.4H64z m915.2-620.8c-19.2 0-38.4-19.2-38.4-38.4V140.8l-198.4 198.4c-6.4 6.4-19.2 12.8-25.6 12.8-12.8 0-19.2-6.4-25.6-12.8-12.8-12.8-12.8-38.4 0-51.2l198.4-198.4h-217.6c-19.2 0-38.4-19.2-38.4-38.4s19.2-38.4 38.4-38.4h294.4c32 0 51.2 25.6 51.2 51.2v294.4c0 19.2-19.2 38.4-38.4 38.4z" + class="fullScreen-border" + ></path> + </svg> + <svg @click="toggleFullscreen" v-else viewBox="0 0 1024 1024"> + <path + d="M379.336 697.237L153.362 921.55c-14.11 14.007-36.905 13.922-50.912-0.188-14.007-14.11-13.922-36.905 0.188-50.912l227.6-225.927H138.645c-18.99 0-34.385-15.446-34.385-34.5 0-19.053 15.395-34.5 34.385-34.5H413.72c18.99 0 34.384 15.447 34.384 34.5v276c0 9.15-3.622 17.926-10.07 24.396a34.326 34.326 0 0 1-24.314 10.104 34.326 34.326 0 0 1-24.314-10.104 34.559 34.559 0 0 1-10.071-24.396V697.237z m263.395-366.88l227.813-227.813c14.059-14.059 36.853-14.059 50.912 0 14.059 14.059 14.059 36.853 0 50.912l-225.18 225.18h187.147c18.99 0 34.385 15.445 34.385 34.5 0 19.053-15.395 34.5-34.385 34.5H608.346c-18.99 0-34.384-15.447-34.384-34.5v-276c0-9.15 3.622-17.926 10.07-24.396a34.326 34.326 0 0 1 24.314-10.105c9.12 0 17.865 3.635 24.314 10.105a34.559 34.559 0 0 1 10.07 24.395v193.223zM99.385 410a34.326 34.326 0 0 1-24.314-10.105A34.559 34.559 0 0 1 65 375.5v-276C65 80.446 80.395 65 99.385 65h275.077c18.99 0 34.384 15.446 34.384 34.5 0 19.054-15.394 34.5-34.384 34.5H133.769v241.5c0 9.15-3.622 17.925-10.07 24.395A34.326 34.326 0 0 1 99.384 410z m825.23 552H649.538c-18.99 0-34.384-15.446-34.384-34.5 0-19.054 15.394-34.5 34.384-34.5h240.693V651.5c0-19.054 15.394-34.5 34.384-34.5 18.99 0 34.385 15.446 34.385 34.5v276c0 19.054-15.395 34.5-34.385 34.5z" + class="fullScreen-border" + ></path> + </svg> +</template> + +<script setup lang="ts"> +import { PropType, toRefs, ref, onMounted, onUnmounted } from 'vue' +import { CreateComponentType } from '@/packages/index.d' +import { option } from './config' + +const props = defineProps({ + chartConfig: { + type: Object as PropType<CreateComponentType & typeof option>, + required: true + } +}) + +let { border, bgColor, borderColor } = toRefs(props.chartConfig.option) +const isFullscreen = ref(false) +const checkFullscreen = () => { + isFullscreen.value = !!( + document.fullscreenElement || + (document as any).webkitFullscreenElement || + (document as any).mozFullScreenElement || + (document as any).msFullscreenElement + ) +} +checkFullscreen() + +const requestFullscreen = (element: Element) => { + if (element.requestFullscreen) { + element.requestFullscreen() + } else if ((document as any).mozRequestFullScreen) { + /* Firefox */ + (document as any).mozRequestFullScreen() + } else if ((document as any).webkitRequestFullscreen) { + /* Chrome, Safari and Opera */ + (document as any).webkitRequestFullscreen() + } else if ((document as any).msRequestFullscreen) { + /* IE/Edge */ + (document as any).msRequestFullscreen() + } +} + +const exitFullscreen = () => { + if (document.fullscreenElement && document.exitFullscreen) { + document.exitFullscreen() + } else if ((document as any).mozFullScreenElement && (document as any).mozCancelFullScreen) { + /* Firefox */ + (document as any).mozCancelFullScreen() + } else if ((document as any).webkitFullscreenElement && (document as any).webkitExitFullscreen) { + /* Chrome, Safari and Opera */ + (document as any).webkitExitFullscreen() + } else if ((document as any).msFullscreenElement && (document as any).msExitFullscreen) { + /* IE/Edge */ + (document as any).msExitFullscreen() + } +} + +const toggleFullscreen = () => { + if (!isFullscreen.value) { + requestFullscreen(document.documentElement) + } else { + exitFullscreen() + } + isFullscreen.value = !isFullscreen.value + // 由于全屏状态的改变不会立即生效,所以需要延迟一段时间再去获取全屏状态 + setTimeout(() => { + checkFullscreen() + }, 1000) +} + +// 监听全屏状态的改变,保证多个全屏组件的状态一致 +onMounted(() => { + document.addEventListener('fullscreenchange', checkFullscreen) + document.addEventListener('webkitfullscreenchange', checkFullscreen) + document.addEventListener('mozfullscreenchange', checkFullscreen) + document.addEventListener('MSFullscreenChange', checkFullscreen) +}) + +onUnmounted(() => { + document.removeEventListener('fullscreenchange', checkFullscreen) + document.removeEventListener('webkitfullscreenchange', checkFullscreen) + document.removeEventListener('mozfullscreenchange', checkFullscreen) + document.removeEventListener('MSFullscreenChange', checkFullscreen) +}) +</script> + +<style lang="scss" scoped> +svg { + display: block; + width: 100%; + height: 100%; + cursor: pointer; +} +.fullScreen-border { + stroke: v-bind('borderColor'); + stroke-width: v-bind('border+"px"'); + fill: v-bind('bgColor'); +} +</style> diff --git a/src/packages/components/Decorates/Mores/index.ts b/src/packages/components/Decorates/Mores/index.ts index 7223832b..489b9c5c 100644 --- a/src/packages/components/Decorates/Mores/index.ts +++ b/src/packages/components/Decorates/Mores/index.ts @@ -1,9 +1,19 @@ import { NumberConfig } from './Number/index' import { TimeCommonConfig } from './TimeCommon/index' import { ClockConfig } from './Clock/index' +import { FullScreenConfig } from './FullScreen/index' import { CountDownConfig } from './CountDown/index' import { FlipperNumberConfig } from './FlipperNumber' import { PipelineHConfig } from './PipelineH/index' import { PipelineVConfig } from './PipelineV/index' -export default [NumberConfig, FlipperNumberConfig, TimeCommonConfig, CountDownConfig, ClockConfig, PipelineHConfig, PipelineVConfig] +export default [ + NumberConfig, + FlipperNumberConfig, + TimeCommonConfig, + CountDownConfig, + ClockConfig, + FullScreenConfig, + PipelineHConfig, + PipelineVConfig +] diff --git a/src/packages/components/Decorates/Three/ThreeEarth01/code/world/Basic.ts b/src/packages/components/Decorates/Three/ThreeEarth01/code/world/Basic.ts index bfe9d760..72284935 100644 --- a/src/packages/components/Decorates/Three/ThreeEarth01/code/world/Basic.ts +++ b/src/packages/components/Decorates/Three/ThreeEarth01/code/world/Basic.ts @@ -1,62 +1,63 @@ -/** - * 创建 threejs 四大天王 - * 场景、相机、渲染器、控制器 - */ - -import * as THREE from 'three' -import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' - -export class Basic { - public scene!: THREE.Scene - public camera!: THREE.PerspectiveCamera - public renderer!: THREE.WebGLRenderer - public controls!: OrbitControls - public dom: HTMLElement - - constructor(dom: HTMLElement) { - this.dom = dom - this.initScenes() - this.setControls() - } - - /** - * 初始化场景 - */ - initScenes() { - this.scene = new THREE.Scene() - - this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 100000) - this.camera.position.set(0, 30, -250) - - this.renderer = new THREE.WebGLRenderer({ - // canvas: this.dom, - alpha: true, // 透明 - antialias: true // 抗锯齿 - }) - this.renderer.setPixelRatio(window.devicePixelRatio) // 设置屏幕像素比 - this.renderer.setSize(window.innerWidth, window.innerHeight) // 设置渲染器宽高 - this.dom.appendChild(this.renderer.domElement) // 添加到dom中 - } - - /** - * 设置控制器 - */ - setControls() { - // 鼠标控制 相机,渲染dom - this.controls = new OrbitControls(this.camera, this.renderer.domElement) - - this.controls.autoRotateSpeed = 3 - // 使动画循环使用时阻尼或自转 意思是否有惯性 - this.controls.enableDamping = true - // 动态阻尼系数 就是鼠标拖拽旋转灵敏度 - this.controls.dampingFactor = 0.05 - // 是否可以缩放 - this.controls.enableZoom = true - // 设置相机距离原点的最远距离 - this.controls.minDistance = 100 - // 设置相机距离原点的最远距离 - this.controls.maxDistance = 300 - // 是否开启右键拖拽 - this.controls.enablePan = false - } -} +/** + * 创建 threejs 四大天王 + * 场景、相机、渲染器、控制器 + */ + +import * as THREE from 'three' +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' + +export class Basic { + public scene!: THREE.Scene + public camera!: THREE.PerspectiveCamera + public renderer!: THREE.WebGLRenderer + public controls!: OrbitControls + public dom: HTMLElement + + constructor(dom: HTMLElement) { + this.dom = dom + this.initScenes() + this.setControls() + } + + /** + * 初始化场景 + */ + initScenes() { + this.scene = new THREE.Scene() + + this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 100000) + this.camera.position.set(0, 30, -250) + + this.renderer = new THREE.WebGLRenderer({ + // canvas: this.dom, + alpha: true, // 透明 + antialias: true, // 抗锯齿 + preserveDrawingBuffer: true + }) + this.renderer.setPixelRatio(window.devicePixelRatio) // 设置屏幕像素比 + this.renderer.setSize(window.innerWidth, window.innerHeight) // 设置渲染器宽高 + this.dom.appendChild(this.renderer.domElement) // 添加到dom中 + } + + /** + * 设置控制器 + */ + setControls() { + // 鼠标控制 相机,渲染dom + this.controls = new OrbitControls(this.camera, this.renderer.domElement) + + this.controls.autoRotateSpeed = 3 + // 使动画循环使用时阻尼或自转 意思是否有惯性 + this.controls.enableDamping = true + // 动态阻尼系数 就是鼠标拖拽旋转灵敏度 + this.controls.dampingFactor = 0.05 + // 是否可以缩放 + this.controls.enableZoom = true + // 设置相机距离原点的最远距离 + this.controls.minDistance = 100 + // 设置相机距离原点的最远距离 + this.controls.maxDistance = 300 + // 是否开启右键拖拽 + this.controls.enablePan = false + } +} diff --git a/src/packages/components/Informations/Inputs/InputsDate/config.ts b/src/packages/components/Informations/Inputs/InputsDate/config.ts old mode 100644 new mode 100755 index e8f1d233..02b639c6 --- a/src/packages/components/Informations/Inputs/InputsDate/config.ts +++ b/src/packages/components/Informations/Inputs/InputsDate/config.ts @@ -4,7 +4,7 @@ import { PublicConfigClass } from '@/packages/public' import { CreateComponentType } from '@/packages/index.d' import { chartInitConfig } from '@/settings/designSetting' import { COMPONENT_INTERACT_EVENT_KET } from '@/enums/eventEnum' -import { interactActions, ComponentInteractEventEnum } from './interact' +import { interactActions, ComponentInteractEventEnum, DefaultTypeEnum, DifferUnitEnum } from './interact' import { InputsDateConfig } from './index' export const option = { @@ -12,9 +12,14 @@ export const option = { [COMPONENT_INTERACT_EVENT_KET]: ComponentInteractEventEnum.DATE, // 下拉展示 isPanel: 0, - dataset: dayjs().valueOf(), - differValue: 0 - + // 默认值 + dataset: dayjs().valueOf() as number | number[] | null, + // 默认值类型 + defaultType: DefaultTypeEnum.STATIC, + // 动态默认值偏移单位 + differUnit: [DifferUnitEnum.DAY, DifferUnitEnum.DAY], + // 动态默认值偏移值 + differValue: [0, 0] } export default class Config extends PublicConfigClass implements CreateComponentType { diff --git a/src/packages/components/Informations/Inputs/InputsDate/config.vue b/src/packages/components/Informations/Inputs/InputsDate/config.vue old mode 100644 new mode 100755 index 0d806518..228fa494 --- a/src/packages/components/Informations/Inputs/InputsDate/config.vue +++ b/src/packages/components/Informations/Inputs/InputsDate/config.vue @@ -8,39 +8,67 @@ <collapse-item name="时间配置" :expanded="true"> <setting-item-box name="基础"> <setting-item name="类型"> - <n-select v-model:value="optionData.componentInteractEventKey" size="small" :options="datePickerTypeOptions" /> + <n-select v-model:value="optionData.componentInteractEventKey" size="small" :options="datePickerTypeOptions" + @update:value="datePickerTypeUpdate"/> </setting-item> </setting-item-box> - <setting-item-box name="默认值" :alone="true"> - <n-date-picker size="small" v-model:value="optionData.dataset" :type="optionData.componentInteractEventKey" /> - </setting-item-box> + <setting-item-box name="默认值"> + <setting-item name="类型"> + <n-select v-model:value="optionData.defaultType" size="small" :options="defaultTypeOptions" + @update:value="defaultTypeUpdate" /> + </setting-item> - <setting-item-box :alone="true"> + </setting-item-box> + <setting-item-box v-if="optionData.defaultType === DefaultTypeEnum.STATIC" :alone="true"> + <setting-item name="静态默认值"> + <n-date-picker size="small" clearable v-model:value="optionData.dataset" :type="optionData.componentInteractEventKey" /> + </setting-item> + </setting-item-box> + <setting-item-box v-if="optionData.defaultType === DefaultTypeEnum.DYNAMIC" > <template #name> - <n-text>动态</n-text> + <n-text></n-text> <n-tooltip trigger="hover"> <template #trigger> <n-icon size="21" :depth="3"> <help-outline-icon></help-outline-icon> </n-icon> </template> - <n-text>动态值不为0时,默认值:取当天时间相加当前值</n-text> + <span>打开页面时浏览器操作系统的系统时间+偏移量(单位)</span> </n-tooltip> </template> - <n-input-number v-model:value="optionData.differValue" class="input-num-width" size="small" :min="-40" :max="40"> - <template #suffix> 天 </template> - </n-input-number> + <setting-item :name="differValueName"> + <n-input-number v-model:value="optionData.differValue[0]" class="input-num-width" size="small"> + <template #suffix> + {{DifferUnitObject[optionData.differUnit[0]]}} + </template> + </n-input-number> + </setting-item> + <setting-item :name="differUnitName"> + <n-select v-model:value="optionData.differUnit[0]" size="small" :options="differUnitOptions" /> + </setting-item> + <setting-item v-if="isRange" name="结束值动态偏移量"> + <n-input-number v-model:value="optionData.differValue[1]" class="input-num-width" size="small"> + <template #suffix> + {{DifferUnitObject[optionData.differUnit[1]]}} + </template> + </n-input-number> + </setting-item> + <setting-item v-if="isRange" name="结束值偏移单位"> + <n-select v-model:value="optionData.differUnit[1]" size="small" :options="differUnitOptions" /> + </setting-item> </setting-item-box> + </collapse-item> </template> <script lang="ts" setup> -import { PropType } from 'vue' +import { PropType, computed } from 'vue' import { icon } from '@/plugins' import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' import { option } from './config' -import { ComponentInteractEventEnum } from './interact' +import { ComponentInteractEventEnum, DefaultTypeEnum, DifferUnitEnum, DifferUnitObject } from './interact' +import dayjs from "dayjs"; const { HelpOutlineIcon } = icon.ionicons5 @@ -100,4 +128,87 @@ const datePickerTypeOptions = [ value: ComponentInteractEventEnum.QUARTER_RANGE } ] + +const defaultTypeOptions = [ + { + label: '静态', + value: DefaultTypeEnum.STATIC + }, + { + label: '动态', + value: DefaultTypeEnum.DYNAMIC + }, + { + label: '无', + value: DefaultTypeEnum.NONE + } +] + + +const differUnitOptions = [ + // ManipulateType + { + value: DifferUnitEnum.DAY, + label: DifferUnitObject[DifferUnitEnum.DAY] + }, + { + value: DifferUnitEnum.WEEK, + label: DifferUnitObject[DifferUnitEnum.WEEK] + }, + { + value: DifferUnitEnum.MONTH, + label: DifferUnitObject[DifferUnitEnum.MONTH] + }, + { + value: DifferUnitEnum.QUARTER, + label: DifferUnitObject[DifferUnitEnum.QUARTER] + }, + { + value: DifferUnitEnum.YEAR, + label: DifferUnitObject[DifferUnitEnum.YEAR] + }, + { + value: DifferUnitEnum.HOUR, + label: DifferUnitObject[DifferUnitEnum.HOUR] + }, + { + value: DifferUnitEnum.MINUTE, + label: DifferUnitObject[DifferUnitEnum.MINUTE] + }, + { + value: DifferUnitEnum.SECOND, + label: DifferUnitObject[DifferUnitEnum.SECOND] + }, + { + value: DifferUnitEnum.MILLISECOND, + label: DifferUnitObject[DifferUnitEnum.MILLISECOND] + } +] + + +const isRange = computed(() => { + return props.optionData.componentInteractEventKey.endsWith('range') +}) + +const differValueName = computed(() => { + return isRange.value ? '开始值动态偏移量' : '动态偏移量' +}) + +const differUnitName = computed(() => { + return isRange.value ? '开始值偏移单位' : '偏移单位' +}) + +const datePickerTypeUpdate = () => { + props.optionData.dataset = isRange.value ? [dayjs().valueOf(), dayjs().valueOf()] : dayjs().valueOf() +} + +const defaultTypeUpdate = (v: string) => { + if (v === DefaultTypeEnum.STATIC) { + datePickerTypeUpdate() + } else { + // DefaultTypeEnum. + props.optionData.dataset = null + } +} + </script> diff --git a/src/packages/components/Informations/Inputs/InputsDate/index.vue b/src/packages/components/Informations/Inputs/InputsDate/index.vue old mode 100644 new mode 100755 index 3800590f..a8e16bc1 --- a/src/packages/components/Informations/Inputs/InputsDate/index.vue +++ b/src/packages/components/Informations/Inputs/InputsDate/index.vue @@ -1,6 +1,7 @@ <template> <n-date-picker v-model:value="option.dataset" + clearable :panel="!!chartConfig.option.isPanel" :type="chartConfig.option.componentInteractEventKey" :style="`width:${w}px;`" @@ -9,13 +10,15 @@ </template> <script setup lang="ts"> -import { PropType, toRefs, ref, shallowReactive, watch } from 'vue' -import dayjs from 'dayjs' +import { computed, PropType, ref, shallowReactive, toRefs, watch } from 'vue' import { CreateComponentType } from '@/packages/index.d' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartInteract } from '@/hooks' import { InteractEventOn } from '@/enums/eventEnum' -import { ComponentInteractParamsEnum } from './interact' +import {ComponentInteractEventEnum, ComponentInteractParamsEnum, DefaultTypeEnum} from './interact' +import dayjs, {ManipulateType} from 'dayjs' +import quarterOfYear from 'dayjs/plugin/quarterOfYear'; + const props = defineProps({ chartConfig: { @@ -31,61 +34,107 @@ const option = shallowReactive({ dataset: props.chartConfig.option.dataset }) +const isRange = computed(() => { + return props.chartConfig.option.componentInteractEventKey.endsWith('range') +}) + // 监听事件改变 -const onChange = (v: number | number[]) => { - if (v instanceof Array) { +const onChange = (v: number | number[] | null) => { + if (isRange.value) { + let dateStart = null + let dateEnd = null + let daterange = null + if(v instanceof Array){ + dateStart = v[0] + dateEnd = v[1] + daterange = `${v[0]}-${v[1]}` + } // 存储到联动数据 useChartInteract( - props.chartConfig, - useChartEditStore, - { - [ComponentInteractParamsEnum.DATE_START]: v[0] || dayjs().valueOf(), - [ComponentInteractParamsEnum.DATE_END]: v[1] || dayjs().valueOf(), - [ComponentInteractParamsEnum.DATE_RANGE]: `${v[0] || dayjs().valueOf()}-${v[1] || dayjs().valueOf()}` - }, - InteractEventOn.CHANGE + props.chartConfig, + useChartEditStore, + { + [ComponentInteractParamsEnum.DATE_START]: dateStart, + [ComponentInteractParamsEnum.DATE_END]: dateEnd, + [ComponentInteractParamsEnum.DATE_RANGE]: daterange + }, + InteractEventOn.CHANGE ) } else { // 存储到联动数据 useChartInteract( - props.chartConfig, - useChartEditStore, - { [ComponentInteractParamsEnum.DATE]: v || dayjs().valueOf() }, - InteractEventOn.CHANGE + props.chartConfig, + useChartEditStore, + { [ComponentInteractParamsEnum.DATE]: v }, + InteractEventOn.CHANGE ) } } -watch( - () => props.chartConfig.option.dataset, - (newData: number | number[]) => { - option.dataset = newData - // 关联目标组件首次请求带上默认内容 - onChange(newData) - }, - { - immediate: true +const getDiffDate = (type: ComponentInteractEventEnum, date: dayjs.Dayjs) => { + // 注册 quarterOfYear 插件 + dayjs.extend(quarterOfYear) + switch (type) { + case ComponentInteractEventEnum.DATE: + case ComponentInteractEventEnum.DATE_RANGE: + date = date.startOf('day') + break + case ComponentInteractEventEnum.MONTH: + case ComponentInteractEventEnum.MONTH_RANGE: + date = date.startOf('month') + break + case ComponentInteractEventEnum.YEAR: + case ComponentInteractEventEnum.YEAR_RANGE: + date = date.startOf('year') + break + case ComponentInteractEventEnum.QUARTER: + case ComponentInteractEventEnum.QUARTER_RANGE: + date = date.startOf('quarter') + break + default: + break } -) + return date +} -// 手动更新 watch( - () => props.chartConfig.option.differValue, - (newData: number) => { - if (props.chartConfig.option.differValue === 0) return - if (typeof option.dataset === 'object') { - option.dataset[0] = dayjs().add(newData, 'day').valueOf() - option.dataset[1] = dayjs().add(newData, 'day').valueOf() - } else { - option.dataset = dayjs().add(newData, 'day').valueOf() + () => { + return { + type: props.chartConfig.option.componentInteractEventKey as ComponentInteractEventEnum, + defaultType: props.chartConfig.option.defaultType as string, + differValue: props.chartConfig.option.differValue as number[], + differUnit: props.chartConfig.option.differUnit as ManipulateType[], + dataset: props.chartConfig.option.dataset as number | number[] | null, + }; + }, + (newData, oldData) => { + const hasTypeChanged = newData.type !== oldData?.type; + const hasDefaultTypeChanged = newData.defaultType !== oldData?.defaultType; + const hasDifferValueChanged = newData.differValue !== oldData?.differValue; + const hasDifferUnitChanged = newData.differUnit !== oldData?.differUnit; + + if (hasTypeChanged || hasDefaultTypeChanged || hasDifferValueChanged || hasDifferUnitChanged) { + if (newData.defaultType === DefaultTypeEnum.NONE) { + props.chartConfig.option.dataset = null; + } else if (newData.defaultType === DefaultTypeEnum.DYNAMIC) { + let date = dayjs(); + if (isRange.value) { + props.chartConfig.option.dataset = [ + getDiffDate(newData.type,date.add(newData.differValue[0], newData.differUnit[0])).valueOf(), + getDiffDate(newData.type,date.add(newData.differValue[1], newData.differUnit[1])).valueOf(), + ]; + } else { + props.chartConfig.option.dataset = getDiffDate(newData.type,date.add(newData.differValue[0], newData.differUnit[0])).valueOf() + } + } + } + option.dataset = props.chartConfig.option.dataset; + onChange(option.dataset); + }, + { + immediate: true, } - // 关联目标组件首次请求带上默认内容 - onChange(newData) - }, - { - immediate: true - } -) +); </script> <style lang="scss" scoped> diff --git a/src/packages/components/Informations/Inputs/InputsDate/interact.ts b/src/packages/components/Informations/Inputs/InputsDate/interact.ts old mode 100644 new mode 100755 index a466c989..6690f0eb --- a/src/packages/components/Informations/Inputs/InputsDate/interact.ts +++ b/src/packages/components/Informations/Inputs/InputsDate/interact.ts @@ -22,6 +22,37 @@ export enum ComponentInteractParamsEnum { DATE_RANGE = 'daterange' } +export enum DefaultTypeEnum { + NONE = "none", + STATIC = "static", + DYNAMIC = "dynamic" +} + +export enum DifferUnitEnum { + DAY = 'd', + WEEK = 'w', + MONTH = 'M', + QUARTER = 'Q', + YEAR = 'y', + HOUR = 'h', + MINUTE = 'm', + SECOND = 's', + MILLISECOND = 'ms', +} + +export const DifferUnitObject = { + // https://day.js.org/docs/en/manipulate/add + [DifferUnitEnum.DAY]: '天', + [DifferUnitEnum.WEEK]: '周', + [DifferUnitEnum.MONTH]: '月', + [DifferUnitEnum.QUARTER]: '季度', + [DifferUnitEnum.YEAR]: '年', + [DifferUnitEnum.HOUR]: '小时', + [DifferUnitEnum.MINUTE]: '分钟', + [DifferUnitEnum.SECOND]: '秒', + [DifferUnitEnum.MILLISECOND]: '毫秒', +} + const time = [ { value: ComponentInteractParamsEnum.DATE, diff --git a/src/packages/components/Informations/Inputs/InputsInput/config.ts b/src/packages/components/Informations/Inputs/InputsInput/config.ts new file mode 100644 index 00000000..f99c35d7 --- /dev/null +++ b/src/packages/components/Informations/Inputs/InputsInput/config.ts @@ -0,0 +1,24 @@ +import cloneDeep from 'lodash/cloneDeep' +import { PublicConfigClass } from '@/packages/public' +import { CreateComponentType } from '@/packages/index.d' +import { chartInitConfig } from '@/settings/designSetting' +import { COMPONENT_INTERACT_EVENT_KET } from '@/enums/eventEnum' +import { interactActions, ComponentInteractEventEnum } from './interact' +import {InputsInputConfig} from "./index"; + +export const option = { + // 时间组件展示类型,必须和 interactActions 中定义的数据一致 + [COMPONENT_INTERACT_EVENT_KET]: ComponentInteractEventEnum.DATA, + // 默认值 + inputValue: "0", + // 暴露配置内容给用户 + dataset: "" +} + +export default class Config extends PublicConfigClass implements CreateComponentType { + public key = InputsInputConfig.key + public attr = { ...chartInitConfig, w: 260, h: 32, zIndex: -1 } + public chartConfig = cloneDeep(InputsInputConfig) + public interactActions = interactActions + public option = cloneDeep(option) +} \ No newline at end of file diff --git a/src/packages/components/Informations/Inputs/InputsInput/config.vue b/src/packages/components/Informations/Inputs/InputsInput/config.vue new file mode 100644 index 00000000..1c7900ee --- /dev/null +++ b/src/packages/components/Informations/Inputs/InputsInput/config.vue @@ -0,0 +1,18 @@ +<template> + <collapse-item name="输入框配置" :expanded="true"> + <setting-item-box name="默认值" :alone="true"> + <n-input v-model:value="optionData.dataset" placeholder="若未输入,则默认值为0"/> + </setting-item-box> + </collapse-item> +</template> +<script setup lang="ts"> +import { PropType } from 'vue' +import { CollapseItem, SettingItemBox } from '@/components/Pages/ChartItemSetting' +import { option } from './config' +defineProps({ + optionData: { + type: Object as PropType<typeof option>, + required: true + } +}) +</script> \ No newline at end of file diff --git a/src/packages/components/Informations/Inputs/InputsInput/index.ts b/src/packages/components/Informations/Inputs/InputsInput/index.ts new file mode 100644 index 00000000..0828ea35 --- /dev/null +++ b/src/packages/components/Informations/Inputs/InputsInput/index.ts @@ -0,0 +1,14 @@ +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d' +import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d' + +export const InputsInputConfig: ConfigType = { + key: 'InputsInput', + chartKey: 'VInputsInput', + conKey: 'VCInputsInput', + title: '输入框', + category: ChatCategoryEnum.INPUTS, + categoryName: ChatCategoryEnumName.INPUTS, + package: PackagesCategoryEnum.INFORMATIONS, + chartFrame: ChartFrameEnum.STATIC, + image: 'inputs_select.png' +} \ No newline at end of file diff --git a/src/packages/components/Informations/Inputs/InputsInput/index.vue b/src/packages/components/Informations/Inputs/InputsInput/index.vue new file mode 100644 index 00000000..e01e75f3 --- /dev/null +++ b/src/packages/components/Informations/Inputs/InputsInput/index.vue @@ -0,0 +1,64 @@ +<template> + <div> + <n-input :style="`width:${w}px;`" type="text" + v-model:value="option.value.dataset" + placeholder="请输入" + @change="onChange"> + + </n-input> + </div> +</template> + +<script lang="ts" setup> +import { PropType, toRefs, shallowReactive, watch } from 'vue' +import { CreateComponentType } from '@/packages/index.d' +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' +import { useChartInteract } from '@/hooks' +import { InteractEventOn } from '@/enums/eventEnum' +import { ComponentInteractParamsEnum } from './interact' + +const props = defineProps({ + chartConfig: { + type: Object as PropType<CreateComponentType>, + required: true + } +}) + +const { w, h } = toRefs(props.chartConfig.attr) +const option = shallowReactive({ + value: { + inputValue: props.chartConfig.option.inputValue, + dataset: props.chartConfig.option.dataset + } +}) + +const onChange = (v: string) => { + if(v == undefined) return; + // 存储到联动数据 + useChartInteract( + props.chartConfig, + useChartEditStore, + { [ComponentInteractParamsEnum.DATA]: v }, + InteractEventOn.CHANGE + ) +} + +// 手动更新 +watch( + () => props.chartConfig.option, + (newData: any) => { + option.value = newData + onChange(newData.inputValue) + }, + { + immediate: true, + deep: true + } +) + +</script> + + + + + diff --git a/src/packages/components/Informations/Inputs/InputsInput/interact.ts b/src/packages/components/Informations/Inputs/InputsInput/interact.ts new file mode 100644 index 00000000..d6c070f4 --- /dev/null +++ b/src/packages/components/Informations/Inputs/InputsInput/interact.ts @@ -0,0 +1,27 @@ +import { InteractEventOn, InteractActionsType } from '@/enums/eventEnum' + +// 时间组件类型 +export enum ComponentInteractEventEnum { + DATA = 'data' +} + +// 联动参数 +export enum ComponentInteractParamsEnum { + DATA = 'data' +} + +// 定义组件触发回调事件 +export const interactActions: InteractActionsType[] = [ + { + interactType: InteractEventOn.CHANGE, + interactName: '选择完成', + componentEmitEvents: { + [ComponentInteractEventEnum.DATA]: [ + { + value: ComponentInteractParamsEnum.DATA, + label: '选择项' + } + ] + } + } +] diff --git a/src/packages/components/Informations/Inputs/InputsPagination/config.ts b/src/packages/components/Informations/Inputs/InputsPagination/config.ts new file mode 100644 index 00000000..7d73e5a1 --- /dev/null +++ b/src/packages/components/Informations/Inputs/InputsPagination/config.ts @@ -0,0 +1,26 @@ +import cloneDeep from 'lodash/cloneDeep' +import { PublicConfigClass } from '@/packages/public' +import { CreateComponentType } from '@/packages/index.d' +import { chartInitConfig } from '@/settings/designSetting' +import { COMPONENT_INTERACT_EVENT_KET } from '@/enums/eventEnum' +import { interactActions, ComponentInteractEventEnum } from './interact' +import {InputsPaginationConfig} from "./index"; + +export const option = { + // 时间组件展示类型,必须和 interactActions 中定义的数据一致 + [COMPONENT_INTERACT_EVENT_KET]: ComponentInteractEventEnum.DATA, + // 默认值 + pageValue:1, + sizeValue:[2,4,8,10,20], + pageSize:4, + // 暴露配置内容给用户 + dataset: 10 +} + +export default class Config extends PublicConfigClass implements CreateComponentType { + public key = InputsPaginationConfig.key + public attr = { ...chartInitConfig, w: 395, h: 32, zIndex: -1 } + public chartConfig = cloneDeep(InputsPaginationConfig) + public interactActions = interactActions + public option = cloneDeep(option) +} \ No newline at end of file diff --git a/src/packages/components/Informations/Inputs/InputsPagination/config.vue b/src/packages/components/Informations/Inputs/InputsPagination/config.vue new file mode 100644 index 00000000..3599781d --- /dev/null +++ b/src/packages/components/Informations/Inputs/InputsPagination/config.vue @@ -0,0 +1,35 @@ +<template> + <collapse-item name="分页配置" :expanded="true"> + <setting-item-box :alone="false" name="分页设置"> + <setting-item name="默认页码" :alone="true"> + <n-input-number v-model:value="optionData.pageValue" size="small" placeholder="字体大小"></n-input-number> + </setting-item> + <setting-item name="分页" :alone="true"> + <n-select v-model:value="optionData.pageSize" size="small" + :options="page" /> + </setting-item> + <setting-item name="页数" :alone="true"> + <n-input-number v-model:value="optionData.dataset" size="small" placeholder="字体大小"></n-input-number> + </setting-item> + </setting-item-box> + </collapse-item> +</template> +<script setup lang="ts"> +import { PropType } from 'vue' +import {CollapseItem, SettingItem, SettingItemBox} from '@/components/Pages/ChartItemSetting' +import { option } from './config' + +const page = [ + {label:'2',value:2}, + {label:'4',value:4}, + {label:'8',value:8}, + {label:'10',value:10}, + {label:'20',value:20} +] +defineProps({ + optionData: { + type: Object as PropType<typeof option>, + required: true + } +}) +</script> \ No newline at end of file diff --git a/src/packages/components/Informations/Inputs/InputsPagination/index.ts b/src/packages/components/Informations/Inputs/InputsPagination/index.ts new file mode 100644 index 00000000..c1a69252 --- /dev/null +++ b/src/packages/components/Informations/Inputs/InputsPagination/index.ts @@ -0,0 +1,14 @@ +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d' +import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d' + +export const InputsPaginationConfig: ConfigType = { + key: 'InputsPagination', + chartKey: 'VInputsPagination', + conKey: 'VCInputsPagination', + title: '分页', + category: ChatCategoryEnum.INPUTS, + categoryName: ChatCategoryEnumName.INPUTS, + package: PackagesCategoryEnum.INFORMATIONS, + chartFrame: ChartFrameEnum.STATIC, + image: 'inputs_pagination.png' +} \ No newline at end of file diff --git a/src/packages/components/Informations/Inputs/InputsPagination/index.vue b/src/packages/components/Informations/Inputs/InputsPagination/index.vue new file mode 100644 index 00000000..2dc81c75 --- /dev/null +++ b/src/packages/components/Informations/Inputs/InputsPagination/index.vue @@ -0,0 +1,66 @@ +<template> + <div> + <n-pagination + @on-update:page="onChange" :style="`width:${w}px;`" + v-model:page="option.value.pageValue" + :page-count="option.value.dataset" + :page-slot="7" + show-size-picker + :page-sizes="option.value.sizeValue" + v-model:page-size="option.value.pageSize" + /> + </div> +</template> + +<script lang="ts" setup> +import { PropType, toRefs, shallowReactive, watch } from 'vue' +import { CreateComponentType } from '@/packages/index.d' +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' +import { useChartInteract } from '@/hooks' +import { InteractEventOn } from '@/enums/eventEnum' +import { ComponentInteractParamsEnum } from './interact' + +const props = defineProps({ + chartConfig: { + type: Object as PropType<CreateComponentType>, + required: true + } +}) + +const { w, h } = toRefs(props.chartConfig.attr) +const option = shallowReactive({ + value: { + pageValue: props.chartConfig.option.pageValue, + dataset:props.chartConfig.option.dataset, + sizeValue:props.chartConfig.option.sizeValue, + pageSize:props.chartConfig.option.pageSize + } +}) + +const onChange = (v: number,v2:number) => { + if(v == undefined) return; + // 存储到联动数据 + useChartInteract( + props.chartConfig, + useChartEditStore, + { + [ComponentInteractParamsEnum.DATA]: v , + [ComponentInteractParamsEnum.DATA2]:v2 + }, + InteractEventOn.CHANGE + ) +} + +// 手动更新 +watch( + () => props.chartConfig.option, + (newData: any) => { + option.value = newData + onChange(newData.pageValue,newData.pageSize) + }, + { + immediate: true, + deep: true + } +) +</script> \ No newline at end of file diff --git a/src/packages/components/Informations/Inputs/InputsPagination/interact.ts b/src/packages/components/Informations/Inputs/InputsPagination/interact.ts new file mode 100644 index 00000000..acc1453f --- /dev/null +++ b/src/packages/components/Informations/Inputs/InputsPagination/interact.ts @@ -0,0 +1,32 @@ +import { InteractEventOn, InteractActionsType } from '@/enums/eventEnum' + +// 时间组件类型 +export enum ComponentInteractEventEnum { + DATA = 'data' +} + +// 联动参数 +export enum ComponentInteractParamsEnum { + DATA = 'data', + DATA2 = 'data2' +} + +// 定义组件触发回调事件 +export const interactActions: InteractActionsType[] = [ + { + interactType: InteractEventOn.CHANGE, + interactName: '选择完成', + componentEmitEvents: { + [ComponentInteractEventEnum.DATA]: [ + { + value: ComponentInteractParamsEnum.DATA, + label: '页数' + }, + { + value: ComponentInteractParamsEnum.DATA2, + label: '每页条数' + } + ] + } + } +] \ No newline at end of file diff --git a/src/packages/components/Informations/Inputs/InputsTab/config.vue b/src/packages/components/Informations/Inputs/InputsTab/config.vue index 39d9cad5..c6e8c7c9 100644 --- a/src/packages/components/Informations/Inputs/InputsTab/config.vue +++ b/src/packages/components/Informations/Inputs/InputsTab/config.vue @@ -1,8 +1,11 @@ <template> <collapse-item name="标签页配置" :expanded="true"> - <setting-item-box name="默认值" :alone="true"> + <setting-item-box name="标签类型" :alone="true"> <n-select size="small" v-model:value="optionData.tabType" :options="tabTypeOptions" /> </setting-item-box> + <setting-item-box name="默认值" :alone="true"> + <n-select size="small" v-model:value="optionData.tabLabel" value-field="label" :options="optionData.dataset" /> + </setting-item-box> </collapse-item> </template> diff --git a/src/packages/components/Informations/Inputs/InputsTab/index.vue b/src/packages/components/Informations/Inputs/InputsTab/index.vue index d75bde97..a02900b9 100644 --- a/src/packages/components/Informations/Inputs/InputsTab/index.vue +++ b/src/packages/components/Informations/Inputs/InputsTab/index.vue @@ -1,5 +1,5 @@ <template> - <n-tabs :type="option.value.tabType" @update:value="onChange"> + <n-tabs :type="option.value.tabType" @update:value="onChange" :default-value="option.value.tabLabel"> <n-tab v-for="(item, index) in option.value.dataset" :name="item.label" :key="index"> {{ item.label }} </n-tab> </n-tabs> </template> diff --git a/src/packages/components/Informations/Inputs/index.ts b/src/packages/components/Informations/Inputs/index.ts index 1f4f22a7..215dd78a 100644 --- a/src/packages/components/Informations/Inputs/index.ts +++ b/src/packages/components/Informations/Inputs/index.ts @@ -1,5 +1,7 @@ import { InputsDateConfig } from './InputsDate/index' import { InputsSelectConfig } from './InputsSelect/index' import { InputsTabConfig } from './InputsTab/index' +import { InputsPaginationConfig } from "./InputsPagination/index"; +import { InputsInputConfig} from "./InputsInput/index"; -export default [InputsDateConfig, InputsSelectConfig, InputsTabConfig] +export default [InputsDateConfig, InputsSelectConfig, InputsTabConfig,InputsPaginationConfig,InputsInputConfig] diff --git a/src/packages/components/Informations/Texts/TextBarrage/config.vue b/src/packages/components/Informations/Texts/TextBarrage/config.vue index e2a5ad59..c149102d 100644 --- a/src/packages/components/Informations/Texts/TextBarrage/config.vue +++ b/src/packages/components/Informations/Texts/TextBarrage/config.vue @@ -1,89 +1,89 @@ -<template> - <collapse-item name="信息" :expanded="true"> - <setting-item-box name="文字" :alone="true"> - <setting-item> - <n-input v-model:value="optionData.dataset" size="small"></n-input> - </setting-item> - </setting-item-box> - </collapse-item> - - <collapse-item name="样式" :expanded="true"> - <setting-item-box name="文字"> - <setting-item name="颜色"> - <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.fontColor"></n-color-picker> - </setting-item> - <setting-item name="字体大小"> - <n-input-number v-model:value="optionData.fontSize" size="small" placeholder="字体大小"></n-input-number> - </setting-item> - <setting-item name="字体粗细"> - <n-select v-model:value="optionData.fontWeight" size="small" :options="fontWeightOptions" /> - </setting-item> - - <setting-item name="字间距"> - <n-input-number v-model:value="optionData.letterSpacing" size="small" placeholder="输入字间距"></n-input-number> - </setting-item> - </setting-item-box> - <setting-item-box name="阴影"> - <setting-item> - <n-space> - <n-switch v-model:value="optionData.showShadow" size="small" /> - <n-text>展示阴影</n-text> - </n-space> - </setting-item> - <setting-item name="颜色"> - <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.colorShadow"></n-color-picker - ></setting-item> - <setting-item name="x"> - <n-input-number v-model:value="optionData.hShadow" size="small"></n-input-number - ></setting-item> - <setting-item name="y"> - <n-input-number v-model:value="optionData.vShadow" size="small"></n-input-number - ></setting-item> - <setting-item name="模糊"> - <n-input-number v-model:value="optionData.blurShadow" size="small"></n-input-number - ></setting-item> - </setting-item-box> - - <setting-item-box name="动画"> - <setting-item name="动画速度"> - <n-input-number - v-model:value="optionData.animationSpeed" - size="small" - placeholder="动画速度" - :min="0" - ></n-input-number> - </setting-item> - <setting-item name="动画间隔"> - <n-input-number - v-model:value="optionData.animationTime" - size="small" - placeholder="动画间隔" - :min="0" - ></n-input-number> - </setting-item> - </setting-item-box> - </collapse-item> -</template> - -<script setup lang="ts"> -import { PropType } from 'vue' -import { option, FontWeightEnum, FontWeightObject } from './config' -import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' -const props = defineProps({ - optionData: { - type: Object as PropType<typeof option>, - required: true - } -}) - -const fontWeightOptions = [ - { - label: FontWeightEnum.NORMAL, - value: FontWeightObject[FontWeightEnum.NORMAL] - }, - { - label: FontWeightEnum.BOLD, - value: FontWeightObject[FontWeightEnum.BOLD] - } -] -</script> +<template> + <collapse-item name="信息" :expanded="true"> + <setting-item-box name="文字" :alone="true"> + <setting-item> + <n-input v-model:value="optionData.dataset" type="textarea" size="small"></n-input> + </setting-item> + </setting-item-box> + </collapse-item> + + <collapse-item name="样式" :expanded="true"> + <setting-item-box name="文字"> + <setting-item name="颜色"> + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.fontColor"></n-color-picker> + </setting-item> + <setting-item name="字体大小"> + <n-input-number v-model:value="optionData.fontSize" size="small" placeholder="字体大小"></n-input-number> + </setting-item> + <setting-item name="字体粗细"> + <n-select v-model:value="optionData.fontWeight" size="small" :options="fontWeightOptions" /> + </setting-item> + + <setting-item name="字间距"> + <n-input-number v-model:value="optionData.letterSpacing" size="small" placeholder="输入字间距"></n-input-number> + </setting-item> + </setting-item-box> + <setting-item-box name="阴影"> + <setting-item> + <n-space> + <n-switch v-model:value="optionData.showShadow" size="small" /> + <n-text>展示阴影</n-text> + </n-space> + </setting-item> + <setting-item name="颜色"> + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.colorShadow"></n-color-picker + ></setting-item> + <setting-item name="x"> + <n-input-number v-model:value="optionData.hShadow" size="small"></n-input-number + ></setting-item> + <setting-item name="y"> + <n-input-number v-model:value="optionData.vShadow" size="small"></n-input-number + ></setting-item> + <setting-item name="模糊"> + <n-input-number v-model:value="optionData.blurShadow" size="small"></n-input-number + ></setting-item> + </setting-item-box> + + <setting-item-box name="动画"> + <setting-item name="动画速度"> + <n-input-number + v-model:value="optionData.animationSpeed" + size="small" + placeholder="动画速度" + :min="0" + ></n-input-number> + </setting-item> + <setting-item name="动画间隔"> + <n-input-number + v-model:value="optionData.animationTime" + size="small" + placeholder="动画间隔" + :min="0" + ></n-input-number> + </setting-item> + </setting-item-box> + </collapse-item> +</template> + +<script setup lang="ts"> +import { PropType } from 'vue' +import { option, FontWeightEnum, FontWeightObject } from './config' +import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' +const props = defineProps({ + optionData: { + type: Object as PropType<typeof option>, + required: true + } +}) + +const fontWeightOptions = [ + { + label: FontWeightEnum.NORMAL, + value: FontWeightObject[FontWeightEnum.NORMAL] + }, + { + label: FontWeightEnum.BOLD, + value: FontWeightObject[FontWeightEnum.BOLD] + } +] +</script> diff --git a/src/packages/components/Informations/Texts/TextCommon/index.vue b/src/packages/components/Informations/Texts/TextCommon/index.vue index 9cd16502..6732df8c 100644 --- a/src/packages/components/Informations/Texts/TextCommon/index.vue +++ b/src/packages/components/Informations/Texts/TextCommon/index.vue @@ -1,7 +1,7 @@ <template> <div class="go-text-box"> <div class="content"> - <span style="cursor: pointer; white-space: pre-wrap" v-if="link" @click="click"></span> + <span style="cursor: pointer; white-space: pre-wrap" v-if="link" @click="click">{{ option.dataset }}</span> <span style="white-space: pre-wrap" v-else>{{ option.dataset }}</span> </div> </div> diff --git a/src/packages/components/Informations/Texts/TextGradient/config.vue b/src/packages/components/Informations/Texts/TextGradient/config.vue index 44e08757..a7ff8ebe 100644 --- a/src/packages/components/Informations/Texts/TextGradient/config.vue +++ b/src/packages/components/Informations/Texts/TextGradient/config.vue @@ -1,45 +1,45 @@ -<template> - <collapse-item name="信息" :expanded="true"> - <setting-item-box name="文字" :alone="true"> - <setting-item> - <n-input v-model:value="optionData.dataset" size="small"></n-input> - </setting-item> - </setting-item-box> - </collapse-item> - <collapse-item name="样式" :expanded="true"> - <setting-item-box name="文字"> - <setting-item name="字体大小"> - <n-input-number v-model:value="optionData.size" size="small" placeholder="字体大小"></n-input-number> - </setting-item> - </setting-item-box> - <setting-item-box name="渐变色参数"> - <setting-item name="起始值"> - <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.gradient.from"></n-color-picker> - </setting-item> - <setting-item name="结束值"> - <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.gradient.to"></n-color-picker> - </setting-item> - <setting-item name="偏移角度"> - <n-input-number v-model:value="optionData.gradient.deg" size="small" placeholder="颜色旋转"></n-input-number> - </setting-item> - </setting-item-box> - - </collapse-item> -</template> - -<script setup lang="ts"> -import { PropType } from 'vue' -import { option } from './config' -import { - CollapseItem, - SettingItemBox, - SettingItem -} from '@/components/Pages/ChartItemSetting' - -const props = defineProps({ - optionData: { - type: Object as PropType<typeof option>, - required: true - } -}) -</script> +<template> + <collapse-item name="信息" :expanded="true"> + <setting-item-box name="文字" :alone="true"> + <setting-item> + <n-input v-model:value="optionData.dataset" type="textarea" size="small"></n-input> + </setting-item> + </setting-item-box> + </collapse-item> + <collapse-item name="样式" :expanded="true"> + <setting-item-box name="文字"> + <setting-item name="字体大小"> + <n-input-number v-model:value="optionData.size" size="small" placeholder="字体大小"></n-input-number> + </setting-item> + </setting-item-box> + <setting-item-box name="渐变色参数"> + <setting-item name="起始值"> + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.gradient.from"></n-color-picker> + </setting-item> + <setting-item name="结束值"> + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.gradient.to"></n-color-picker> + </setting-item> + <setting-item name="偏移角度"> + <n-input-number v-model:value="optionData.gradient.deg" size="small" placeholder="颜色旋转"></n-input-number> + </setting-item> + </setting-item-box> + + </collapse-item> +</template> + +<script setup lang="ts"> +import { PropType } from 'vue' +import { option } from './config' +import { + CollapseItem, + SettingItemBox, + SettingItem +} from '@/components/Pages/ChartItemSetting' + +const props = defineProps({ + optionData: { + type: Object as PropType<typeof option>, + required: true + } +}) +</script> diff --git a/src/packages/components/Tables/Tables/TablesBasic/config.ts b/src/packages/components/Tables/Tables/TablesBasic/config.ts new file mode 100644 index 00000000..f1151226 --- /dev/null +++ b/src/packages/components/Tables/Tables/TablesBasic/config.ts @@ -0,0 +1,35 @@ +import cloneDeep from 'lodash/cloneDeep' +import { PublicConfigClass } from '@/packages/public' +import { CreateComponentType } from '@/packages/index.d' +import { chartInitConfig } from '@/settings/designSetting' +import { TablesBasicConfig } from './index' +import dataJson from './data.json' + +const { dimensions, source } = dataJson +export const option = { + dataset: { dimensions, source }, + pagination: { + page: 1, + pageSize: 5 + }, + align: 'center', + style: { + border: 'on', + singleColumn: 'off', + singleLine: 'off', + bottomBordered: 'on', + striped: 'on', + fontSize: 16, + borderWidth: 0, + borderColor: 'black', + borderStyle: 'solid' + }, + inputShow: 'none' +} + +export default class Config extends PublicConfigClass implements CreateComponentType { + public key = TablesBasicConfig.key + public attr = { ...chartInitConfig, w: 600, h: 300, zIndex: -1 } + public chartConfig = cloneDeep(TablesBasicConfig) + public option = cloneDeep(option) +} diff --git a/src/packages/components/Tables/Tables/TablesBasic/config.vue b/src/packages/components/Tables/Tables/TablesBasic/config.vue new file mode 100644 index 00000000..9e8369ae --- /dev/null +++ b/src/packages/components/Tables/Tables/TablesBasic/config.vue @@ -0,0 +1,162 @@ +<template> + <collapse-item name="表格设置" :expanded="true"> + <n-tag type="primary">若配置无响应,请在预览页面查看效果</n-tag> + <setting-item-box :alone="true" name="对齐方式"> + <setting-item :alone="true"> + <n-select + v-model:value="optionData.align" + size="small" + :options="[ + { label: '靠左', value: 'left' }, + { label: '居中', value: 'center' }, + { label: '靠右', value: 'right' } + ]" + /> + </setting-item> + </setting-item-box> + <setting-item-box :alone="false" name="分页设置"> + <setting-item name="默认页码" :alone="true"> + <n-input-number v-model:value="optionData.pagination.page" size="small" placeholder="字体大小"></n-input-number> + </setting-item> + <setting-item name="分页" :alone="true"> + <n-select v-model:value="optionData.pagination.pageSize" size="small" :options="page" /> + </setting-item> + </setting-item-box> + <setting-item-box :alone="false" name="表格数据"> + <SettingItem name="表头名称" class="form_name"> + <div style="width: 260px"> + <n-input v-model:value="header" size="small" placeholder="表头数据(英文','分割)"></n-input> + </div> + </SettingItem> + </setting-item-box> + <setting-item-box :alone="false" name="表格样式"> + <SettingItem name="显示边框" :alone="true"> + <n-select v-model:value="(optionData as any).style.border" size="small" :options="borderFlag" /> + </SettingItem> + <SettingItem name="底部边框" :alone="true"> + <n-select + v-model:value="(optionData as any).style.bottomBordered" + size="small" + :options="bottom_borderedFlag" + /> + </SettingItem> + <SettingItem name="列分割线" :alone="true"> + <n-select v-model:value="(optionData as any).style.singleLine" size="small" :options="columnFlag" /> + </SettingItem> + <SettingItem name="行分割线" :alone="true"> + <n-select v-model:value="(optionData as any).style.singleColumn" size="small" :options="lineFlag" /> + </SettingItem> + <SettingItem name="斑马条纹" :alone="true"> + <n-select v-model:value="(optionData as any).style.striped" size="small" :options="stripedFlag" /> + </SettingItem> + <setting-item name="字体大小" :alone="true"> + <n-input-number + v-model:value="optionData.style.fontSize" + :min="12" + size="small" + placeholder="字体大小" + ></n-input-number> + </setting-item> + <setting-item name="边框宽度" :alone="true"> + <n-input-number + v-model:value="optionData.style.borderWidth" + :min="0" + size="small" + placeholder="字体大小" + ></n-input-number> + </setting-item> + <setting-item name="边框颜色" :alone="true"> + <n-color-picker size="small" :modes="['rgb']" v-model:value="optionData.style.borderColor"></n-color-picker> + </setting-item> + <setting-item name="边框样式" :alone="true"> + <n-select v-model:value="optionData.style.borderStyle" size="small" :options="borderStyleFlag" /> + </setting-item> + <SettingItem name="表格搜索(前端静态搜索)" :alone="true"> + <n-select v-model:value="optionData.inputShow" size="small" :options="inputSelect" /> + </SettingItem> + </setting-item-box> + </collapse-item> +</template> + +<script setup lang="ts"> +import { PropType, watch, ref } from 'vue' +import { option } from './config' +import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' + +const page = [ + { label: '2', value: 2 }, + { label: '5', value: 5 }, + { label: '10', value: 10 }, + { label: '15', value: 15 }, + { label: '30', value: 30 } +] +const borderFlag = [ + { label: '显示', value: 'on' }, + { label: '不显示', value: 'off' } +] +const columnFlag = [ + { label: '显示', value: 'off' }, + { label: '不显示', value: 'on' } +] +const lineFlag = [ + { label: '显示', value: 'off' }, + { label: '不显示', value: 'on' } +] +const bottom_borderedFlag = [ + { label: '显示', value: 'on' }, + { label: '不显示', value: 'off' } +] +const stripedFlag = [ + { label: '显示', value: 'on' }, + { label: '不显示', value: 'off' } +] +const borderStyleFlag = [ + { label: '实线边框', value: 'solid' }, + { label: '虚线边框', value: 'dashed' }, + { label: '点状边框', value: 'dotted' }, + { label: '双线边框', value: 'double' } +] +const inputSelect = [ + { label: '停用', value: 'none' }, + { label: '启用', value: 'flex' } +] +const props = defineProps({ + optionData: { + type: Object as PropType<typeof option>, + required: true + } +}) + +const header = ref() +const median = ref<string[]>([]) +props.optionData.dataset.dimensions.forEach(item => { + median.value.push(item.title) +}) + +//转string +watch( + () => props.optionData, + () => { + median.value = [] + props.optionData.dataset.dimensions.forEach(item => { + median.value.push(item.title) + }) + header.value = median.value.toString() + }, + { + deep: false, + immediate: true + } +) + +//更新columns +watch([header], ([headerNew], [headerOld]) => { + if (headerNew !== headerOld) { + headerNew.split(',').forEach((item: string, index: number) => { + if (index + 1 <= props.optionData.dataset.dimensions.length) { + props.optionData.dataset.dimensions[index].title = headerNew.split(',')[index] + } + }) + } +}) +</script> diff --git a/src/packages/components/Tables/Tables/TablesBasic/data.json b/src/packages/components/Tables/Tables/TablesBasic/data.json new file mode 100644 index 00000000..29ba4085 --- /dev/null +++ b/src/packages/components/Tables/Tables/TablesBasic/data.json @@ -0,0 +1,60 @@ +{ + "dimensions": [ + { + "title": "产品名称", + "key": "productName" + }, + { + "title": "产品销量(万)", + "key": "totalSum" + }, + { + "title": "销售额(万)", + "key": "totalAmount" + } + ], + "source": [ + { + "key": 0, + "productName": "产品A1", + "totalSum": 10, + "totalAmount": 10 + }, + { + "key": 1, + "productName": "产品B1", + "totalSum": 10, + "totalAmount": 10 + }, + { + "key": 2, + "productName": "产品C1", + "totalSum": 10, + "totalAmount": 10 + }, + { + "key": 3, + "productName": "产品D1", + "totalSum": 10, + "totalAmount": 10 + }, + { + "key": 4, + "productName": "产品A2", + "totalSum": 10, + "totalAmount": 10 + }, + { + "key": 5, + "productName": "产品D2", + "totalSum": 10, + "totalAmount": 10 + }, + { + "key": 6, + "productName": "产品A3", + "totalSum": 10, + "totalAmount": 10 + } + ] +} diff --git a/src/packages/components/Tables/Tables/TablesBasic/index.ts b/src/packages/components/Tables/Tables/TablesBasic/index.ts new file mode 100644 index 00000000..9cb1f51a --- /dev/null +++ b/src/packages/components/Tables/Tables/TablesBasic/index.ts @@ -0,0 +1,14 @@ +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d' +import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d' + +export const TablesBasicConfig: ConfigType = { + key: 'TablesBasic', + chartKey: 'VTablesBasic', + conKey: 'VCTablesBasic', + title: '基础分页表格', + category: ChatCategoryEnum.TABLE, + categoryName: ChatCategoryEnumName.TABLE, + package: PackagesCategoryEnum.TABLES, + chartFrame: ChartFrameEnum.COMMON, + image: 'tables_basic.png' +} diff --git a/src/packages/components/Tables/Tables/TablesBasic/index.vue b/src/packages/components/Tables/Tables/TablesBasic/index.vue new file mode 100644 index 00000000..f3580744 --- /dev/null +++ b/src/packages/components/Tables/Tables/TablesBasic/index.vue @@ -0,0 +1,95 @@ +<template> + <div class="go-tables-basic"> + <n-input + v-model:value="inputData" + placeholder="请输入信息" + :style="`display: ${inputShow}`" + style="margin-bottom: 5px; float: right; width: 240px" + > + <template #prefix> + <n-icon :component="SearchIcon" /> + </template> + </n-input> + <n-data-table + :style="` + width: ${w}px; + height: ${h}px; + font-size: ${option.style.fontSize}px; + border-width: ${option.style.border === 'on' ? option.style.borderWidth : 0}px; + border-color: ${option.style.borderColor}; + border-style: ${option.style.borderStyle}`" + :bordered="option.style.border === 'on'" + :single-column="option.style.singleColumn === 'on'" + :single-line="option.style.singleLine === 'on'" + :bottom-bordered="option.style.bottomBordered === 'on'" + :striped="option.style.striped === 'on'" + :max-height="h" + size="small" + :columns="option.dataset.dimensions" + :data="filterData" + :pagination="pagination" + /> + </div> +</template> + +<script setup lang="ts"> +import { computed, PropType, toRefs, watch, reactive, ref } from 'vue' +import { CreateComponentType } from '@/packages/index.d' +import { icon } from '@/plugins' + +const props = defineProps({ + chartConfig: { + type: Object as PropType<CreateComponentType>, + required: true + } +}) + +const { SearchIcon } = icon.ionicons5 + +//查询字段 +const inputData = ref('') +//前台过滤 +const filterData = computed(() => { + return option?.dataset?.source?.filter((item: any) => { + return Object.values(item).some(val => { + return String(val).toLowerCase().includes(inputData.value.toLowerCase()) + }) + }) +}) + +const { align, pagination, inputShow } = toRefs(props.chartConfig.option) + +pagination.value.onChange = (page: number) => { + pagination.value.page = page +} + +const { w, h } = toRefs(props.chartConfig.attr) + +const option = reactive({ + dataset: props.chartConfig.option.dataset, + style: props.chartConfig.option.style +}) + +watch( + () => props.chartConfig.option.dataset, + (newData: any) => { + option.dataset = newData + option?.dataset?.dimensions?.forEach((header: any) => { + header.align = align.value + }) + }, + { + immediate: true, + deep: true + } +) +</script> + +<style lang="scss" scoped> +@include go('tables-basic') { + display: flex; + flex-direction: column; + gap: 15px; + align-items: flex-end; +} +</style> diff --git a/src/packages/components/Tables/Tables/index.ts b/src/packages/components/Tables/Tables/index.ts index dbad93ec..45ca3a63 100644 --- a/src/packages/components/Tables/Tables/index.ts +++ b/src/packages/components/Tables/Tables/index.ts @@ -1,4 +1,5 @@ import { TableListConfig } from './TableList' import { TableScrollBoardConfig } from './TableScrollBoard' +import { TablesBasicConfig } from "./TablesBasic/index"; -export default [TableListConfig, TableScrollBoardConfig] +export default [TableListConfig, TableScrollBoardConfig,TablesBasicConfig] diff --git a/src/store/modules/chartEditStore/chartEditStore.ts b/src/store/modules/chartEditStore/chartEditStore.ts index c0625007..f82292ff 100644 --- a/src/store/modules/chartEditStore/chartEditStore.ts +++ b/src/store/modules/chartEditStore/chartEditStore.ts @@ -525,8 +525,8 @@ export const useChartEditStore = defineStore({ } const parseHandle = (e: CreateComponentType | CreateComponentGroupType) => { e = cloneDeep(e) - e.attr.x = this.getMousePosition.x + 30 - e.attr.y = this.getMousePosition.y + 30 + e.attr.x = this.getMousePosition.startX + e.attr.y = this.getMousePosition.startY // 外层生成新 id e.id = getUUID() // 分组列表生成新 id @@ -535,11 +535,11 @@ export const useChartEditStore = defineStore({ item.id = getUUID() }) } - + return e } const isCut = recordCharts.type === HistoryActionTypeEnum.CUT - const targetList = Array.isArray(recordCharts.charts) ? recordCharts.charts : [ recordCharts.charts ] + const targetList = Array.isArray(recordCharts.charts) ? recordCharts.charts : [recordCharts.charts] // 多项 targetList.forEach((e: CreateComponentType | CreateComponentGroupType) => { this.addComponentList(parseHandle(e), undefined, true) @@ -639,7 +639,7 @@ export const useChartEditStore = defineStore({ } else { const group = historyData[0] as CreateComponentGroupType group.groupList.forEach(item => { - ids.push(item.id) + ids.unshift(item.id) }) } this.setGroup(ids, false) @@ -788,7 +788,7 @@ export const useChartEditStore = defineStore({ // 高 groupAttr.b = b < y + h ? y + h : b - targetList.push(item) + targetList.unshift(item) historyList.push(toRaw(item)) }) @@ -834,7 +834,7 @@ export const useChartEditStore = defineStore({ if (isHistory) chartHistoryStore.createUnGroupHistory(cloneDeep([targetGroup])) // 分离组件并还原位置属性 - targetGroup.groupList.forEach(item => { + targetGroup.groupList.reverse().forEach(item => { item.attr.x = item.attr.x + targetGroup.attr.x item.attr.y = item.attr.y + targetGroup.attr.y if (!callBack) { diff --git a/src/styles/common/mixins/mixins.scss b/src/styles/common/mixins/mixins.scss index ec6f2ccd..38228a91 100644 --- a/src/styles/common/mixins/mixins.scss +++ b/src/styles/common/mixins/mixins.scss @@ -1,78 +1,84 @@ -@import './config.scss'; -@import './function.scss'; -@import '../theme.scss'; - -@mixin go($block) { - $B: $namespace + '-' + $block; - .#{$B} { - @content; - } -} - -@mixin goId($block) { - $B: $namespace + '-' + $block; - ##{$B} { - @content; - } -} - -@mixin deep() { - :deep() { - @content; - } -} - -@mixin when($state) { - @at-root { - &.#{$state-prefix + $state} { - @content; - } - } -} - -@mixin themeify { - @each $theme-name, $theme-map in $themes { - $theme-map: $theme-map !global; - [data-theme='#{$theme-name}'] & { - @content; - } - } -} - -@mixin fetch-theme($param) { - @include themeify { - #{$param}: themed($param); - } -} - -@mixin fetch-theme-custom ($key, $value) { - @include themeify { - #{$key}: themed($value); - } -} - -//获取背景颜色 -@mixin fetch-bg-color($target) { - @include themeify { - background-color: themed($target); - } -} - -//获取背景渐变颜色 -@mixin background-image($target) { - @include themeify { - background-image: themed($target); - } -} - -//设置边框颜色 -@mixin fetch-border-color($target) { - @include themeify { - border-color: themed($target); - } -} -@mixin hover-border-color($target) { - @include themeify { - border: 1px solid themed($target); - } -} +@import './config.scss'; +@import './function.scss'; +@import '../theme.scss'; + +@mixin go($block) { + $B: $namespace + '-' + $block; + .#{$B} { + @content; + } +} + +@mixin dark { + [data-theme='dark'] { + @content; + } +} + +@mixin goId($block) { + $B: $namespace + '-' + $block; + ##{$B} { + @content; + } +} + +@mixin deep() { + :deep() { + @content; + } +} + +@mixin when($state) { + @at-root { + &.#{$state-prefix + $state} { + @content; + } + } +} + +@mixin themeify { + @each $theme-name, $theme-map in $themes { + $theme-map: $theme-map !global; + [data-theme='#{$theme-name}'] & { + @content; + } + } +} + +@mixin fetch-theme($param) { + @include themeify { + #{$param}: themed($param); + } +} + +@mixin fetch-theme-custom ($key, $value) { + @include themeify { + #{$key}: themed($value); + } +} + +//获取背景颜色 +@mixin fetch-bg-color($target) { + @include themeify { + background-color: themed($target); + } +} + +//获取背景渐变颜色 +@mixin background-image($target) { + @include themeify { + background-image: themed($target); + } +} + +//设置边框颜色 +@mixin fetch-border-color($target) { + @include themeify { + border-color: themed($target); + } +} +@mixin hover-border-color($target) { + @include themeify { + border: 1px solid themed($target); + } +} diff --git a/src/styles/pages/index.scss b/src/styles/pages/index.scss index b8f7272d..cde2d584 100644 --- a/src/styles/pages/index.scss +++ b/src/styles/pages/index.scss @@ -1,9 +1,15 @@ -// 页面全局样式 -// 去除高德地图 logo -.amap-logo { - display: none !important; - opacity: 0 !important; -} -.amap-copyright { - opacity: 0 !important; -} +// 页面全局样式 +// 去除高德地图 logo +.amap-logo { + display: none !important; + opacity: 0 !important; +} +.amap-copyright { + opacity: 0 !important; +} + +[data-theme='dark'] { + body { + background-color: #18181c; + } +} \ No newline at end of file diff --git a/src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataAjax/index.vue b/src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataAjax/index.vue index 203bb0e6..376a84b7 100644 --- a/src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataAjax/index.vue +++ b/src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataAjax/index.vue @@ -131,7 +131,11 @@ const sendHandle = async () => { loading.value = false if (res) { const { data } = res - if (!data && !targetData.value.filter) window['$message'].warning('您的数据不符合默认格式,请配置过滤器!') + if (!data && !targetData.value.filter) { + window['$message'].warning('您的数据不符合默认格式,请配置过滤器!') + showMatching.value = true + return + } targetData.value.option.dataset = newFunctionHandle(data, res, targetData.value.filter) showMatching.value = true return diff --git a/src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataMonacoEditor/index.vue b/src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataMonacoEditor/index.vue index 8bdb6440..5a8d11b3 100644 --- a/src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataMonacoEditor/index.vue +++ b/src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataMonacoEditor/index.vue @@ -35,7 +35,7 @@ <!-- 弹窗 --> <n-modal class="go-chart-data-monaco-editor" v-model:show="showModal" :mask-closable="false" :closeOnEsc="false"> - <n-card :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 1000px; height: 600px"> + <n-card :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 1200px; height: 700px"> <template #header> <n-space> <n-text>过滤器函数编辑器</n-text> @@ -49,12 +49,12 @@ <n-tag type="info"> <span class="func-keyword">function</span> filter(data, res) { </n-tag> - <monaco-editor v-model:modelValue="filter" width="460px" height="380px" language="javascript" /> + <monaco-editor v-model:modelValue="filter" width="660px" height="500px" language="javascript" /> <n-tag type="info">}</n-tag> </n-space> </div> - <n-divider vertical style="height: 480px" /> - <n-scrollbar style="max-height: 480px"> + <n-divider vertical style="height: 580px" /> + <n-scrollbar style="max-height: 580px"> <n-space :size="15" vertical> <div class="editor-data-show"> <n-space> diff --git a/src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataPond/index.vue b/src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataPond/index.vue index b51938dd..a7a849fa 100644 --- a/src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataPond/index.vue +++ b/src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataPond/index.vue @@ -117,7 +117,11 @@ const sendHandle = async () => { const res = await customizeHttp(toRaw(targetData.value.request), toRaw(chartEditStore.getRequestGlobalConfig)) loading.value = false if (res) { - if (!res?.data && !targetData.value.filter) window['$message'].warning('您的数据不符合默认格式,请配置过滤器!') + if (!res?.data && !targetData.value.filter) { + window['$message'].warning('您的数据不符合默认格式,请配置过滤器!') + showMatching.value = true + return + } targetData.value.option.dataset = newFunctionHandle(res?.data, res, targetData.value.filter) showMatching.value = true return diff --git a/src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventBaseHandle/index.vue b/src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventBaseHandle/index.vue index cc2a7525..81ac710f 100644 --- a/src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventBaseHandle/index.vue +++ b/src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventBaseHandle/index.vue @@ -16,7 +16,7 @@ <p> <span class="func-annotate">// {{ EventTypeName[eventName] }}</span> <br /> - <span class="func-keyword">async {{ eventName }}</span> (mouseEvent) { + <span class="func-keyword">async {{ eventName }}</span> (mouseEvent,components) { </p> <p class="go-ml-4"> <n-code :code="(targetData.events.baseEvent || {})[eventName] || ''" language="typescript"></n-code> @@ -52,7 +52,7 @@ <!-- 函数名称 --> <p class="go-pl-3"> <span class="func-keyword">async function </span> - <span class="func-keyNameWord">{{ eventName }}(mouseEvent) {</span> + <span class="func-keyNameWord">{{ eventName }}(mouseEvent,components) {</span> </p> <!-- 编辑主体 --> <monaco-editor v-model:modelValue="baseEvent[eventName]" height="480px" language="javascript" /> diff --git a/src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventInteraction/index.vue b/src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventInteraction/index.vue index b5a74bd4..614b3d15 100644 --- a/src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventInteraction/index.vue +++ b/src/views/chart/ContentConfigurations/components/ChartEvent/components/ChartEventInteraction/index.vue @@ -58,7 +58,7 @@ <help-outline-icon></help-outline-icon> </n-icon> </template> - <n-text>不支持「静态组件」</n-text> + <n-text>不支持「静态组件」支持「组件」「公共APi」</n-text> </n-tooltip> </template> <n-select @@ -89,7 +89,7 @@ </n-table> </setting-item-box> - <n-tag :bordered="false" type="primary"> 关联目标组件请求参数 </n-tag> + <n-tag :bordered="false" type="primary"> 关联目标请求参数 </n-tag> <setting-item-box :name="requestParamsItem" @@ -125,7 +125,7 @@ import { VNodeChild, computed } from 'vue' import { SelectOption, SelectGroupOption } from 'naive-ui' import { SettingItemBox, SettingItem, CollapseItem } from '@/components/Pages/ChartItemSetting' import { CreateComponentType, CreateComponentGroupType, ChartFrameEnum } from '@/packages/index.d' -import { RequestParamsTypeEnum } from '@/enums/httpEnum' +import { RequestParamsTypeEnum, RequestDataTypeEnum } from '@/enums/httpEnum' import { InteractEventOn, COMPONENT_INTERACT_EVENT_KET } from '@/enums/eventEnum' import { icon } from '@/plugins' import noData from '@/assets/images/canvas/noData.png' @@ -154,6 +154,11 @@ const option = computed(() => { // 绑定组件数据 request const fnGetRequest = (id: string | undefined, key: RequestParamsTypeEnum) => { if (!id) return {} + const globalConfigPindApr = chartEditStore.requestGlobalConfig.requestDataPond.find(item => { + return item.dataPondId === id + })?.dataPondRequestConfig.requestParams + + if (globalConfigPindApr) return globalConfigPindApr[key] return chartEditStore.componentList[chartEditStore.fetchTargetIndex(id)]?.request.requestParams[key] } @@ -178,12 +183,10 @@ const fnEventsOptions = (): Array<SelectOption | SelectGroupOption> => { iter: Array<CreateComponentType | CreateComponentGroupType>, val: CreateComponentType | CreateComponentGroupType ) => { - if (val.groupList && val.groupList.length > 0) { - iter.push(val) - } else { + if (!val.groupList && val.request.requestDataType === RequestDataTypeEnum.AJAX && val.request.requestUrl) { iter.push(val) } - return val.groupList ? [...iter, ...fnFlattern(val.groupList)] : iter + return val.groupList && val.groupList.length > 0 ? [...iter, ...fnFlattern(val.groupList)] : iter }, [] ) @@ -203,18 +206,26 @@ const fnEventsOptions = (): Array<SelectOption | SelectGroupOption> => { const mapOptionList = filterOptionList.map(item => ({ id: item.id, title: item.chartConfig.title, - disabled: false + disabled: false, + type: 'componentList' })) + const requestDataPond = chartEditStore.requestGlobalConfig.requestDataPond.map(item => ({ + id: item.dataPondId, + title: item.dataPondName, + disabled: false, + type: 'requestDataPond' + })) + const tarArr = requestDataPond.concat(mapOptionList) targetData.value.events.interactEvents?.forEach(iaItem => { - mapOptionList.forEach(optionItem => { + tarArr.forEach(optionItem => { if (optionItem.id === iaItem.interactComponentId) { optionItem.disabled = true } }) }) - return mapOptionList + return tarArr } // 新增模块 diff --git a/src/views/chart/ContentEdit/components/EditRule/index.vue b/src/views/chart/ContentEdit/components/EditRule/index.vue index e200bee7..ccefadb4 100644 --- a/src/views/chart/ContentEdit/components/EditRule/index.vue +++ b/src/views/chart/ContentEdit/components/EditRule/index.vue @@ -27,7 +27,7 @@ </div> </div> <!-- 修复右下角白点用的 --> - <div v-if="designStore.getDarkTheme" class="fix-edit-screens-block"></div> + <!-- <div v-if="designStore.getDarkTheme" class="fix-edit-screens-block"></div> --> </div> </template> <script setup lang="ts"> @@ -159,9 +159,11 @@ const dragCanvas = (e: any) => { const canvasBox = () => { const layoutDom = document.getElementById('go-chart-edit-layout') if (layoutDom) { + // 此处减去滚动条的宽度和高度 + const scrollW = 20 return { - height: layoutDom.clientHeight - 25, - width: layoutDom.clientWidth + height: layoutDom.clientHeight - scrollW, + width: layoutDom.clientWidth - scrollW } } return { @@ -288,7 +290,6 @@ window.onKeySpacePressHold = (isHold: boolean) => { <style lang="scss" scoped> @include go('sketch-rule') { - position: relative; overflow: hidden; width: 100%; height: 100%; @@ -319,6 +320,10 @@ window.onKeySpacePressHold = (isHold: boolean) => { border-radius: 5px; background-color: rgba(144, 146, 152, 0.3); } + // 修复右下角白点用的 + &::-webkit-scrollbar-corner { + background-color: transparent; + } } .fix-edit-screens-block { @@ -339,7 +344,7 @@ window.onKeySpacePressHold = (isHold: boolean) => { .canvas { position: absolute; - top: 50%; + top:50%; left: 50%; transform-origin: 50% 0; transform: translateY(-50%); diff --git a/src/views/preview/components/PreviewRenderList/index.vue b/src/views/preview/components/PreviewRenderList/index.vue index f685f983..4d44e678 100644 --- a/src/views/preview/components/PreviewRenderList/index.vue +++ b/src/views/preview/components/PreviewRenderList/index.vue @@ -10,7 +10,8 @@ ...getTransformStyle(item.styles), ...getStatusStyle(item.status), ...getPreviewConfigStyle(item.preview), - ...getBlendModeStyle(item.styles) as any + ...getBlendModeStyle(item.styles) as any, + ...getSizeStyle(item.attr) }" > <!-- 分组 --> @@ -74,7 +75,7 @@ const themeColor = computed(() => { // 组件渲染结束初始化数据池 clearMittDataPondMap() onMounted(() => { - initDataPond(chartEditStore.requestGlobalConfig) + initDataPond(useChartEditStore) }) </script> diff --git a/src/views/preview/hooks/useScale.hook.ts b/src/views/preview/hooks/useScale.hook.ts index 4a5698f5..2666f9c8 100644 --- a/src/views/preview/hooks/useScale.hook.ts +++ b/src/views/preview/hooks/useScale.hook.ts @@ -13,7 +13,42 @@ export const useScale = (localStorageInfo: ChartEditStorageType) => { const height = ref(localStorageInfo.editCanvasConfig.height) const scaleRef = ref({ width: 1, height: 1 }) - provide(SCALE_KEY, scaleRef); + provide(SCALE_KEY, scaleRef) + + // 监听鼠标滚轮 +ctrl 键 + const useAddWheelHandle = (removeEvent: Function) => { + addEventListener( + 'wheel', + (e: any) => { + if (window?.$KeyboardActive?.ctrl) { + e.preventDefault() + e.stopPropagation() + removeEvent() + const transform = previewRef.value?.style.transform + const regRes = transform.match(/scale\((\d+\.?\d*)*/) as RegExpMatchArray + const width = regRes[1] + const previewBoxDom = document.querySelector('.go-preview') as HTMLElement + const entityDom = document.querySelector('.go-preview-entity') as HTMLElement + if (previewBoxDom) { + previewBoxDom.style.overflow = 'unset' + previewBoxDom.style.position = 'absolute' + } + if (entityDom) { + entityDom.style.overflow = 'unset' + } + + if (e.wheelDelta > 0) { + const resNum = parseFloat(Number(width).toFixed(2)) + previewRef.value.style.transform = `scale(${resNum > 5 ? 5 : resNum + 0.1})` + } else { + const resNum = parseFloat(Number(width).toFixed(2)) + previewRef.value.style.transform = `scale(${resNum < 0.2 ? 0.2 : resNum - 0.1})` + } + } + }, + { passive: false } + ) + } const updateScaleRef = (scale: { width: number; height: number }) => { // 这里需要解构,保证赋值给scaleRef的为一个新对象 @@ -24,74 +59,82 @@ export const useScale = (localStorageInfo: ChartEditStorageType) => { // 屏幕适配 onMounted(() => { switch (localStorageInfo.editCanvasConfig.previewScaleType) { - case PreviewScaleEnum.FIT: (() => { - const { calcRate, windowResize, unWindowResize } = usePreviewFitScale( - width.value as number, - height.value as number, - previewRef.value, - updateScaleRef - ) - calcRate() - windowResize() - onUnmounted(() => { - unWindowResize() - }) - })() - break; - case PreviewScaleEnum.SCROLL_Y: (() => { - const { calcRate, windowResize, unWindowResize } = usePreviewScrollYScale( - width.value as number, - height.value as number, - previewRef.value, - (scale) => { - const dom = entityRef.value - dom.style.width = `${width.value * scale.width}px` - dom.style.height = `${height.value * scale.height}px` - updateScaleRef(scale) - } - ) - calcRate() - windowResize() - onUnmounted(() => { - unWindowResize() - }) - })() + case PreviewScaleEnum.FIT: + ;(() => { + const { calcRate, windowResize, unWindowResize } = usePreviewFitScale( + width.value as number, + height.value as number, + previewRef.value, + updateScaleRef + ) + calcRate() + windowResize() + useAddWheelHandle(unWindowResize) + onUnmounted(() => { + unWindowResize() + }) + })() + break + case PreviewScaleEnum.SCROLL_Y: + ;(() => { + const { calcRate, windowResize, unWindowResize } = usePreviewScrollYScale( + width.value as number, + height.value as number, + previewRef.value, + scale => { + const dom = entityRef.value + dom.style.width = `${width.value * scale.width}px` + dom.style.height = `${height.value * scale.height}px` + updateScaleRef(scale) + } + ) + calcRate() + windowResize() + useAddWheelHandle(unWindowResize) + onUnmounted(() => { + unWindowResize() + }) + })() - break; - case PreviewScaleEnum.SCROLL_X: (() => { - const { calcRate, windowResize, unWindowResize } = usePreviewScrollXScale( - width.value as number, - height.value as number, - previewRef.value, - (scale) => { - const dom = entityRef.value - dom.style.width = `${width.value * scale.width}px` - dom.style.height = `${height.value * scale.height}px` - updateScaleRef(scale) - } - ) - calcRate() - windowResize() - onUnmounted(() => { - unWindowResize() - }) - })() + break + case PreviewScaleEnum.SCROLL_X: + ;(() => { + const { calcRate, windowResize, unWindowResize } = usePreviewScrollXScale( + width.value as number, + height.value as number, + previewRef.value, + scale => { + const dom = entityRef.value + dom.style.width = `${width.value * scale.width}px` + dom.style.height = `${height.value * scale.height}px` + updateScaleRef(scale) + } + ) + calcRate() + windowResize() + useAddWheelHandle(unWindowResize) + onUnmounted(() => { + unWindowResize() + }) + })() - break; - case PreviewScaleEnum.FULL: (() => { - const { calcRate, windowResize, unWindowResize } = usePreviewFullScale( - width.value as number, - height.value as number, - previewRef.value, - updateScaleRef - ) - calcRate() - windowResize() - onUnmounted(() => { - unWindowResize() - }) - })() - break; + break + case PreviewScaleEnum.FULL: + ;(() => { + const { calcRate, windowResize, unWindowResize } = usePreviewFullScale( + width.value as number, + height.value as number, + previewRef.value, + updateScaleRef + ) + calcRate() + windowResize() + useAddWheelHandle(unWindowResize) + onUnmounted(() => { + unWindowResize() + }) + })() + break } }) diff --git a/src/views/preview/suspenseIndex.vue b/src/views/preview/suspenseIndex.vue index fdc3fdf6..bf2c528e 100644 --- a/src/views/preview/suspenseIndex.vue +++ b/src/views/preview/suspenseIndex.vue @@ -1,5 +1,5 @@ <template> - <div :class="`go-preview ${chartEditStore.editCanvasConfig.previewScaleType}`"> + <div :class="`go-preview ${chartEditStore.editCanvasConfig.previewScaleType}`" @mousedown="dragCanvas"> <template v-if="showEntity"> <!-- 实体区域 --> <div ref="entityRef" class="go-preview-entity"> @@ -30,7 +30,7 @@ import { computed } from 'vue' import { PreviewRenderList } from './components/PreviewRenderList' import { getFilterStyle, setTitle } from '@/utils' -import { getEditCanvasConfigStyle, getSessionStorageInfo } from './utils' +import { getEditCanvasConfigStyle, getSessionStorageInfo, keyRecordHandle, dragCanvas } from './utils' import { useComInstall } from './hooks/useComInstall.hook' import { useScale } from './hooks/useScale.hook' import { useStore } from './hooks/useStore.hook' @@ -60,6 +60,9 @@ const showEntity = computed(() => { useStore(chartEditStore) const { entityRef, previewRef } = useScale(chartEditStore) const { show } = useComInstall(chartEditStore) + +// 开启键盘监听 +keyRecordHandle() </script> <style lang="scss" scoped> diff --git a/src/views/preview/utils/drag.ts b/src/views/preview/utils/drag.ts new file mode 100644 index 00000000..b78bbb53 --- /dev/null +++ b/src/views/preview/utils/drag.ts @@ -0,0 +1,55 @@ +import { listen } from 'dom-helpers' +import throttle from 'lodash/throttle' + +let prevMoveXValue = [0, 0] +let prevMoveYValue = [0, 0] + +// 拖拽处理 +export const dragCanvas = (e: MouseEvent) => { + const previewBoxDom = document.querySelector('.go-preview') as HTMLElement + if (!previewBoxDom || previewBoxDom.style.position !== 'absolute') return + if (!window.$KeyboardActive?.space) return + + e.preventDefault() + e.stopPropagation() + // @ts-ignore + document.activeElement?.blur() + const startX = e.screenX + const startY = e.screenY + + const listenMousemove = listen( + window, + 'mousemove', + throttle((moveEvent: MouseEvent) => { + const nx = moveEvent.screenX - startX + const ny = moveEvent.screenY - startY + + const [prevMoveX1, prevMoveX2] = prevMoveXValue + const [prevMoveY1, prevMoveY2] = prevMoveYValue + + prevMoveXValue = [prevMoveX2, nx] + prevMoveYValue = [prevMoveY2, ny] + + // 去除小数部分 + if (previewBoxDom) { + const oldLeft = previewBoxDom.style.left ? Number(previewBoxDom.style.left.split('px')[0]) : 0 + const oldTop = previewBoxDom.style.top ? Number(previewBoxDom.style.top.split('px')[0]) : 0 + previewBoxDom.style.left = + oldLeft + + (prevMoveX2 > prevMoveX1 ? Math.abs(prevMoveX2 - prevMoveX1) : -Math.abs(prevMoveX2 - prevMoveX1)) + + 'px' + previewBoxDom.style.top = + oldTop + + (prevMoveY2 > prevMoveY1 ? Math.abs(prevMoveY2 - prevMoveY1) : -Math.abs(prevMoveY2 - prevMoveY1)) + + 'px' + } + }, 20) + ) + + const listenMouseup = listen(window, 'mouseup', () => { + prevMoveXValue = [0, 0] + prevMoveYValue = [0, 0] + listenMousemove() + listenMouseup() + }) +} diff --git a/src/views/preview/utils/index.ts b/src/views/preview/utils/index.ts index f0f79cfa..c5aa120f 100644 --- a/src/views/preview/utils/index.ts +++ b/src/views/preview/utils/index.ts @@ -1,2 +1,4 @@ -export * from './style' -export * from './storage' \ No newline at end of file +export * from './style' +export * from './storage' +export * from './keyboard' +export * from './drag' \ No newline at end of file diff --git a/src/views/preview/utils/keyboard.ts b/src/views/preview/utils/keyboard.ts new file mode 100644 index 00000000..bacaa4e7 --- /dev/null +++ b/src/views/preview/utils/keyboard.ts @@ -0,0 +1,49 @@ +// 处理键盘记录 +export const keyRecordHandle = () => { + // 默认赋值 + window.$KeyboardActive = { + ctrl: false, + space: false + } + + document.onkeydown = (e: KeyboardEvent) => { + const { keyCode } = e + if (keyCode == 32 && e.target == document.body) e.preventDefault() + + if ([17, 32].includes(keyCode) && window.$KeyboardActive) { + switch (keyCode) { + case 17: + window.$KeyboardActive.ctrl = true + break + case 32: + window.$KeyboardActive.space = true + const previewBoxDom = document.querySelector('.go-preview') as HTMLElement + if (previewBoxDom && previewBoxDom.style.position === 'absolute') { + previewBoxDom.style.cursor = 'move' + } + break + } + } + } + + document.onkeyup = (e: KeyboardEvent) => { + const { keyCode } = e + if (keyCode == 32 && e.target == document.body) e.preventDefault() + + if ([17, 32].includes(keyCode) && window.$KeyboardActive) { + switch (keyCode) { + case 17: + window.$KeyboardActive.ctrl = false + break + case 32: + window.$KeyboardActive.space = false + break + } + } + + const previewBoxDom = document.querySelector('.go-preview') as HTMLElement + if (previewBoxDom) { + previewBoxDom.style.cursor = 'default' + } + } +} diff --git a/types/shims-vue.d.ts b/types/shims-vue.d.ts index fbfc8922..fa19c5db 100644 --- a/types/shims-vue.d.ts +++ b/types/shims-vue.d.ts @@ -5,4 +5,5 @@ declare module '*.vue' { } declare module 'lodash/*' -declare module 'dom-helpers' \ No newline at end of file +declare module 'dom-helpers' +declare module '@iconify/vue' \ No newline at end of file