feat: 添加设备振动功能,支持多种振动模式和反馈

This commit is contained in:
2025-12-18 15:48:30 +07:00
parent 5cf5052a51
commit 6ceb80e6f2
3 changed files with 126 additions and 4 deletions

View File

@@ -1,5 +1,5 @@
import type { Awaitable } from "@vueuse/core";
import type { ChartOptions, DeepPartial, IChartApi, ISeriesApi, OhlcData, SeriesOptionsMap, SeriesType } from "lightweight-charts";
import type { ChartOptions, DeepPartial, IChartApi, ISeriesApi, OhlcData, SeriesType } from "lightweight-charts";
import { AreaSeries, BarSeries, BaselineSeries, CandlestickSeries, ColorType, createChart, HistogramSeries, LineSeries } from "lightweight-charts";
import { mergeWith } from "lodash-es";
@@ -27,8 +27,8 @@ const initializeOptions: Required<TradingViewOptions> = {
width: 400,
height: 300,
layout: {
textColor: "white",
background: { type: ColorType.Solid, color: "#000000" },
textColor: "black",
background: { type: ColorType.Solid, color: "#fff" },
},
},
autosize: true,

View File

@@ -0,0 +1,116 @@
import { Capacitor } from "@capacitor/core";
import { Haptics, ImpactStyle } from "@capacitor/haptics";
export interface HapticsOptions {
/**
* 振动强度
* - light: 轻微振动
* - medium: 中等振动
* - heavy: 强烈振动
*/
impact?: ImpactStyle;
/**
* 振动时长 (毫秒)
*/
duration?: number;
}
/**
* 设备振动 Composable
*/
export function useHaptics() {
/**
* 检查设备是否支持振动
*/
const isHapticsAvailable = computed(() => {
return Capacitor.isNativePlatform();
});
/**
* 触发振动反馈
* @param options 振动配置选项
*/
async function vibrate(options: HapticsOptions = {}) {
if (!isHapticsAvailable.value) {
console.warn("Haptics not available on this platform");
return;
}
const { impact = ImpactStyle.Medium, duration } = options;
try {
if (duration) {
// 自定义时长振动
await Haptics.vibrate({ duration });
}
else {
// 触觉反馈振动
await Haptics.impact({ style: impact });
}
}
catch (error) {
console.error("Haptics error:", error);
}
}
/**
* 轻微振动 (适用于按钮点击)
*/
async function lightVibrate() {
await vibrate({ impact: ImpactStyle.Light });
}
/**
* 中等振动 (适用于选择操作)
*/
async function mediumVibrate() {
await vibrate({ impact: ImpactStyle.Medium });
}
/**
* 强烈振动 (适用于重要通知)
*/
async function heavyVibrate() {
await vibrate({ impact: ImpactStyle.Heavy });
}
/**
* 成功振动模式
*/
async function successVibrate() {
await lightVibrate();
await new Promise(resolve => setTimeout(resolve, 100));
await lightVibrate();
}
/**
* 错误振动模式
*/
async function errorVibrate() {
await heavyVibrate();
await new Promise(resolve => setTimeout(resolve, 200));
await heavyVibrate();
await new Promise(resolve => setTimeout(resolve, 200));
await heavyVibrate();
}
/**
* 警告振动模式
*/
async function warningVibrate() {
await mediumVibrate();
await new Promise(resolve => setTimeout(resolve, 150));
await mediumVibrate();
}
return {
isHapticsAvailable,
vibrate,
lightVibrate,
mediumVibrate,
heavyVibrate,
successVibrate,
errorVibrate,
warningVibrate,
};
}

View File

@@ -10,7 +10,7 @@ import IssuePeriod from "./issue-period.vue";
const { t } = useI18n();
const now = useNow();
const { data: categories } = await safeClient(() => client.api.rwa.issuance.categories.get());
const { data: categories, onFetchResponse } = await safeClient(() => client.api.rwa.issuance.categories.get());
const step = useRouteQuery<number>("step", 1, { transform: v => Number(v), mode: "push" });
const initialData: RwaIssuanceProductBody = {
@@ -33,6 +33,12 @@ const initialData: RwaIssuanceProductBody = {
};
const form = useStorage<RwaIssuanceProductBody>("issuing-apply-form", { ...initialData });
onFetchResponse(() => {
if (!form.value.product.categoryId && categories.value?.data && categories.value?.data.length > 0) {
form.value.product.categoryId = categories.value?.data![0].id;
}
});
function handleNext(values: GenericObject) {
form.value.product = { ...values as any };
step.value = 2;