Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad8bc1f152 | ||
|
|
348d490de8 | ||
|
|
d3e0307854 | ||
|
|
0a678cb387 |
@@ -521,6 +521,10 @@ export const getPhoneNumberAPI = (data) => {
|
|||||||
return request("POST", "/index/getPhone", data);
|
return request("POST", "/index/getPhone", data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getPhoneNumberAPIv2 = (data) => {
|
||||||
|
return request("POST", "/index/getPhone/v2", data);
|
||||||
|
};
|
||||||
|
|
||||||
export const getPointBookRankListAPI = (page = 1) => {
|
export const getPointBookRankListAPI = (page = 1) => {
|
||||||
return request(
|
return request(
|
||||||
"GET",
|
"GET",
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ const phase = ref("idle");
|
|||||||
const activePlayKey = ref("");
|
const activePlayKey = ref("");
|
||||||
const animationKey = ref("");
|
const animationKey = ref("");
|
||||||
const impactEmitted = ref(false);
|
const impactEmitted = ref(false);
|
||||||
|
const activeShot = ref(null);
|
||||||
let timers = [];
|
let timers = [];
|
||||||
|
|
||||||
const isActive = computed(() => phase.value !== "idle");
|
const isActive = computed(() => phase.value !== "idle");
|
||||||
@@ -54,9 +55,17 @@ const safeTargetSize = computed(() => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function hasShotPoint(shot) {
|
||||||
|
const x = Number(shot?.x);
|
||||||
|
const y = Number(shot?.y);
|
||||||
|
return Number.isFinite(x) && Number.isFinite(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
const effectiveShot = computed(() => activeShot.value || props.shot);
|
||||||
|
|
||||||
const shotPoint = computed(() => {
|
const shotPoint = computed(() => {
|
||||||
const x = Number(props.shot?.x);
|
const x = Number(effectiveShot.value?.x);
|
||||||
const y = Number(props.shot?.y);
|
const y = Number(effectiveShot.value?.y);
|
||||||
return {
|
return {
|
||||||
x: Number.isFinite(x) ? x : 0,
|
x: Number.isFinite(x) ? x : 0,
|
||||||
y: Number.isFinite(y) ? y : 0,
|
y: Number.isFinite(y) ? y : 0,
|
||||||
@@ -179,16 +188,20 @@ function finish(playKey) {
|
|||||||
clearTimers();
|
clearTimers();
|
||||||
phase.value = "idle";
|
phase.value = "idle";
|
||||||
activePlayKey.value = "";
|
activePlayKey.value = "";
|
||||||
|
activeShot.value = null;
|
||||||
emit("complete", playKey);
|
emit("complete", playKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
function play() {
|
function play() {
|
||||||
if (!props.playKey || !props.shot || !props.shot.ring) return;
|
if (!props.playKey || !props.shot || !props.shot.ring || !hasShotPoint(props.shot)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
clearTimers();
|
clearTimers();
|
||||||
activePlayKey.value = props.playKey;
|
activePlayKey.value = props.playKey;
|
||||||
animationKey.value = `${props.playKey}`;
|
animationKey.value = `${props.playKey}`;
|
||||||
impactEmitted.value = false;
|
impactEmitted.value = false;
|
||||||
|
activeShot.value = { ...props.shot };
|
||||||
phase.value = "playing";
|
phase.value = "playing";
|
||||||
|
|
||||||
queueTimer(() => {
|
queueTimer(() => {
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, onMounted, onBeforeUnmount, computed, nextTick } from "vue";
|
import {
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
onMounted,
|
||||||
|
onBeforeUnmount,
|
||||||
|
computed,
|
||||||
|
nextTick,
|
||||||
|
getCurrentInstance,
|
||||||
|
} from "vue";
|
||||||
import PointSwitcher from "@/components/PointSwitcher.vue";
|
import PointSwitcher from "@/components/PointSwitcher.vue";
|
||||||
import BowShotEffect from "@/components/BowShotEffect.vue";
|
import BowShotEffect from "@/components/BowShotEffect.vue";
|
||||||
|
|
||||||
@@ -56,8 +64,6 @@ const props = defineProps({
|
|||||||
const pMode = ref(true);
|
const pMode = ref(true);
|
||||||
const latestOne = ref(null);
|
const latestOne = ref(null);
|
||||||
const bluelatestOne = ref(null);
|
const bluelatestOne = ref(null);
|
||||||
const prevScores = ref([]);
|
|
||||||
const prevBlueScores = ref([]);
|
|
||||||
const timer = ref(null);
|
const timer = ref(null);
|
||||||
const dirTimer = ref(null);
|
const dirTimer = ref(null);
|
||||||
const angle = ref(null);
|
const angle = ref(null);
|
||||||
@@ -66,7 +72,9 @@ const shotEffect = ref(null);
|
|||||||
const hiddenRedLatestKey = ref("");
|
const hiddenRedLatestKey = ref("");
|
||||||
const hiddenBlueLatestKey = ref("");
|
const hiddenBlueLatestKey = ref("");
|
||||||
const targetShaking = ref(false);
|
const targetShaking = ref(false);
|
||||||
|
const targetSize = ref({ width: 0, height: 0 });
|
||||||
const shakeTimer = ref(null);
|
const shakeTimer = ref(null);
|
||||||
|
const instance = getCurrentInstance();
|
||||||
const ROUND_TIP_OFFSET_Y = -32;
|
const ROUND_TIP_OFFSET_Y = -32;
|
||||||
const EXPERIENCE_TIP_OFFSET_Y = -68;
|
const EXPERIENCE_TIP_OFFSET_Y = -68;
|
||||||
|
|
||||||
@@ -82,8 +90,14 @@ function buildShotEffectKey(team, shot, index) {
|
|||||||
].join("-");
|
].join("-");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasShotPoint(shot) {
|
||||||
|
const x = Number(shot?.x);
|
||||||
|
const y = Number(shot?.y);
|
||||||
|
return Number.isFinite(x) && Number.isFinite(y);
|
||||||
|
}
|
||||||
|
|
||||||
function shouldPlayShotEffect(shot) {
|
function shouldPlayShotEffect(shot) {
|
||||||
return props.isSvip && !!shot && Number(shot.ring) > 0;
|
return props.isSvip && !!shot && Number(shot.ring) > 0 && hasShotPoint(shot);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearTipTimer() {
|
function clearTipTimer() {
|
||||||
@@ -154,6 +168,29 @@ function shakeTarget() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateTargetSize() {
|
||||||
|
nextTick(() => {
|
||||||
|
const query = instance?.proxy
|
||||||
|
? uni.createSelectorQuery().in(instance.proxy)
|
||||||
|
: uni.createSelectorQuery();
|
||||||
|
|
||||||
|
query
|
||||||
|
.select(".target")
|
||||||
|
.boundingClientRect((rect) => {
|
||||||
|
const width = Number(rect?.width);
|
||||||
|
const height = Number(rect?.height);
|
||||||
|
if (!Number.isFinite(width) || !Number.isFinite(height)) return;
|
||||||
|
if (width <= 0 || height <= 0) return;
|
||||||
|
targetSize.value = { width, height };
|
||||||
|
})
|
||||||
|
.exec();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleWindowResize() {
|
||||||
|
updateTargetSize();
|
||||||
|
}
|
||||||
|
|
||||||
function shouldHideRedHit(index) {
|
function shouldHideRedHit(index) {
|
||||||
return !!hiddenRedLatestKey.value && index === props.scores.length - 1;
|
return !!hiddenRedLatestKey.value && index === props.scores.length - 1;
|
||||||
}
|
}
|
||||||
@@ -163,46 +200,44 @@ function shouldHideBlueHit(index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.scores,
|
() => props.scores.length,
|
||||||
(newVal) => {
|
(newLen, oldLen) => {
|
||||||
if (newVal.length - prevScores.value.length === 1) {
|
if (newLen === oldLen + 1) {
|
||||||
const latestShot = newVal[newVal.length - 1];
|
const latestShot = props.scores[newLen - 1];
|
||||||
if (shouldPlayShotEffect(latestShot)) {
|
if (shouldPlayShotEffect(latestShot)) {
|
||||||
triggerShotEffect("red", latestShot, newVal.length - 1);
|
triggerShotEffect("red", latestShot, newLen - 1);
|
||||||
} else {
|
} else {
|
||||||
showShotTip("red", latestShot);
|
showShotTip("red", latestShot);
|
||||||
}
|
}
|
||||||
} else if (newVal.length <= prevScores.value.length) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newLen < oldLen) {
|
||||||
latestOne.value = null;
|
latestOne.value = null;
|
||||||
hiddenRedLatestKey.value = "";
|
hiddenRedLatestKey.value = "";
|
||||||
if (shotEffect.value?.team === "red") shotEffect.value = null;
|
if (shotEffect.value?.team === "red") shotEffect.value = null;
|
||||||
}
|
}
|
||||||
prevScores.value = [...newVal];
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.blueScores,
|
() => props.blueScores.length,
|
||||||
(newVal) => {
|
(newLen, oldLen) => {
|
||||||
if (newVal.length - prevBlueScores.value.length === 1) {
|
if (newLen === oldLen + 1) {
|
||||||
const latestShot = newVal[newVal.length - 1];
|
const latestShot = props.blueScores[newLen - 1];
|
||||||
if (shouldPlayShotEffect(latestShot)) {
|
if (shouldPlayShotEffect(latestShot)) {
|
||||||
triggerShotEffect("blue", latestShot, newVal.length - 1);
|
triggerShotEffect("blue", latestShot, newLen - 1);
|
||||||
} else {
|
} else {
|
||||||
showShotTip("blue", latestShot);
|
showShotTip("blue", latestShot);
|
||||||
}
|
}
|
||||||
} else if (newVal.length <= prevBlueScores.value.length) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newLen < oldLen) {
|
||||||
bluelatestOne.value = null;
|
bluelatestOne.value = null;
|
||||||
hiddenBlueLatestKey.value = "";
|
hiddenBlueLatestKey.value = "";
|
||||||
if (shotEffect.value?.team === "blue") shotEffect.value = null;
|
if (shotEffect.value?.team === "blue") shotEffect.value = null;
|
||||||
}
|
}
|
||||||
prevBlueScores.value = [...newVal];
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -349,6 +384,8 @@ async function onReceiveMessage(message) {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
uni.$on("socket-inbox", onReceiveMessage);
|
uni.$on("socket-inbox", onReceiveMessage);
|
||||||
|
updateTargetSize();
|
||||||
|
if (uni.onWindowResize) uni.onWindowResize(handleWindowResize);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
@@ -365,6 +402,7 @@ onBeforeUnmount(() => {
|
|||||||
shakeTimer.value = null;
|
shakeTimer.value = null;
|
||||||
}
|
}
|
||||||
uni.$off("socket-inbox", onReceiveMessage);
|
uni.$off("socket-inbox", onReceiveMessage);
|
||||||
|
if (uni.offWindowResize) uni.offWindowResize(handleWindowResize);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -461,6 +499,9 @@ onBeforeUnmount(() => {
|
|||||||
:shot="shotEffect && shotEffect.shot"
|
:shot="shotEffect && shotEffect.shot"
|
||||||
:playKey="shotEffect ? shotEffect.key : ''"
|
:playKey="shotEffect ? shotEffect.key : ''"
|
||||||
:targetRadius="safeTargetRadius"
|
:targetRadius="safeTargetRadius"
|
||||||
|
:targetWidth="targetSize.width"
|
||||||
|
:targetHeight="targetSize.height"
|
||||||
|
:hitOffsetPx="currentHitRadiusPx"
|
||||||
@impact="shakeTarget"
|
@impact="shakeTarget"
|
||||||
@complete="completeShotEffect"
|
@complete="completeShotEffect"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -85,25 +85,25 @@ const handleUpdateClick = async () => {
|
|||||||
<!-- 悬浮图标(溢出卡片顶部) -->
|
<!-- 悬浮图标(溢出卡片顶部) -->
|
||||||
<image
|
<image
|
||||||
v-if="isNewVersion"
|
v-if="isNewVersion"
|
||||||
src="../static/ota/ota-mascot.png"
|
src="https://static.shelingxingqiu.com/shootmini/static/ota/ota-mascot.png"
|
||||||
mode="aspectFit"
|
mode="aspectFit"
|
||||||
class="float-icon float-mascot"
|
class="float-icon float-mascot"
|
||||||
/>
|
/>
|
||||||
<image
|
<image
|
||||||
v-else-if="isSuccess"
|
v-else-if="isSuccess"
|
||||||
src="../static/ota/check-char.png"
|
src="https://static.shelingxingqiu.com/shootmini/static/ota/check-char.png"
|
||||||
mode="aspectFit"
|
mode="aspectFit"
|
||||||
class="float-icon float-check"
|
class="float-icon float-check"
|
||||||
/>
|
/>
|
||||||
<image
|
<image
|
||||||
v-else-if="isFailure"
|
v-else-if="isFailure"
|
||||||
src="../static/ota/close-char.png"
|
src="https://static.shelingxingqiu.com/shootmini/static/ota/close-char.png"
|
||||||
mode="aspectFit"
|
mode="aspectFit"
|
||||||
class="float-icon float-close"
|
class="float-icon float-close"
|
||||||
/>
|
/>
|
||||||
<image
|
<image
|
||||||
v-else-if="isProgress"
|
v-else-if="isProgress"
|
||||||
src="../static/ota/target-char.png"
|
src="https://static.shelingxingqiu.com/shootmini/static/ota/target-char.png"
|
||||||
mode="aspectFit"
|
mode="aspectFit"
|
||||||
class="float-icon float-target"
|
class="float-icon float-target"
|
||||||
/>
|
/>
|
||||||
@@ -111,7 +111,7 @@ const handleUpdateClick = async () => {
|
|||||||
<!-- 弹窗卡片:overflow:visible 允许按钮溢出底部,背景图通过 ota-bg-clip 独立裁剪保持圆角 -->
|
<!-- 弹窗卡片:overflow:visible 允许按钮溢出底部,背景图通过 ota-bg-clip 独立裁剪保持圆角 -->
|
||||||
<view class="ota-dialog">
|
<view class="ota-dialog">
|
||||||
<view class="ota-bg-clip">
|
<view class="ota-bg-clip">
|
||||||
<image src="../static/ota/ota-bg.png" mode="aspectFill" class="ota-bg" />
|
<image src="https://static.shelingxingqiu.com/shootmini/static/ota/ota-bg.png" mode="aspectFill" class="ota-bg" />
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
class="ota-content"
|
class="ota-content"
|
||||||
@@ -120,9 +120,9 @@ const handleUpdateClick = async () => {
|
|||||||
|
|
||||||
<!-- 发现新版本:new-ver.png 已包含标题图,不再重复文字;版本号使用 ota-ver.png 胶囊背景 -->
|
<!-- 发现新版本:new-ver.png 已包含标题图,不再重复文字;版本号使用 ota-ver.png 胶囊背景 -->
|
||||||
<block v-if="isNewVersion">
|
<block v-if="isNewVersion">
|
||||||
<image src="../static/ota/new-ver.png" mode="aspectFit" class="new-ver-img" />
|
<image src="https://static.shelingxingqiu.com/shootmini/static/ota/new-ver.png" mode="aspectFit" class="new-ver-img" />
|
||||||
<view v-if="version" class="version-tag-wrap">
|
<view v-if="version" class="version-tag-wrap">
|
||||||
<image src="../static/ota/ota-ver.png" mode="aspectFit" class="version-tag-bg-img" />
|
<image src="https://static.shelingxingqiu.com/shootmini/static/ota/ota-ver.png" mode="aspectFit" class="version-tag-bg-img" />
|
||||||
<text class="version-tag">{{ version }}</text>
|
<text class="version-tag">{{ version }}</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- 副标题:如“新版本将优化智能弓体验”,离下方详情 12rpx -->
|
<!-- 副标题:如“新版本将优化智能弓体验”,离下方详情 12rpx -->
|
||||||
@@ -139,7 +139,7 @@ const handleUpdateClick = async () => {
|
|||||||
|
|
||||||
<!-- 更新成功:图片左边距 34rpx,文案左边距 44rpx,按钮浮动底部居中 -->
|
<!-- 更新成功:图片左边距 34rpx,文案左边距 44rpx,按钮浮动底部居中 -->
|
||||||
<block v-else-if="isSuccess">
|
<block v-else-if="isSuccess">
|
||||||
<image src="../static/ota/update-ok.png" mode="aspectFit" class="result-title-img" style="width: 220rpx; height: 62rpx;" />
|
<image src="https://static.shelingxingqiu.com/shootmini/static/ota/update-ok.png" mode="aspectFit" class="result-title-img" style="width: 220rpx; height: 62rpx;" />
|
||||||
<text class="dialog-desc">请关机并重启智能弓</text>
|
<text class="dialog-desc">请关机并重启智能弓</text>
|
||||||
<view class="btn-group-result">
|
<view class="btn-group-result">
|
||||||
<view class="primary-btn" @click="emit('done')">
|
<view class="primary-btn" @click="emit('done')">
|
||||||
@@ -150,7 +150,7 @@ const handleUpdateClick = async () => {
|
|||||||
|
|
||||||
<!-- 更新中:复用成功标题图,正文区域展示进度条,无底部按钮 -->
|
<!-- 更新中:复用成功标题图,正文区域展示进度条,无底部按钮 -->
|
||||||
<block v-else-if="isProgress">
|
<block v-else-if="isProgress">
|
||||||
<image src="../static/ota/update_progress.png" mode="aspectFit" class="result-title-img" style="width: 220rpx; height: 62rpx;" />
|
<image src="https://static.shelingxingqiu.com/shootmini/static/ota/update_progress.png" mode="aspectFit" class="result-title-img" style="width: 220rpx; height: 62rpx;" />
|
||||||
<view class="progress-wrap">
|
<view class="progress-wrap">
|
||||||
<view class="progress-track">
|
<view class="progress-track">
|
||||||
<view class="progress-fill" :style="{ width: `${progressValue}%` }"></view>
|
<view class="progress-fill" :style="{ width: `${progressValue}%` }"></view>
|
||||||
@@ -160,7 +160,7 @@ const handleUpdateClick = async () => {
|
|||||||
|
|
||||||
<!-- 更新失败:图片左边距 34rpx,文案左对齐 44rpx,按钮浮动底部居中 -->
|
<!-- 更新失败:图片左边距 34rpx,文案左对齐 44rpx,按钮浮动底部居中 -->
|
||||||
<block v-else-if="isFailure">
|
<block v-else-if="isFailure">
|
||||||
<image src="../static/ota/update-fail.png" mode="aspectFit" class="result-title-img" style="width: 222rpx; height: 62rpx;" />
|
<image src="https://static.shelingxingqiu.com/shootmini/static/ota/update-fail.png" mode="aspectFit" class="result-title-img" style="width: 222rpx; height: 62rpx;" />
|
||||||
<text class="dialog-desc">请确保:</text>
|
<text class="dialog-desc">请确保:</text>
|
||||||
<text class="dialog-desc">1、智能弓已开启</text>
|
<text class="dialog-desc">1、智能弓已开启</text>
|
||||||
<text class="dialog-desc">2、网路连接稳定</text>
|
<text class="dialog-desc">2、网路连接稳定</text>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
loginAPI,
|
loginAPI,
|
||||||
getHomeData,
|
getHomeData,
|
||||||
getPhoneNumberAPI,
|
getPhoneNumberAPI,
|
||||||
|
getPhoneNumberAPIv2,
|
||||||
getDeviceBatteryAPI,
|
getDeviceBatteryAPI,
|
||||||
} from "@/apis";
|
} from "@/apis";
|
||||||
|
|
||||||
@@ -43,12 +44,12 @@ const handleAgree = () => {
|
|||||||
|
|
||||||
async function getphonenumber(e) {
|
async function getphonenumber(e) {
|
||||||
if (e.detail.code) {
|
if (e.detail.code) {
|
||||||
const wxResult = await wxLogin();
|
// const wxResult = await wxLogin();
|
||||||
const result = await getPhoneNumberAPI({
|
const result = await getPhoneNumberAPIv2({
|
||||||
...e.detail,
|
// ...e.detail,
|
||||||
code: wxResult.code,
|
code: e.detail.code,
|
||||||
});
|
});
|
||||||
if (result.phone) phone.value = result.phone;
|
if (result.purePhoneNumber) phone.value = result.purePhoneNumber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const loadingConfig = ref(false);
|
|||||||
const paying = ref(false);
|
const paying = ref(false);
|
||||||
const refreshing = ref(false);
|
const refreshing = ref(false);
|
||||||
|
|
||||||
// 会员页核心展示数据:视觉、权益、套餐均按蓝湖当前两张设计稿拆分。
|
// 会员页核心展示数据:视觉、权益按蓝湖当前两张设计稿拆分,套餐完全使用接口数据。
|
||||||
const memberTypes = [
|
const memberTypes = [
|
||||||
{
|
{
|
||||||
key: "normal",
|
key: "normal",
|
||||||
@@ -41,11 +41,6 @@ const memberTypes = [
|
|||||||
{ label: "排位赛\n每日+20次", icon: "../../static/vip/vip-rank.png" },
|
{ label: "排位赛\n每日+20次", icon: "../../static/vip/vip-rank.png" },
|
||||||
{ label: "约战\n每日+20次", icon: "../../static/vip/vip-battle.png" },
|
{ label: "约战\n每日+20次", icon: "../../static/vip/vip-battle.png" },
|
||||||
],
|
],
|
||||||
packages: [
|
|
||||||
{ name: "连续包月", price: "20", original: "35" },
|
|
||||||
{ name: "12个月", price: "300", original: "420" },
|
|
||||||
{ name: "3个月", price: "84", original: "105" },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "super",
|
key: "super",
|
||||||
@@ -70,11 +65,6 @@ const memberTypes = [
|
|||||||
{ label: "排位赛无限制", icon: "../../static/vip/svip-rank.png" },
|
{ label: "排位赛无限制", icon: "../../static/vip/svip-rank.png" },
|
||||||
{ label: "专享SVIP客服", icon: "../../static/vip/svip-service.png" },
|
{ label: "专享SVIP客服", icon: "../../static/vip/svip-service.png" },
|
||||||
],
|
],
|
||||||
packages: [
|
|
||||||
{ name: "连续包月", price: "20", original: "35" },
|
|
||||||
{ name: "12个月", price: "300", original: "420" },
|
|
||||||
{ name: "3个月", price: "84", original: "105" },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -140,9 +130,17 @@ const getMenuName = (item) => {
|
|||||||
return item.name || item.vipName || item.title || "";
|
return item.name || item.vipName || item.title || "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const formatPrice = (value) => {
|
||||||
|
if (value === undefined || value === null || value === "") return "";
|
||||||
|
return String(value).replace("¥", "").replace("¥", "");
|
||||||
|
};
|
||||||
|
|
||||||
const getMenuPrice = (item) => {
|
const getMenuPrice = (item) => {
|
||||||
const value = item.price || item.total || item.amount || item.money;
|
return formatPrice(item && item.price);
|
||||||
return value ? String(value).replace("¥", "").replace("¥", "") : "";
|
};
|
||||||
|
|
||||||
|
const getMenuOriginalPrice = (item) => {
|
||||||
|
return formatPrice(item && item.originalPrice);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMenuSortValue = (item) => {
|
const getMenuSortValue = (item) => {
|
||||||
@@ -177,48 +175,19 @@ const getMenuType = (item) => {
|
|||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPackageMonths = (name) => {
|
|
||||||
if (/连续包月/.test(name)) return 1;
|
|
||||||
const match = String(name || "").match(/(\d+)\s*个?月/);
|
|
||||||
return match ? Number(match[1]) : 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const matchPackageSource = (type, pack, index) => {
|
|
||||||
const menus = sortMenusBySort(configMenus.value);
|
|
||||||
const typedMenus = menus.filter((item) => {
|
|
||||||
const menuType = getMenuType(item);
|
|
||||||
if (type.key === "super") return menuType === "super";
|
|
||||||
return menuType === "normal";
|
|
||||||
});
|
|
||||||
const pool = typedMenus.length ? typedMenus : menus;
|
|
||||||
const packMonths = getPackageMonths(pack.name);
|
|
||||||
return (
|
|
||||||
pool.find((item) => getMenuName(item).indexOf(pack.name) !== -1) ||
|
|
||||||
pool.find((item) => packMonths && getPackageMonths(getMenuName(item)) === packMonths) ||
|
|
||||||
pool.find((item) => getMenuPrice(item) === pack.price) ||
|
|
||||||
pool[index]
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPackages = (type) => {
|
const getPackages = (type) => {
|
||||||
return type.packages
|
return sortMenusBySort(configMenus.value)
|
||||||
.map((item, index) => {
|
.filter((item) => getMenuType(item) === type.key)
|
||||||
const source = matchPackageSource(type, item, index);
|
.map((item) => {
|
||||||
return {
|
return {
|
||||||
...item,
|
source: item,
|
||||||
source,
|
id: item.id,
|
||||||
name: source ? getMenuName(source) || item.name : item.name,
|
name: getMenuName(item),
|
||||||
price: source ? getMenuPrice(source) || item.price : item.price,
|
price: getMenuPrice(item),
|
||||||
icon: source && source.icon,
|
originalPrice: getMenuOriginalPrice(item),
|
||||||
id: source && source.id,
|
icon: item.icon,
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
.map((item, index) => ({ item, index }))
|
|
||||||
.sort((a, b) => {
|
|
||||||
const sortDiff = getMenuSortValue(a.item.source) - getMenuSortValue(b.item.source);
|
|
||||||
return sortDiff || a.index - b.index;
|
|
||||||
})
|
|
||||||
.map(({ item }) => item);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const currentPackages = computed(() => {
|
const currentPackages = computed(() => {
|
||||||
@@ -487,7 +456,7 @@ onShow(loadVipConfig);
|
|||||||
<view class="package-list">
|
<view class="package-list">
|
||||||
<view
|
<view
|
||||||
v-for="(pack, index) in getPackages(type)"
|
v-for="(pack, index) in getPackages(type)"
|
||||||
:key="`${type.key}-${pack.id || pack.name}`"
|
:key="`${type.key}-${pack.id || pack.name || index}`"
|
||||||
class="package-card"
|
class="package-card"
|
||||||
:class="{ 'package-card--active': selectedPackageIndex === index }"
|
:class="{ 'package-card--active': selectedPackageIndex === index }"
|
||||||
@click="selectPackage(index)"
|
@click="selectPackage(index)"
|
||||||
@@ -498,8 +467,8 @@ onShow(loadVipConfig);
|
|||||||
<text class="package-price__symbol">¥</text>
|
<text class="package-price__symbol">¥</text>
|
||||||
<text class="package-price__value">{{ pack.price }}</text>
|
<text class="package-price__value">{{ pack.price }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="package-origin">
|
<view v-if="pack.originalPrice" class="package-origin">
|
||||||
<text>¥{{ pack.original }}</text>
|
<text>¥{{ pack.originalPrice }}</text>
|
||||||
<view class="package-origin__line" />
|
<view class="package-origin__line" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -511,13 +480,14 @@ onShow(loadVipConfig);
|
|||||||
hover-class="none"
|
hover-class="none"
|
||||||
class="activate-btn"
|
class="activate-btn"
|
||||||
:class="type.buttonClass"
|
:class="type.buttonClass"
|
||||||
:disabled="loadingConfig || paying || refreshing"
|
:disabled="loadingConfig || paying || refreshing || !selectedPackage"
|
||||||
@click="onPay"
|
@click="onPay"
|
||||||
>
|
>
|
||||||
<text v-if="loadingConfig">加载套餐中</text>
|
<text v-if="loadingConfig">加载套餐中</text>
|
||||||
<text v-else-if="paying">创建订单中</text>
|
<text v-else-if="paying">创建订单中</text>
|
||||||
<text v-else-if="!refreshing">¥ {{ selectedPackage.price }} 一键激活</text>
|
<text v-else-if="refreshing">刷新会员状态中</text>
|
||||||
<text v-else>刷新会员状态中</text>
|
<text v-else-if="selectedPackage">¥ {{ selectedPackage.price }} 一键激活</text>
|
||||||
|
<text v-else>套餐暂不可购买</text>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<view class="agreement">
|
<view class="agreement">
|
||||||
@@ -784,8 +754,8 @@ onShow(loadVipConfig);
|
|||||||
}
|
}
|
||||||
|
|
||||||
.package-list {
|
.package-list {
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
width: 984rpx;
|
min-width: 100%;
|
||||||
padding: 6rpx 84rpx 6rpx 6rpx;
|
padding: 6rpx 84rpx 6rpx 6rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
@@ -793,6 +763,7 @@ onShow(loadVipConfig);
|
|||||||
.package-card {
|
.package-card {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 264rpx;
|
width: 264rpx;
|
||||||
|
flex: 0 0 264rpx;
|
||||||
height: 224rpx;
|
height: 224rpx;
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
border: 2rpx solid #999999;
|
border: 2rpx solid #999999;
|
||||||
|
|||||||
@@ -632,7 +632,7 @@ onUnmounted(() => {
|
|||||||
>
|
>
|
||||||
<!-- Hero: wifi1.png 单图(已内含吉祥物) -->
|
<!-- Hero: wifi1.png 单图(已内含吉祥物) -->
|
||||||
<view class="hero-area">
|
<view class="hero-area">
|
||||||
<image src="../static/ota/wifi1.png" mode="aspectFit" style="width: 194rpx; height: 164rpx;" />
|
<image src="https://static.shelingxingqiu.com/shootmini/static/ota/wifi1.png" mode="aspectFit" style="width: 194rpx; height: 164rpx;" />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<text class="page-title" :class="{ 'connected-page-title': currentState === 'CONNECTED' }">
|
<text class="page-title" :class="{ 'connected-page-title': currentState === 'CONNECTED' }">
|
||||||
@@ -721,7 +721,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- UPDATING -->
|
<!-- UPDATING -->
|
||||||
<view v-else-if="currentState === 'UPDATING'" class="center-page">
|
<view v-else-if="currentState === 'UPDATING'" class="center-page">
|
||||||
<image src="../static/ota/target-char.png" mode="aspectFit" style="width: 194rpx; height: 164rpx;" />
|
<image src="https://static.shelingxingqiu.com/shootmini/static/ota/target-char.png" mode="aspectFit" style="width: 194rpx; height: 164rpx;" />
|
||||||
<text class="page-title" style="margin-top: 24rpx;">更新中,请稍等片刻...</text>
|
<text class="page-title" style="margin-top: 24rpx;">更新中,请稍等片刻...</text>
|
||||||
<view class="progress-wrap">
|
<view class="progress-wrap">
|
||||||
<view class="progress-track">
|
<view class="progress-track">
|
||||||
@@ -733,7 +733,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- DONE -->
|
<!-- DONE -->
|
||||||
<view v-else-if="currentState === 'DONE'" class="center-page">
|
<view v-else-if="currentState === 'DONE'" class="center-page">
|
||||||
<image src="../static/ota/check-char.png" mode="aspectFit" style="width: 194rpx; height: 166rpx;" />
|
<image src="https://static.shelingxingqiu.com/shootmini/static/ota/check-char.png" mode="aspectFit" style="width: 194rpx; height: 166rpx;" />
|
||||||
<text class="page-title" style="margin-top: 24rpx;">更新完成</text>
|
<text class="page-title" style="margin-top: 24rpx;">更新完成</text>
|
||||||
<text class="page-desc-white">请关机并重启智能弓</text>
|
<text class="page-desc-white">请关机并重启智能弓</text>
|
||||||
<view class="primary-btn done-btn" style="margin-top:20px" @click="handleDone">
|
<view class="primary-btn done-btn" style="margin-top:20px" @click="handleDone">
|
||||||
@@ -743,7 +743,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- FAILED -->
|
<!-- FAILED -->
|
||||||
<view v-else-if="currentState === 'FAILED'" class="center-page">
|
<view v-else-if="currentState === 'FAILED'" class="center-page">
|
||||||
<image src="../static/ota/close-char.png" mode="aspectFit" style="width: 194rpx; height: 164rpx;" />
|
<image src="https://static.shelingxingqiu.com/shootmini/static/ota/close-char.png" mode="aspectFit" style="width: 194rpx; height: 164rpx;" />
|
||||||
<text class="page-title fail-title" style="margin-top: 24rpx;">更新失败</text>
|
<text class="page-title fail-title" style="margin-top: 24rpx;">更新失败</text>
|
||||||
<text class="page-desc-white">请确保:</text>
|
<text class="page-desc-white">请确保:</text>
|
||||||
<text class="page-desc-white">1、智能弓已开启</text>
|
<text class="page-desc-white">1、智能弓已开启</text>
|
||||||
@@ -756,7 +756,7 @@ onUnmounted(() => {
|
|||||||
<!-- CONNECTING 底部弹窗 -->
|
<!-- CONNECTING 底部弹窗 -->
|
||||||
<view v-if="currentState === 'CONNECTING'" class="sheet-mask">
|
<view v-if="currentState === 'CONNECTING'" class="sheet-mask">
|
||||||
<view class="sheet" :style="{ marginBottom: keyboardHeight + 'px' }" @click.stop="">
|
<view class="sheet" :style="{ marginBottom: keyboardHeight + 'px' }" @click.stop="">
|
||||||
<image src="../static/ota/ota-bg.png" mode="aspectFill" class="sheet-bg" />
|
<image src="https://static.shelingxingqiu.com/shootmini/static/ota/ota-bg.png" mode="aspectFill" class="sheet-bg" />
|
||||||
<view class="sheet-inner">
|
<view class="sheet-inner">
|
||||||
|
|
||||||
<!-- 有密码网络 -->
|
<!-- 有密码网络 -->
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 237 B |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |