Files
Neo-ZQYY/apps/DEMO-miniprogram/miniprogram/components/note-modal/note-modal.ts

245 lines
7.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Component({
properties: {
visible: {
type: Boolean,
value: false,
},
customerName: {
type: String,
value: '',
},
initialScore: {
type: Number,
value: 0,
},
initialContent: {
type: String,
value: '',
},
/** 是否显示展开/收起按钮 */
showExpandBtn: {
type: Boolean,
value: true,
},
/** 是否显示评分区域 */
showRating: {
type: Boolean,
value: true,
},
},
data: {
ratingExpanded: false, // 默认收起
serviceScore: 0, // 再次服务此客户 1-5
returnScore: 0, // 再来店可能性 1-5
content: '',
keyboardHeight: 0, // 键盘高度 px
canSave: false, // 是否可保存
},
lifetimes: {
created() {
// 初始化私有缓存(不走 setData避免触发渲染
;(this as any)._heartContainerRect = null
;(this as any)._ballContainerRect = null
},
},
observers: {
visible(val: boolean) {
if (val) {
// 打开弹窗时重置
this.setData({
ratingExpanded: this.data.showRating && !this.data.showExpandBtn, // 只有在显示评分且无按钮时才默认展开
serviceScore: 0,
returnScore: 0,
content: this.data.initialContent || '',
keyboardHeight: 0,
canSave: false,
})
// 多次重试获取容器位置信息,确保首次也能正确缓存
this._retryGetContainerRects(0)
} else {
// 关闭时重置键盘高度
this.setData({ keyboardHeight: 0 })
}
},
// 任意评分或内容变化时重新计算 canSave
'serviceScore, returnScore, content, showRating'(
serviceScore: number,
returnScore: number,
content: string,
showRating: boolean
) {
// 如果不显示评分,只需要有内容即可保存;否则需要评分和内容都有
const canSave = showRating
? serviceScore > 0 && returnScore > 0 && content.trim().length > 0
: content.trim().length > 0
this.setData({ canSave })
},
},
methods: {
/** 缓存容器位置信息 */
_cacheContainerRects() {
const query = this.createSelectorQuery()
query.select('.rating-right-heart').boundingClientRect()
query.select('.rating-right-ball').boundingClientRect()
query.exec((res) => {
if (res[0]) (this as any)._heartContainerRect = res[0] as WechatMiniprogram.BoundingClientRectCallbackResult
if (res[1]) (this as any)._ballContainerRect = res[1] as WechatMiniprogram.BoundingClientRectCallbackResult
})
},
/** 重试获取容器位置,确保首次打开也能正确缓存 */
_retryGetContainerRects(attempt: number) {
const maxAttempts = 5
const delay = 50 + attempt * 50 // 50ms, 100ms, 150ms, 200ms, 250ms
setTimeout(() => {
this._cacheContainerRects()
// 检查是否成功缓存,如果没有且还有重试次数,继续重试
const heartRect = (this as any)._heartContainerRect
const ballRect = (this as any)._ballContainerRect
if ((!heartRect || !ballRect) && attempt < maxAttempts - 1) {
this._retryGetContainerRects(attempt + 1)
}
}, delay)
},
/** 切换展开/收起 */
onToggleExpand() {
const nextExpanded = !this.data.ratingExpanded
this.setData({ ratingExpanded: nextExpanded })
// 展开后重新获取位置
if (nextExpanded) {
setTimeout(() => {
this._cacheContainerRects()
}, 100)
}
},
/** 爱心点击 */
onHeartTap(e: WechatMiniprogram.BaseEvent) {
const score = e.currentTarget.dataset.score as number
this.setData({ serviceScore: score })
},
/** 爱心区域触摸开始 */
onHeartTouchStart(e: WechatMiniprogram.TouchEvent) {
this._updateHeartScore(e)
},
/** 爱心区域触摸移动 */
onHeartTouchMove(e: WechatMiniprogram.TouchEvent) {
this._updateHeartScore(e)
},
/** 更新爱心评分 */
_updateHeartScore(e: WechatMiniprogram.TouchEvent) {
const container = (this as any)._heartContainerRect as WechatMiniprogram.BoundingClientRectCallbackResult | null
if (!container) return
const touch = e.touches[0]
// 垂直方向超出不影响评分,仅用 X 轴计算
let relativeX = touch.clientX - container.left
// 水平边界:最左=1分最右=5分
if (relativeX < 0) relativeX = 0
if (relativeX > container.width) relativeX = container.width
// 计算分数 (1-5)
const score = Math.ceil((relativeX / container.width) * 5)
const finalScore = Math.max(1, Math.min(5, score))
if (finalScore !== this.data.serviceScore) {
this.setData({ serviceScore: finalScore })
}
},
/** 台球点击 */
onBallTap(e: WechatMiniprogram.BaseEvent) {
const score = e.currentTarget.dataset.score as number
this.setData({ returnScore: score })
},
/** 台球区域触摸开始 */
onBallTouchStart(e: WechatMiniprogram.TouchEvent) {
this._updateBallScore(e)
},
/** 台球区域触摸移动 */
onBallTouchMove(e: WechatMiniprogram.TouchEvent) {
this._updateBallScore(e)
},
/** 更新台球评分 */
_updateBallScore(e: WechatMiniprogram.TouchEvent) {
const container = (this as any)._ballContainerRect as WechatMiniprogram.BoundingClientRectCallbackResult | null
if (!container) return
const touch = e.touches[0]
// 垂直方向超出不影响评分,仅用 X 轴计算
let relativeX = touch.clientX - container.left
// 水平边界:最左=1分最右=5分
if (relativeX < 0) relativeX = 0
if (relativeX > container.width) relativeX = container.width
// 计算分数 (1-5)
const score = Math.ceil((relativeX / container.width) * 5)
const finalScore = Math.max(1, Math.min(5, score))
if (finalScore !== this.data.returnScore) {
this.setData({ returnScore: finalScore })
}
},
/** 文本输入 */
onContentInput(e: WechatMiniprogram.CustomEvent<{ value: string }>) {
this.setData({ content: e.detail.value })
},
/** 键盘弹出 */
onTextareaFocus(e: WechatMiniprogram.InputEvent) {
let height = (e as any).detail?.height ?? 0
// 修复首次激活时键盘高度可能为0需要设置最小值确保弹窗移动
if (height === 0) {
height = 260 // 微信小程序默认键盘高度约 260px
}
this.setData({ keyboardHeight: height })
},
/** 键盘收起 */
onTextareaBlur() {
this.setData({ keyboardHeight: 0 })
},
/** 确认保存 */
onConfirm() {
if (!this.data.canSave) return
// 计算综合评分取两个评分的平均值转换为10分制
const avgScore = (this.data.serviceScore + this.data.returnScore) / 2
const finalScore = avgScore * 2 // 转换为10分制
this.triggerEvent('confirm', {
score: finalScore,
content: this.data.content.trim(),
serviceScore: this.data.serviceScore,
returnScore: this.data.returnScore,
})
},
/** 取消 */
onCancel() {
this.triggerEvent('cancel')
},
/** 阻止冒泡 */
noop() {},
},
})