feat: 增强组件功能与样式优化

- 在多个组件中添加了新的属性和方法以提升功能性,如在卡片组件中引入 _ActiveTab 属性以管理活动标签。
- 优化了商品详情和商户页面的收藏逻辑,确保在未登录状态下引导用户登录。
- 更新了轮播组件的路由解析逻辑,增强了用户体验。
- 改进了投诉列表和收藏列表的样式,提升了响应式布局和可读性。
- 在全局配置中添加了对开发环境的错误处理,提升了调试体验。
This commit is contained in:
田香琪
2026-06-25 16:52:46 +08:00
parent 1b2e980fb6
commit ec948b5496
86 changed files with 1741 additions and 1050 deletions

View File

@@ -31,9 +31,8 @@
{{ showZoneText(zone) }}
</div>
</div>
<div class="flex">
<div class="hotzone-box-item-actions">
<div class="hotzone-btn" @click="editZone(index)">修改</div>
&nbsp;&nbsp;&nbsp;&nbsp;
<div class="hotzone-btn" @click="delZone(index)">删除</div>
</div>
</div>
@@ -241,13 +240,16 @@ export default {
}
> div:nth-of-type(2) {
width: 50%;
min-width: 0;
background: #f7f7f7;
overflow: hidden;
}
}
.hotzone-add-box-body {
height: 90%;
overflow-y: auto;
overflow-x: hidden;
}
.hotzone-box-item-main {
@@ -258,11 +260,18 @@ export default {
.hotzone-box-item {
align-items: center;
display: flex;
gap: 8px;
border-bottom: 1px solid #ededed;
font-size: 12px;
justify-content: space-between;
padding: 5px 10px 0;
width: 100%;
box-sizing: border-box;
min-width: 0;
> div:nth-child(2) {
flex: 1;
min-width: 0;
}
}
.hotzone-add-box-footer {
@@ -282,8 +291,18 @@ export default {
cursor: pointer;
}
.hotzone-box-item-actions {
display: flex;
flex-shrink: 0;
align-items: center;
gap: 8px;
white-space: nowrap;
}
.hotzone-box-item-text {
width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
}

View File

@@ -64,7 +64,7 @@
<img class="show-image" :src="zoneForm.img" alt />
</div>
<el-form :model="zoneForm" label-width="80px">
<el-form :model="zoneForm" label-width="90px" class="hz-edit-form">
<el-form-item label="跳转链接:">
<el-input type="textarea" v-if="zoneForm.type === 'other' && zoneForm.title === '外部链接'" v-model="zoneForm.link" />
<el-button size="small" type="primary" @click="handleSelectLink"
@@ -252,3 +252,9 @@ export default {
},
};
</script>
<style scoped lang="scss">
.hz-edit-form :deep(.el-form-item__label) {
white-space: nowrap;
}
</style>

View File

@@ -1,34 +1,31 @@
import _ from '../utils'
export default {
bind: function (el, binding, vnode) {
const MIN_LIMIT = _.MIN_LIMIT
mounted(el, binding) {
const handleMouseDown = (e) => {
const ctx = binding.instance
if (!ctx) return
el.addEventListener('mousedown', handleMouseDown, { passive: false })
function handleMouseDown(e) {
// console.log('additem', e)
e && e.preventDefault()
let itemInfo = {
top: _.getDistanceY(e, el),
left: _.getDistanceX(e, el),
width: 0,
height: 0
height: 0,
}
let container = _.getOffset(el)
const container = _.getOffset(el)
// Only used once at the beginning of init
let setting = {
const setting = {
topPer: _.decimalPoint(itemInfo.top / container.height),
leftPer: _.decimalPoint(itemInfo.left / container.width),
widthPer: 0,
heightPer: 0
heightPer: 0,
}
let preX = _.getPageX(e)
let preY = _.getPageY(e)
vnode.context.addItem(setting)// 这里去添加并发送了add通知不应该发送通知
ctx.addItem(setting)
window.addEventListener('mousemove', handleChange, { passive: false })
window.addEventListener('mouseup', handleMouseUp, { passive: false })
@@ -36,56 +33,58 @@ export default {
function handleChange(e) {
e && e.preventDefault()
let moveX = _.getPageX(e) - preX
let moveY = _.getPageY(e) - preY
const moveX = _.getPageX(e) - preX
const moveY = _.getPageY(e) - preY
preX = _.getPageX(e)
preY = _.getPageY(e)
// Not consider the direction of movement first, consider only the lower right drag point
let minLimit = 0
// 添加热区时判定鼠标释放时满足热区大于48*48时条件时生效
let styleInfo = _.dealBR(itemInfo, moveX, moveY, minLimit)
const minLimit = 0
const styleInfo = _.dealBR(itemInfo, moveX, moveY, minLimit)
// Boundary value processing 改变热区大小时边界条件的处理
itemInfo = _.dealEdgeValue(itemInfo, styleInfo, container, vnode.context.zones)
itemInfo = _.dealEdgeValue(itemInfo, styleInfo, container, ctx.zones)
Object.assign(el.lastElementChild.style, {
top: `${itemInfo.top}px`,
left: `${itemInfo.left}px`,
width: `${itemInfo.width}px`,
height: `${itemInfo.height}px`
height: `${itemInfo.height}px`,
})
}
function handleMouseUp() {
let perInfo = {
const perInfo = {
topPer: _.decimalPoint(itemInfo.top / container.height),
leftPer: _.decimalPoint(itemInfo.left / container.width),
widthPer: _.decimalPoint(itemInfo.width / container.width),
heightPer: _.decimalPoint(itemInfo.height / container.height),
img: "",
link: "",
type: "",
title: ""
img: '',
link: '',
type: '',
title: '',
}
if (vnode.context.isOverRange()) {
vnode.context.overRange() // 判断超出个数限制给overRange钩子抛回调
} else if (container.height < MIN_LIMIT && itemInfo.width > MIN_LIMIT) {
vnode.context.changeItem(Object.assign(perInfo, {
topPer: 0,
heightPer: 1
}), true)
} else if (container.width < MIN_LIMIT && itemInfo.height > MIN_LIMIT) {
vnode.context.changeItem(Object.assign(perInfo, {
leftper: 0,
widthPer: 1
}), true)
} else if (itemInfo.width > MIN_LIMIT && itemInfo.height > MIN_LIMIT) {
vnode.context.changeItem(perInfo, true)
if (ctx.isOverRange()) {
ctx.overRange()
} else if (container.height < _.MIN_LIMIT && itemInfo.width > _.MIN_LIMIT) {
ctx.changeItem(
Object.assign(perInfo, {
topPer: 0,
heightPer: 1,
}),
true
)
} else if (container.width < _.MIN_LIMIT && itemInfo.height > _.MIN_LIMIT) {
ctx.changeItem(
Object.assign(perInfo, {
leftper: 0,
widthPer: 1,
}),
true
)
} else if (itemInfo.width > _.MIN_LIMIT && itemInfo.height > _.MIN_LIMIT) {
ctx.changeItem(perInfo, true)
} else {
// 当添加区域超出范围或小于最小区域48*48时触发删除当亲绘制的热区并发送erase事件通知
vnode.context.eraseItem()
ctx.eraseItem()
}
window.removeEventListener('mousemove', handleChange)
@@ -93,9 +92,10 @@ export default {
}
}
el.$destroy = () => el.removeEventListener('mousedown', handleMouseDown)
el.__hotzoneAddDestroy = () => el.removeEventListener('mousedown', handleMouseDown)
el.addEventListener('mousedown', handleMouseDown, { passive: false })
},
beforeUnmount(el) {
el.__hotzoneAddDestroy?.()
},
unbind: function (el) {
el.$destroy()
}
}

View File

@@ -1,11 +1,12 @@
import _ from '../utils'
export default {
bind: function (el, binding, vnode) {
el.addEventListener('mousedown', handleMouseDown,{ passive: false })
mounted(el, binding) {
const handleMouseDown = (e) => {
const ctx = binding.instance
if (!ctx) return
function handleMouseDown (e) {
let pointer = e.target.dataset.pointer //元素上绑定的方法名
const pointer = e.target.dataset.pointer
if (!pointer) {
return
@@ -13,79 +14,81 @@ export default {
e && e.stopPropagation()
let zone = el.parentNode
let setting = vnode.context.setting
let currentIndex = vnode.context.index
let container = _.getOffset(zone.parentNode)
const zone = el.parentNode
const setting = ctx.setting
const currentIndex = ctx.index
const container = _.getOffset(zone.parentNode)
let itemInfo = {
width: _.getOffset(zone).width || 0,
height: _.getOffset(zone).height || 0,
top: setting.topPer * container.height || 0,
left: setting.leftPer * container.width || 0
left: setting.leftPer * container.width || 0,
}
let preX = _.getPageX(e)
let preY = _.getPageY(e)
let flag
// Hide the info displayed by hover
vnode.context.handlehideZone(true)
ctx.handlehideZone(true)
window.addEventListener('mousemove', handleChange,{ passive: false })
window.addEventListener('mouseup', handleMouseUp,{ passive: false })
window.addEventListener('mousemove', handleChange, { passive: false })
window.addEventListener('mouseup', handleMouseUp, { passive: false })
function handleChange (e) {
function handleChange(e) {
e && e.preventDefault()
flag = true
let moveX = _.getPageX(e) - preX
let moveY = _.getPageY(e) - preY
const moveX = _.getPageX(e) - preX
const moveY = _.getPageY(e) - preY
preX = _.getPageX(e)
preY = _.getPageY(e)
// Handling the situation when different dragging points are selected
let styleInfo = _[pointer](itemInfo, moveX, moveY)//调用对应的方法
// Boundary value processing
itemInfo = _.dealEdgeValue(itemInfo, styleInfo, container, vnode.context.$parent.zones, currentIndex)
const styleInfo = _[pointer](itemInfo, moveX, moveY)
itemInfo = _.dealEdgeValue(
itemInfo,
styleInfo,
container,
ctx.$parent.zones,
currentIndex
)
Object.assign(zone.style, {
top: `${itemInfo.top}px`,
left: `${itemInfo.left}px`,
width: `${itemInfo.width}px`,
height: `${itemInfo.height}px`
height: `${itemInfo.height}px`,
})
}
function handleMouseUp () {
function handleMouseUp() {
if (flag) {
flag = false
let perInfo = {
const perInfo = {
topPer: _.decimalPoint(itemInfo.top / container.height),
leftPer: _.decimalPoint(itemInfo.left / container.width),
widthPer: _.decimalPoint(itemInfo.width / container.width),
heightPer: _.decimalPoint(itemInfo.height / container.height)
heightPer: _.decimalPoint(itemInfo.height / container.height),
}
vnode.context.changeInfo(perInfo)
ctx.changeInfo(perInfo)
// 兼容数据无变更情况下导致 computed 不更新,数据仍为 px 时 resize 出现的问题
Object.assign(zone.style, {
top: `${itemInfo.top}px`,
left: `${itemInfo.left}px`,
width: `${itemInfo.width}px`,
height: `${itemInfo.height}px`
height: `${itemInfo.height}px`,
})
}
// Show the info
vnode.context.handlehideZone(false)
ctx.handlehideZone(false)
window.removeEventListener('mousemove', handleChange)
window.removeEventListener('mouseup', handleMouseUp)
}
}
el.$destroy = () => el.removeEventListener('mousedown', handleMouseDown)
el.__hotzoneResizeDestroy = () => el.removeEventListener('mousedown', handleMouseDown)
el.addEventListener('mousedown', handleMouseDown, { passive: false })
},
beforeUnmount(el) {
el.__hotzoneResizeDestroy?.()
},
unbind: function (el) {
el.$destroy()
}
}

View File

@@ -1,30 +1,31 @@
import _ from '../utils'
export default {
bind: function (el, binding, vnode) {
el.addEventListener('mousedown', handleMouseDown)
mounted(el, binding) {
let collision
function handleMouseDown (e) {
const handleMouseDown = (e) => {
const ctx = binding.instance
if (!ctx) return
e && e.stopPropagation()
let container = _.getOffset(el.parentNode)
const container = _.getOffset(el.parentNode)
let preX = _.getPageX(e)
let preY = _.getPageY(e)
let topPer
let leftPer
let flag
window.addEventListener('mousemove', handleChange,{ passive: false })
window.addEventListener('mouseup', handleMouseUp,{ passive: false })
window.addEventListener('mousemove', handleChange, { passive: false })
window.addEventListener('mouseup', handleMouseUp, { passive: false })
function handleChange (e) {
function handleChange(e) {
e && e.preventDefault()
flag = true
collision = false
// Hide the info displayed by hover
vnode.context.handlehideZone(true)
ctx.handlehideZone(true)
let setting = vnode.context.setting
let currentIndex = vnode.context.index
const setting = ctx.setting
const currentIndex = ctx.index
let moveX = _.getPageX(e) - preX
let moveY = _.getPageY(e) - preY
@@ -33,7 +34,6 @@ export default {
topPer = _.decimalPoint(moveY / container.height + setting.topPer)
leftPer = _.decimalPoint(moveX / container.width + setting.leftPer)
// Hotzone moving boundary processing
if (topPer < 0) {
topPer = 0
moveY = -container.height * setting.topPer
@@ -53,23 +53,20 @@ export default {
leftPer = 1 - setting.widthPer
moveX = container.width * (leftPer - setting.leftPer)
}
// 拖拽碰撞检测
if (vnode.context.$parent.zones.length > 1) {
let currentzones = JSON.parse(JSON.stringify(vnode.context.$parent.zones)).map((zone) => {
return {
left: (zone.leftPer || 0) * container.width,
top: (zone.topPer || 0) * container.height,
width: (zone.widthPer || 0) * container.width,
height: (zone.heightPer || 0) * container.height
}
})
// 矫正
let changeSetting = {}
changeSetting.left = setting.leftPer * container.width + moveX
changeSetting.top = setting.topPer * container.height + moveY
changeSetting.width = setting.widthPer * container.width
changeSetting.height = setting.heightPer * container.height
// 碰撞检测
if (ctx.$parent.zones.length > 1) {
const currentzones = JSON.parse(JSON.stringify(ctx.$parent.zones)).map((zone) => ({
left: (zone.leftPer || 0) * container.width,
top: (zone.topPer || 0) * container.height,
width: (zone.widthPer || 0) * container.width,
height: (zone.heightPer || 0) * container.height,
}))
const changeSetting = {
left: setting.leftPer * container.width + moveX,
top: setting.topPer * container.height + moveY,
width: setting.widthPer * container.width,
height: setting.heightPer * container.height,
}
for (let i = 0, len = currentzones.length; i < len; i++) {
if (currentIndex !== i && _.handleEgdeCollisions(currentzones[i], changeSetting)) {
collision = true
@@ -80,29 +77,29 @@ export default {
el.style.transform = `translate(${moveX}px, ${moveY}px)`
}
function handleMouseUp () {
function handleMouseUp() {
if (flag) {
flag = false
el.style.transform = 'translate(0, 0)'
if (!collision) {
vnode.context.changeInfo({
ctx.changeInfo({
topPer,
leftPer
leftPer,
})
}
}
// Show the info
vnode.context.handlehideZone(false)
ctx.handlehideZone(false)
window.removeEventListener('mousemove', handleChange)
window.removeEventListener('mouseup', handleMouseUp)
}
}
el.$destroy = () => el.removeEventListener('mousedown', handleMouseDown)
el.__hotzoneDragDestroy = () => el.removeEventListener('mousedown', handleMouseDown)
el.addEventListener('mousedown', handleMouseDown)
},
beforeUnmount(el) {
el.__hotzoneDragDestroy?.()
},
unbind: function (el) {
el.$destroy()
}
}

View File

@@ -8,12 +8,12 @@
destroy-on-close
@close="clickClose"
>
<template v-if="flag">
<template v-if="flag && hotzoneRes">
<hotzone
ref="hotzone"
@change="changeHotzone"
:zonesInit="res.zoneInfo"
:image="res.img"
:zonesInit="hotzoneRes.zoneInfo"
:image="hotzoneRes.img"
></hotzone>
</template>
<template #footer>
@@ -31,30 +31,35 @@ export default {
},
data() {
return {
flag: false, // modal显隐
flag: false,
hotzoneRes: null,
};
},
props: ["res"],
mounted() {},
methods: {
changeHotzone(info) {
if (this.hotzoneRes) {
this.hotzoneRes.zoneInfo = info;
}
this.$emit("changeZone", info);
},
// 关闭弹窗
clickClose() {
this.flag = false;
this.hotzoneRes = null;
this.$emit("closeFlag", false);
},
// 点击确认
clickOK() {
this.clickClose();
},
// 打开组件方法
open(type, mutiple) {
open(val) {
this.hotzoneRes = val || null;
if (this.hotzoneRes && !this.hotzoneRes.zoneInfo) {
this.hotzoneRes.zoneInfo = [];
}
this.flag = true;
},
// 关闭组件
close() {
this.flag = false;
this.hotzoneRes = null;
},
},
};

View File

@@ -58,8 +58,9 @@ export default {
},
// 关闭弹窗
clickClose() {
this.$emit("closeFlag", false);
this.flag = false;
this.goodsFlag = false;
this.$emit("closeFlag", false);
},
// 单选商品
singleGoods() {