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

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

抬起事件应该在悬浮球上不应该在 window 上,防止切换导致 up 事件提前触发找不到悬浮球标签导致报错
上级 e3da9762
...@@ -25,13 +25,14 @@ ...@@ -25,13 +25,14 @@
"element-plus": "2.7.6", "element-plus": "2.7.6",
"file-saver": "2.0.5", "file-saver": "2.0.5",
"fuse.js": "6.6.2", "fuse.js": "6.6.2",
"gsap": "^3.12.5",
"js-beautify": "1.14.11", "js-beautify": "1.14.11",
"js-cookie": "3.0.5", "js-cookie": "3.0.5",
"jsencrypt": "3.3.2", "jsencrypt": "3.3.2",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"pinia": "2.1.7", "pinia": "2.1.7",
"splitpanes": "3.1.5", "splitpanes": "3.1.5",
"vue": "3.4.31", "vue": "^3.5.13",
"vue-count-to": "^1.0.13", "vue-count-to": "^1.0.13",
"vue-cropper": "1.1.1", "vue-cropper": "1.1.1",
"vue-router": "4.4.0", "vue-router": "4.4.0",
......
...@@ -84,6 +84,7 @@ $--color-info: #909399; ...@@ -84,6 +84,7 @@ $--color-info: #909399;
html { html {
--el-gray-1: rgba(0, 0, 0, 0.45); --el-gray-1: rgba(0, 0, 0, 0.45);
--el-gray-2: #666666; --el-gray-2: #666666;
--el-gray-3: rgb(235, 235, 235);
/* 主页背景 */ /* 主页背景 */
.app-main { .app-main {
...@@ -95,6 +96,8 @@ html { ...@@ -95,6 +96,8 @@ html {
html.dark { html.dark {
--el-gray-1: white; --el-gray-1: white;
--el-gray-2: white; --el-gray-2: white;
--el-gray-3: #1d1e1f;
/* 默认通用 */ /* 默认通用 */
--el-bg-color: #141414; --el-bg-color: #141414;
......
...@@ -65,40 +65,34 @@ resize() ...@@ -65,40 +65,34 @@ resize()
onMounted(() => { onMounted(() => {
window.addEventListener('resize', resize) window.addEventListener('resize', resize)
nextTick(() => { nextTick(() => {
let isDown = false const mousemove = (e) => {
levitatedSphere.value.addEventListener('mousedown', (e) => {
e.preventDefault(); 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; let touch = e;
// 限制 left 和 top 的值,使其不会超出可视区域 // 限制 left 和 top 的值,使其不会超出可视区域
left.value = Math.max(props.gapWidth, Math.min(touch.clientX - 20, clientWidth.value - props.itemWidth - props.gapWidth)); 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)); top.value = Math.max(0, Math.min(touch.clientY - 25, clientHeight.value - props.itemHeight));
} }
}); levitatedSphere.value.addEventListener('mousedown', (e) => {
// 拖拽结束后,重新调整组件的位置并重新设置过渡动画 e.preventDefault();
window.addEventListener('mouseup', (e) => { 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'; levitatedSphere.value.style.transition = 'all 0.5s';
if (left.value > document.documentElement.clientWidth / 2) { if (left.value > document.documentElement.clientWidth / 2) {
left.value = document.documentElement.clientWidth - props.itemWidth - props.gapWidth; left.value = document.documentElement.clientWidth - props.itemWidth - props.gapWidth;
} else { } else {
left.value = props.gapWidth; left.value = props.gapWidth;
} }
isDown = false
upPoint.x = e.clientX upPoint.x = e.clientX
upPoint.y = e.clientY upPoint.y = e.clientY
window.removeEventListener('mousemove', mousemove);
}) })
}); });
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.removeEventListener('resize', resize) window.removeEventListener('resize', resize)
}) })
......
<template> <template>
<div class="top-right-btn" :style="style"> <div class="top-right-btn"
:style="style">
<el-row> <el-row>
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search"> <el-tooltip class="item"
<el-button circle icon="Search" @click="toggleSearch()" /> effect="dark"
:content="showSearch ? '隐藏搜索' : '显示搜索'"
placement="top"
v-if="search">
<el-button circle
icon="Search"
@click="toggleSearch()" />
</el-tooltip> </el-tooltip>
<el-tooltip class="item" effect="dark" content="刷新" placement="top"> <el-tooltip class="item"
<el-button circle icon="Refresh" @click="refresh()" /> effect="dark"
content="刷新"
placement="top">
<el-button circle
icon="Refresh"
@click="refresh()" />
</el-tooltip> </el-tooltip>
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns"> <el-tooltip class="item"
<el-button circle icon="Menu" @click="showColumn()" v-if="showColumnsType == 'transfer'"/> effect="dark"
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px" v-if="showColumnsType == 'checkbox'"> content="显隐列"
<el-button circle icon="Menu" /> 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> <template #dropdown>
<el-dropdown-menu> <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-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> </el-dropdown-item>
</template> </template>
</el-dropdown-menu> </el-dropdown-menu>
...@@ -25,13 +51,13 @@ ...@@ -25,13 +51,13 @@
<!-- 自定义功能 --> <!-- 自定义功能 -->
<slot></slot> <slot></slot>
</el-row> </el-row>
<el-dialog :title="title" v-model="open" append-to-body> <el-dialog :title="title"
<el-transfer v-model="open"
:titles="['显示', '隐藏']" append-to-body>
<el-transfer :titles="['显示', '隐藏']"
v-model="value" v-model="value"
:data="columns" :data="columns"
@change="dataChange" @change="dataChange"></el-transfer>
></el-transfer>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
...@@ -120,17 +146,31 @@ function checkboxChange(event, label) { ...@@ -120,17 +146,31 @@ function checkboxChange(event, label) {
</script> </script>
<style lang='scss' scoped> <style lang='scss'
:deep(.el-transfer__button) { scoped>
:deep(.el-transfer__button) {
border-radius: 50%; border-radius: 50%;
display: block; display: block;
margin-left: 0px; margin-left: 0px;
} }
:deep(.el-transfer__button:first-child) {
:deep(.el-transfer__button:first-child) {
margin-bottom: 10px; margin-bottom: 10px;
} }
:deep(.el-dropdown-menu__item) {
:deep(.el-dropdown-menu__item) {
line-height: 30px; line-height: 30px;
padding: 0 17px; padding: 0 17px;
} }
:deep(.el-button) {
margin-left: 12px;
}
.el-dropdown {
.el-button {
margin-left: 0;
}
}
</style> </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 hasRole from './permission/hasRole'
import hasPermi from './permission/hasPermi' import hasPermi from './permission/hasPermi'
import copyText from './common/copyText' import copyText from './common/copyText'
import formError from './form/error'
export default function directive(app){ export default function directive(app){
app.directive('hasRole', hasRole) app.directive('hasRole', hasRole)
app.directive('hasPermi', hasPermi) app.directive('hasPermi', hasPermi)
app.directive('copyText', copyText) app.directive('copyText', copyText)
app.directive('removeConsoleError', formError)
} }
...@@ -419,3 +419,20 @@ export function resetObjValue(obj, props) { ...@@ -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) { ...@@ -28,9 +28,11 @@ export function divSafe(arg1, arg2) {
* @returns {string} 格式化后的字符串 * @returns {string} 格式化后的字符串
*/ */
export function formatNumberWithUnit(value, extraDescription, bool, round) { export function formatNumberWithUnit(value, extraDescription, bool, round) {
if (typeof value !== 'number') { // if (typeof value !== 'number') {
throw new Error('输入值必须是数字'); // throw new Error('输入值必须是数字');
} // }
// 如果 value 是空直接返回 0
if (value === 0 || value === '' || value === null || value === undefined) return 0;
// 不转换 // 不转换
if (!bool) { if (!bool) {
......
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
<script setup> <script setup>
import GradientArea from './GradientArea.vue'; import GradientArea from './GradientArea.vue';
import TableList from './TableList.vue'; import TableList from './TableList.vue';
import { getComPrdListAPI } from '@/api' import { getComPrdListAPI, getSycmListAPI } from '@/api'
import { generatorDayList, parseTime, getBrandColor, resetObjValue } from '@/utils' import { generatorDayList, parseTime, getBrandColor, resetObjValue } from '@/utils'
import { useDatePickerOptions } from '@/hooks' import { useDatePickerOptions } from '@/hooks'
...@@ -328,11 +328,10 @@ const filterTableData = () => { ...@@ -328,11 +328,10 @@ const filterTableData = () => {
// 获取数据 // 获取数据
const getList = async () => { const getList = async () => {
loading.value = true loading.value = true
const { data } = await getSycmListAPI({
const { data } = await getSycmStoreListAPI({
startDate: parseTime(queryParams.date[0], '{y}-{m}-{d}'), startDate: parseTime(queryParams.date[0], '{y}-{m}-{d}'),
endDate: parseTime(queryParams.date[1], '{y}-{m}-{d}'), endDate: parseTime(queryParams.date[1], '{y}-{m}-{d}'),
platformStore: queryParams.brandList.join(','), prdId: queryParams.prdList.join(','),
dataType: queryParams.typeList.join(',') dataType: queryParams.typeList.join(',')
}) })
...@@ -358,7 +357,7 @@ const dateMergeFn = () => { ...@@ -358,7 +357,7 @@ const dateMergeFn = () => {
// 默认打开页面请求一次所有数据,并保存在数据源 // 默认打开页面请求一次所有数据,并保存在数据源
async function init() { async function init() {
await getPrdList() await getPrdList()
// await getList() await getList()
}; };
onMounted(() => { 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 }) => { ...@@ -27,7 +27,7 @@ export default defineConfig(({ mode, command }) => {
rewrite: (p) => p.replace(/^\/dev-api/, '') rewrite: (p) => p.replace(/^\/dev-api/, '')
}, },
'/qllan': { '/qllan': {
target: 'http://192.168.140.189:8080', target: 'http://192.168.140.31:8080',
changeOrigin: true, changeOrigin: true,
rewrite: (p) => p.replace(/^\/qllan/, '') rewrite: (p) => p.replace(/^\/qllan/, '')
}, },
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论