提交 71b6af3b authored 作者: lidongxu's avatar lidongxu

fix(levitatedsphere): 修复悬浮球工具的 bug

抬起事件应该在悬浮球上不应该在 window 上,防止切换导致 up 事件提前触发找不到悬浮球标签导致报错
上级 e3da9762
......@@ -25,13 +25,14 @@
"element-plus": "2.7.6",
"file-saver": "2.0.5",
"fuse.js": "6.6.2",
"gsap": "^3.12.5",
"js-beautify": "1.14.11",
"js-cookie": "3.0.5",
"jsencrypt": "3.3.2",
"nprogress": "0.2.0",
"pinia": "2.1.7",
"splitpanes": "3.1.5",
"vue": "3.4.31",
"vue": "^3.5.13",
"vue-count-to": "^1.0.13",
"vue-cropper": "1.1.1",
"vue-router": "4.4.0",
......
......@@ -84,6 +84,7 @@ $--color-info: #909399;
html {
--el-gray-1: rgba(0, 0, 0, 0.45);
--el-gray-2: #666666;
--el-gray-3: rgb(235, 235, 235);
/* 主页背景 */
.app-main {
......@@ -95,6 +96,8 @@ html {
html.dark {
--el-gray-1: white;
--el-gray-2: white;
--el-gray-3: #1d1e1f;
/* 默认通用 */
--el-bg-color: #141414;
......
......@@ -65,40 +65,34 @@ resize()
onMounted(() => {
window.addEventListener('resize', resize)
nextTick(() => {
let isDown = false
levitatedSphere.value.addEventListener('mousedown', (e) => {
const mousemove = (e) => {
e.preventDefault();
levitatedSphere.value.style.transition = 'none';
downPoint.x = e.clientX
downPoint.y = e.clientY
isDown = true
})
// 在拖拽过程中,组件应该跟随手指的移动而移动
window.addEventListener('mousemove', (e) => {
e.preventDefault();
if (isDown) {
let touch = e;
// 限制 left 和 top 的值,使其不会超出可视区域
left.value = Math.max(props.gapWidth, Math.min(touch.clientX - 20, clientWidth.value - props.itemWidth - props.gapWidth));
top.value = Math.max(0, Math.min(touch.clientY - 25, clientHeight.value - props.itemHeight));
}
});
// 拖拽结束后,重新调整组件的位置并重新设置过渡动画
window.addEventListener('mouseup', (e) => {
levitatedSphere.value.addEventListener('mousedown', (e) => {
e.preventDefault();
levitatedSphere.value.style.transition = 'none';
downPoint.x = e.clientX
downPoint.y = e.clientY
window.addEventListener('mousemove', mousemove);
})
levitatedSphere.value.addEventListener('mouseup', e => {
levitatedSphere.value.style.transition = 'all 0.5s';
if (left.value > document.documentElement.clientWidth / 2) {
left.value = document.documentElement.clientWidth - props.itemWidth - props.gapWidth;
} else {
left.value = props.gapWidth;
}
isDown = false
upPoint.x = e.clientX
upPoint.y = e.clientY
window.removeEventListener('mousemove', mousemove);
})
});
})
onBeforeUnmount(() => {
window.removeEventListener('resize', resize)
})
......
<template>
<div class="top-right-btn" :style="style">
<div class="top-right-btn"
:style="style">
<el-row>
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search">
<el-button circle icon="Search" @click="toggleSearch()" />
<el-tooltip class="item"
effect="dark"
:content="showSearch ? '隐藏搜索' : '显示搜索'"
placement="top"
v-if="search">
<el-button circle
icon="Search"
@click="toggleSearch()" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
<el-button circle icon="Refresh" @click="refresh()" />
<el-tooltip class="item"
effect="dark"
content="刷新"
placement="top">
<el-button circle
icon="Refresh"
@click="refresh()" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns">
<el-button circle icon="Menu" @click="showColumn()" v-if="showColumnsType == 'transfer'"/>
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px" v-if="showColumnsType == 'checkbox'">
<el-button circle icon="Menu" />
<el-tooltip class="item"
effect="dark"
content="显隐列"
placement="top"
v-if="columns">
<el-button circle
icon="Menu"
@click="showColumn()"
v-if="showColumnsType == 'transfer'" />
<el-dropdown trigger="click"
:hide-on-click="false"
style="padding-left: 12px"
v-if="showColumnsType == 'checkbox'">
<el-button circle
icon="Menu" />
<template #dropdown>
<el-dropdown-menu>
<template v-for="item in columns" :key="item.key">
<template v-for="item in columns"
:key="item.key">
<el-dropdown-item>
<el-checkbox :checked="item.visible" @change="checkboxChange($event, item.label)" :label="item.label" />
<el-checkbox :checked="item.visible"
@change="checkboxChange($event, item.label)"
:label="item.label" />
</el-dropdown-item>
</template>
</el-dropdown-menu>
......@@ -25,13 +51,13 @@
<!-- 自定义功能 -->
<slot></slot>
</el-row>
<el-dialog :title="title" v-model="open" append-to-body>
<el-transfer
:titles="['显示', '隐藏']"
<el-dialog :title="title"
v-model="open"
append-to-body>
<el-transfer :titles="['显示', '隐藏']"
v-model="value"
:data="columns"
@change="dataChange"
></el-transfer>
@change="dataChange"></el-transfer>
</el-dialog>
</div>
</template>
......@@ -120,17 +146,31 @@ function checkboxChange(event, label) {
</script>
<style lang='scss' scoped>
:deep(.el-transfer__button) {
<style lang='scss'
scoped>
:deep(.el-transfer__button) {
border-radius: 50%;
display: block;
margin-left: 0px;
}
:deep(.el-transfer__button:first-child) {
}
:deep(.el-transfer__button:first-child) {
margin-bottom: 10px;
}
:deep(.el-dropdown-menu__item) {
}
:deep(.el-dropdown-menu__item) {
line-height: 30px;
padding: 0 17px;
}
}
:deep(.el-button) {
margin-left: 12px;
}
.el-dropdown {
.el-button {
margin-left: 0;
}
}
</style>
// 表单在浏览器控制台的异常处理
// 处理控制台错误异常-对业务没影响就是不好看
// 错误如此:Blocked aria-hidden on an element because its descendant retained focus. ......
export default {
bind(el, binding) {
const ariaEls = el.querySelectorAll('.el-radio__original')
ariaEls.forEach((item) => {
item.removeAttribute('aria-hidden')
})
}
}
import hasRole from './permission/hasRole'
import hasPermi from './permission/hasPermi'
import copyText from './common/copyText'
import formError from './form/error'
export default function directive(app){
app.directive('hasRole', hasRole)
app.directive('hasPermi', hasPermi)
app.directive('copyText', copyText)
app.directive('removeConsoleError', formError)
}
......@@ -419,3 +419,20 @@ export function resetObjValue(obj, props) {
}
}
}
/**
* 根据字符串属性路径访问对象里的值
* @param {*} obj
* @param {*} path
* @returns
*/
export function getObjValueByPath(obj, path) {
const pathArr = path?.split('.')
let result = obj
for (let i = 0; i < pathArr?.length; i++) {
result = result[pathArr[i]]
}
if (result === null) return 0
return result
}
\ No newline at end of file
......@@ -28,9 +28,11 @@ export function divSafe(arg1, arg2) {
* @returns {string} 格式化后的字符串
*/
export function formatNumberWithUnit(value, extraDescription, bool, round) {
if (typeof value !== 'number') {
throw new Error('输入值必须是数字');
}
// if (typeof value !== 'number') {
// throw new Error('输入值必须是数字');
// }
// 如果 value 是空直接返回 0
if (value === 0 || value === '' || value === null || value === undefined) return 0;
// 不转换
if (!bool) {
......
......@@ -71,7 +71,7 @@
<script setup>
import GradientArea from './GradientArea.vue';
import TableList from './TableList.vue';
import { getComPrdListAPI } from '@/api'
import { getComPrdListAPI, getSycmListAPI } from '@/api'
import { generatorDayList, parseTime, getBrandColor, resetObjValue } from '@/utils'
import { useDatePickerOptions } from '@/hooks'
......@@ -328,11 +328,10 @@ const filterTableData = () => {
// 获取数据
const getList = async () => {
loading.value = true
const { data } = await getSycmStoreListAPI({
const { data } = await getSycmListAPI({
startDate: parseTime(queryParams.date[0], '{y}-{m}-{d}'),
endDate: parseTime(queryParams.date[1], '{y}-{m}-{d}'),
platformStore: queryParams.brandList.join(','),
prdId: queryParams.prdList.join(','),
dataType: queryParams.typeList.join(',')
})
......@@ -358,7 +357,7 @@ const dateMergeFn = () => {
// 默认打开页面请求一次所有数据,并保存在数据源
async function init() {
await getPrdList()
// await getList()
await getList()
};
onMounted(() => {
......
<template>
<div ref="chartRef"
:class="className"
:style="{ height: height, width: width }"></div>
</template>
<script setup>
import * as echarts from 'echarts'
import { useWindowResize } from '@/hooks'
const props = defineProps({
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '400px'
},
// 图表总体数据对象
chartData: {
type: Object,
required: true,
validator: (val) => {
// 必要属性
// title: 图表标题,data:图表系列数据, xData:图表x轴数据, yName:图表y轴名称
const requiredPrototype = ['title', 'data', 'xData', 'yName']
return requiredPrototype.every(key => {
return Object.prototype.hasOwnProperty.call(val, key)
})
}
},
// 图表是否显示工具
showTool: {
type: Boolean,
default: true
}
})
let chart = null
const chartRef = ref(null)
const myThousand = ref(true)
const setOptions = () => {
chart.setOption({
color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#444648', '#fc8452', '#9a60b4', '#ea7ccc'],
title: {
text: this.chartData.title,
},
tooltip: {
trigger: 'axis'
},
legend: {
data: this.chartData.data?.map(o => o.name)
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
toolbox: {
show: this.showTool,
feature: {
saveAsImage: {}, // 保存为图片
magicType: {
type: ['line', 'bar', 'stack', 'tiled'] // 切换图表类型
},
myThousandTool: {
show: true,
title: '切换万单位',
icon: 'path://M50,50 L100,50 L100,100 L150,100 L150,150 L100,150 L100,200 L50,200 L50,150 L0,150 L0,100 L50,100 Z',
onclick: () => {
this.myThousand = !this.myThousand
this.lockThousand = !this.lockThousand
this.setOptions()
}
}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: this.chartData.xData
},
yAxis: {
type: 'value',
name: (this.myThousand ? '(万)' : '') + this.chartData.yName
},
series: [
...this.chartData.data?.map(o => {
return {
name: o.name,
type: 'line',
data: o.data.map(num => {
return parseFloat((this.myThousand ? num / 10000 : num).toFixed(2))
})
}
})
]
})
}
watch(() => props.chartData, () => {
// 自动计算是否需要过万单位
// if (!this.lockThousand) {
// this.myThousand = this.chartData.data?.some(o => {
// return o.data.some(num => num >= 10000)
// })
// }
chart && setOptions()
})
watch(() => props.showTool, () => {
setOptions()
})
const initChart = () => {
chart = echarts.init(chartRef.value)
setOptions()
}
const resize = () => {
chart.value.resize()
}
useWindowResize(resize)
onMounted(() => {
nextTick(() => {
initChart()
})
})
onBeforeUnmount(() => {
if (!chart) {
return
}
chart.dispose()
chart = null
})
</script>
\ No newline at end of file
差异被折叠。
......@@ -27,7 +27,7 @@ export default defineConfig(({ mode, command }) => {
rewrite: (p) => p.replace(/^\/dev-api/, '')
},
'/qllan': {
target: 'http://192.168.140.189:8080',
target: 'http://192.168.140.31:8080',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/qllan/, '')
},
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论