978 lines
25 KiB
Plaintext
978 lines
25 KiB
Plaintext
<template>
|
||
<view class="settings-container">
|
||
<!-- 头部导航 -->
|
||
<view class="header">
|
||
<button class="back-btn" @click="goBack">
|
||
<text class="back-icon">←</text>
|
||
</button>
|
||
<text class="title">传感器设置</text>
|
||
<button class="save-btn" @click="saveSettings">保存</button>
|
||
</view>
|
||
|
||
<!-- 通用设置 -->
|
||
<view class="section">
|
||
<text class="section-title">通用设置</text>
|
||
<view class="setting-item">
|
||
<text class="setting-label">实时数据推送</text>
|
||
<switch :checked="settings.realtime_enabled" @change="onRealtimeChange" />
|
||
</view>
|
||
<view class="setting-item">
|
||
<text class="setting-label">数据自动备份</text>
|
||
<switch :checked="settings.auto_backup" @change="onAutoBackupChange" />
|
||
</view>
|
||
<view class="setting-item">
|
||
<text class="setting-label">异常检测</text>
|
||
<switch :checked="settings.anomaly_detection" @change="onAnomalyDetectionChange" />
|
||
</view>
|
||
<view class="setting-item">
|
||
<text class="setting-label">AI分析</text>
|
||
<switch :checked="settings.ai_analysis" @change="onAIAnalysisChange" />
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 数据采集频率 -->
|
||
<view class="section">
|
||
<text class="section-title">数据采集频率</text>
|
||
<view class="frequency-grid">
|
||
<view class="frequency-item" v-for="(sensor, index) in sensorFrequencies" :key="index">
|
||
<text class="sensor-name">{{ sensor.name }}</text>
|
||
<button class="picker-button" @click="showFrequencyPicker(index)">
|
||
<view class="picker-view">
|
||
<text>{{ frequencyOptions[sensor.frequency_index] }}</text>
|
||
<text class="picker-arrow">▼</text>
|
||
</view>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 阈值设置 -->
|
||
<view class="section">
|
||
<text class="section-title">异常阈值设置</text>
|
||
<view class="threshold-list">
|
||
<view class="threshold-item" v-for="(threshold, index) in thresholds" :key="index">
|
||
<text class="threshold-label">{{ threshold.name }}</text>
|
||
<view class="threshold-inputs">
|
||
<view class="input-group">
|
||
<text class="input-label">最小值</text>
|
||
<input class="threshold-input" type="number" v-model="threshold.min_value" />
|
||
</view>
|
||
<view class="input-group">
|
||
<text class="input-label">最大值</text>
|
||
<input class="threshold-input" type="number" v-model="threshold.max_value" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 通知设置 -->
|
||
<view class="section">
|
||
<text class="section-title">通知设置</text>
|
||
<view class="notification-item">
|
||
<text class="setting-label">异常警报</text>
|
||
<switch :checked="notifications.anomaly_alert" @change="onAnomalyAlertChange" />
|
||
</view>
|
||
<view class="notification-item">
|
||
<text class="setting-label">每日报告</text>
|
||
<switch :checked="notifications.daily_report" @change="onDailyReportChange" />
|
||
</view>
|
||
<view class="notification-item">
|
||
<text class="setting-label">设备离线提醒</text>
|
||
<switch :checked="notifications.device_offline" @change="onDeviceOfflineChange" />
|
||
</view>
|
||
<view class="notification-item" v-if="notifications.daily_report">
|
||
<text class="setting-label">报告发送时间</text>
|
||
<input class="time-input" type="time" :value="notifications.report_time" @input="onReportTimeInput" />
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 数据管理 -->
|
||
<view class="section">
|
||
<text class="section-title">数据管理</text>
|
||
<view class="data-management">
|
||
<view class="management-item">
|
||
<text class="setting-label">本地数据保留时间</text>
|
||
<button class="picker-button" @click="showRetentionPicker">
|
||
<view class="picker-view">
|
||
<text>{{ retentionOptions[dataManagement.retention_index] }}</text>
|
||
<text class="picker-arrow">▼</text>
|
||
</view>
|
||
</button>
|
||
</view>
|
||
<view class="management-item">
|
||
<text class="setting-label">云端同步</text>
|
||
<switch :checked="dataManagement.cloud_sync" @change="onCloudSyncChange" />
|
||
</view>
|
||
<view class="management-item">
|
||
<text class="setting-label">数据压缩</text>
|
||
<switch :checked="dataManagement.data_compression" @change="onDataCompressionChange" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 隐私设置 -->
|
||
<view class="section">
|
||
<text class="section-title">隐私设置</text>
|
||
<view class="privacy-item">
|
||
<text class="setting-label">数据加密</text>
|
||
<switch :checked="privacy.data_encryption" @change="onDataEncryptionChange" />
|
||
</view>
|
||
<view class="privacy-item">
|
||
<text class="setting-label">匿名统计</text>
|
||
<switch :checked="privacy.anonymous_stats" @change="onAnonymousStatsChange" />
|
||
</view>
|
||
<view class="privacy-item">
|
||
<text class="setting-label">位置数据收集</text>
|
||
<switch :checked="privacy.location_data" @change="onLocationDataChange" />
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 设备校准 -->
|
||
<view class="section">
|
||
<text class="section-title">设备校准</text>
|
||
<view class="calibration-list">
|
||
<button class="calibration-btn" @click="calibrateHeartRate">心率传感器校准</button>
|
||
<button class="calibration-btn" @click="calibrateSteps">步数传感器校准</button>
|
||
<button class="calibration-btn" @click="calibrateBloodPressure">血压计校准</button>
|
||
<button class="calibration-btn" @click="calibrateTemperature">体温计校准</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 数据导出 -->
|
||
<view class="section">
|
||
<text class="section-title">数据导出</text>
|
||
<view class="export-options">
|
||
<button class="export-btn" @click="exportData('json')">导出为JSON</button>
|
||
<button class="export-btn" @click="exportData('csv')">导出为CSV</button>
|
||
<button class="export-btn" @click="exportData('pdf')">导出为PDF报告</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 重置选项 -->
|
||
<view class="section">
|
||
<text class="section-title">重置选项</text>
|
||
<view class="reset-options">
|
||
<button class="reset-btn" @click="resetSettings">恢复默认设置</button>
|
||
<button class="clear-btn" @click="clearAllData">清除所有数据</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 校准弹窗 -->
|
||
<view class="calibration-modal" v-if="showCalibration" @click="closeCalibration">
|
||
<view class="modal-content" @click.stop>
|
||
<view class="modal-header">
|
||
<text class="modal-title">{{ calibrationType }}校准</text>
|
||
<button class="close-btn" @click="closeCalibration">×</button>
|
||
</view>
|
||
<view class="modal-body">
|
||
<text class="calibration-instruction">{{ calibrationInstruction }}</text>
|
||
<view class="calibration-progress" v-if="calibrationProgress > 0">
|
||
<view class="progress-bar">
|
||
<view class="progress-fill" :style="{ width: calibrationProgress + '%' }"></view>
|
||
</view>
|
||
<text class="progress-text">{{ calibrationProgress }}%</text>
|
||
</view>
|
||
<view class="calibration-actions">
|
||
<button class="start-btn" v-if="calibrationProgress === 0"
|
||
@click="startCalibration">开始校准</button>
|
||
<button class="stop-btn" v-else @click="stopCalibration">停止校准</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { ref, onMounted } from 'vue'
|
||
import supa from '@/components/supadb/aksupainstance.uts'
|
||
import type {
|
||
UserSettings,
|
||
NotificationSettings,
|
||
DataManagementSettings,
|
||
PrivacySettings,
|
||
SensorFrequency,
|
||
SensorThreshold,
|
||
UserSettingsRecord,
|
||
SensorConfigItem,
|
||
ThresholdConfigItem
|
||
} from './types.uts'
|
||
|
||
// 响应式数据
|
||
const settings = ref<UserSettings>({
|
||
realtime_enabled: true,
|
||
auto_backup: true,
|
||
anomaly_detection: true,
|
||
ai_analysis: false
|
||
})
|
||
|
||
const sensorFrequencies = ref<SensorFrequency[]>([])
|
||
const thresholds = ref<SensorThreshold[]>([])
|
||
|
||
const notifications = ref<NotificationSettings>({
|
||
anomaly_alert: true,
|
||
daily_report: false,
|
||
device_offline: true,
|
||
report_time: '09:00'
|
||
})
|
||
|
||
const dataManagement = ref<DataManagementSettings>({
|
||
retention_index: 2,
|
||
cloud_sync: true,
|
||
data_compression: false
|
||
})
|
||
|
||
const privacy = ref<PrivacySettings>({
|
||
data_encryption: true,
|
||
anonymous_stats: false,
|
||
location_data: false
|
||
})
|
||
|
||
const showCalibration = ref<boolean>(false)
|
||
const calibrationType = ref<string>('')
|
||
const calibrationInstruction = ref<string>('')
|
||
const calibrationProgress = ref<number>(0)
|
||
|
||
// 选项数据
|
||
const frequencyOptions = ['关闭', '1分钟', '5分钟', '15分钟', '30分钟', '1小时']
|
||
const retentionOptions = ['7天', '30天', '90天', '1年', '永久保存']
|
||
|
||
const userId = 'eed3824b-bba1-4309-8048-19d17367c084'
|
||
let calibrationTimer : Number = 0
|
||
|
||
async function loadSettings() {
|
||
try {
|
||
const result = await supa.from('ak_training_intensity_settings')
|
||
.eq('user_id', userId)
|
||
.eq('setting_category', 'sensor')
|
||
.executeAs<Array<UserSettingsRecord>>()
|
||
if (result.data !== null && Array.isArray(result.data)) {
|
||
const dataArray = result.data as Array<any>
|
||
if (dataArray.length > 0) {
|
||
const settingsRecord = dataArray[0] as UTSJSONObject
|
||
const settingValue = settingsRecord.getJSON('setting_value')
|
||
if (settingValue !== null) {
|
||
// 更新设置
|
||
settings.value.realtime_enabled = settingValue.getBoolean('realtime_enabled') ?? true
|
||
settings.value.auto_backup = settingValue.getBoolean('auto_backup') ?? true
|
||
settings.value.anomaly_detection = settingValue.getBoolean('anomaly_detection') ?? true
|
||
settings.value.ai_analysis = settingValue.getBoolean('ai_analysis') ?? false
|
||
|
||
// 更新通知设置
|
||
const notificationData = settingValue.getJSON('notifications')
|
||
if (notificationData !== null) {
|
||
notifications.value.anomaly_alert = notificationData.getBoolean('anomaly_alert') ?? true
|
||
notifications.value.daily_report = notificationData.getBoolean('daily_report') ?? false
|
||
notifications.value.device_offline = notificationData.getBoolean('device_offline') ?? true
|
||
notifications.value.report_time = notificationData.getString('report_time') ?? '09:00'
|
||
}
|
||
|
||
// 更新数据管理设置
|
||
const dataManagementData = settingValue.getJSON('data_management')
|
||
if (dataManagementData !== null) {
|
||
dataManagement.value.retention_index = dataManagementData.getNumber('retention_index')?.toInt() ?? 2
|
||
dataManagement.value.cloud_sync = dataManagementData.getBoolean('cloud_sync') ?? true
|
||
dataManagement.value.data_compression = dataManagementData.getBoolean('data_compression') ?? false
|
||
}
|
||
|
||
// 更新隐私设置
|
||
const privacyData = settingValue.getJSON('privacy')
|
||
if (privacyData !== null) {
|
||
privacy.value.data_encryption = privacyData.getBoolean('data_encryption') ?? true
|
||
privacy.value.anonymous_stats = privacyData.getBoolean('anonymous_stats') ?? false
|
||
privacy.value.location_data = privacyData.getBoolean('location_data') ?? false
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.log('加载设置失败:', e)
|
||
}
|
||
}
|
||
|
||
function initializeSensorFrequencies() {
|
||
const frequencies : SensorFrequency[] = []
|
||
|
||
const sensors : SensorConfigItem[] = [
|
||
{ name: '心率', key: 'heart_rate', frequency_index: 2 },
|
||
{ name: '步数', key: 'steps', frequency_index: 3 },
|
||
{ name: '血氧', key: 'spo2', frequency_index: 4 },
|
||
{ name: '血压', key: 'blood_pressure', frequency_index: 5 },
|
||
{ name: '体温', key: 'temperature', frequency_index: 4 },
|
||
{ name: '位置', key: 'location', frequency_index: 3 }
|
||
]
|
||
|
||
for (let i : Int = 0; i < sensors.length; i++) {
|
||
const sensor = sensors[i]
|
||
const freq : SensorFrequency = {
|
||
name: sensor.name,
|
||
key: sensor.key,
|
||
frequency_index: sensor.frequency_index
|
||
}
|
||
frequencies.push(freq)
|
||
}
|
||
|
||
sensorFrequencies.value = frequencies
|
||
} function initializeThresholds() {
|
||
const thresholdList : SensorThreshold[] = []
|
||
|
||
const thresholdConfig : ThresholdConfigItem[] = [
|
||
{ name: '心率 (bpm)', key: 'heart_rate', min: 60, max: 100 },
|
||
{ name: '血氧 (%)', key: 'spo2', min: 95, max: 100 },
|
||
{ name: '收缩压 (mmHg)', key: 'systolic_bp', min: 90, max: 140 },
|
||
{ name: '舒张压 (mmHg)', key: 'diastolic_bp', min: 60, max: 90 },
|
||
{ name: '体温 (°C)', key: 'temperature', min: 36.0, max: 37.5 }
|
||
]
|
||
|
||
for (let i : Int = 0; i < thresholdConfig.length; i++) {
|
||
const config = thresholdConfig[i]
|
||
const threshold : SensorThreshold = {
|
||
name: config.name,
|
||
key: config.key,
|
||
min_value: config.min.toString(),
|
||
max_value: config.max.toString()
|
||
}
|
||
thresholdList.push(threshold)
|
||
}
|
||
|
||
thresholds.value = thresholdList
|
||
}
|
||
async function saveSettings() {
|
||
try {
|
||
// 构建设置数据
|
||
const settingValue = new UTSJSONObject()
|
||
settingValue.set('realtime_enabled', settings.value.realtime_enabled)
|
||
settingValue.set('auto_backup', settings.value.auto_backup)
|
||
settingValue.set('anomaly_detection', settings.value.anomaly_detection)
|
||
settingValue.set('ai_analysis', settings.value.ai_analysis)
|
||
settingValue.set('notifications', notifications.value)
|
||
settingValue.set('data_management', dataManagement.value)
|
||
settingValue.set('privacy', privacy.value)
|
||
settingValue.set('sensor_frequencies', sensorFrequencies.value)
|
||
settingValue.set('thresholds', thresholds.value)
|
||
const settingData = new UTSJSONObject()
|
||
settingData.set('user_id', userId)
|
||
settingData.set('setting_category', 'sensor')
|
||
settingData.set('setting_key', 'sensor_config')
|
||
settingData.set('setting_value', settingValue)
|
||
|
||
// 先检查是否已存在记录
|
||
const existingResult = await supa.from('ak_user_settings')
|
||
.eq('user_id', userId)
|
||
.eq('setting_category', 'sensor')
|
||
.executeAs<Array<UserSettingsRecord>>()
|
||
let result : any
|
||
if (existingResult.data !== null && Array.isArray(existingResult.data)) {
|
||
const dataArray = existingResult.data as Array<any>
|
||
if (dataArray.length > 0) {
|
||
// 存在记录,使用 update
|
||
const updateData = new UTSJSONObject()
|
||
updateData.set('setting_value', settingValue)
|
||
updateData.set('updated_at', new Date().toISOString())
|
||
|
||
result = await supa.from('ak_user_settings')
|
||
.eq('user_id', userId)
|
||
.eq('setting_category', 'sensor')
|
||
.update(updateData)
|
||
.execute()
|
||
} else {
|
||
// 不存在记录,使用 insert
|
||
settingData.set('created_at', new Date().toISOString())
|
||
settingData.set('updated_at', new Date().toISOString())
|
||
|
||
result = await supa.from('ak_user_settings')
|
||
.insert(settingData)
|
||
.execute()
|
||
}
|
||
} else {
|
||
// 不存在记录,使用 insert
|
||
settingData.set('created_at', new Date().toISOString())
|
||
settingData.set('updated_at', new Date().toISOString())
|
||
|
||
result = await supa.from('ak_user_settings')
|
||
.insert(settingData)
|
||
.execute()
|
||
}
|
||
if (result.error === null) {
|
||
uni.showToast({
|
||
title: '设置保存成功',
|
||
icon: 'success'
|
||
})
|
||
} else {
|
||
console.log('保存设置失败:', result.error)
|
||
uni.showToast({
|
||
title: '保存失败',
|
||
icon: 'error'
|
||
})
|
||
}
|
||
} catch (e) {
|
||
console.log('保存设置异常:', e)
|
||
uni.showToast({
|
||
title: '保存失败',
|
||
icon: 'error'
|
||
})
|
||
}
|
||
}
|
||
|
||
// 事件处理函数
|
||
function onRealtimeChange(e : UniSwitchChangeEvent) {
|
||
settings.value.realtime_enabled = e.detail.value
|
||
}
|
||
|
||
function onAutoBackupChange(e : UniSwitchChangeEvent) {
|
||
settings.value.auto_backup = e.detail.value
|
||
}
|
||
|
||
function onAnomalyDetectionChange(e : UniSwitchChangeEvent) {
|
||
settings.value.anomaly_detection = e.detail.value
|
||
}
|
||
function onAIAnalysisChange(e : UniSwitchChangeEvent) {
|
||
settings.value.ai_analysis = e.detail.value
|
||
}
|
||
function onFrequencyChange(value : number, index : number) {
|
||
if (index >= 0 && index < sensorFrequencies.value.length) {
|
||
sensorFrequencies.value[index].frequency_index = value
|
||
}
|
||
}
|
||
|
||
function onAnomalyAlertChange(e : UniSwitchChangeEvent) {
|
||
notifications.value.anomaly_alert = e.detail.value
|
||
}
|
||
function onDailyReportChange(e : UniSwitchChangeEvent) {
|
||
notifications.value.daily_report = e.detail.value
|
||
}
|
||
|
||
function onDeviceOfflineChange(e : UniSwitchChangeEvent) {
|
||
notifications.value.device_offline = e.detail.value
|
||
}
|
||
function onReportTimeChange(value : string) {
|
||
notifications.value.report_time = value
|
||
}
|
||
|
||
function onRetentionChange(value : number) {
|
||
dataManagement.value.retention_index = value
|
||
}
|
||
|
||
function onCloudSyncChange(e : UniSwitchChangeEvent) {
|
||
dataManagement.value.cloud_sync = e.detail.value
|
||
}
|
||
|
||
function onDataCompressionChange(e : UniSwitchChangeEvent) {
|
||
dataManagement.value.data_compression = e.detail.value
|
||
}
|
||
|
||
function onDataEncryptionChange(e : UniSwitchChangeEvent) {
|
||
privacy.value.data_encryption = e.detail.value
|
||
}
|
||
|
||
function onAnonymousStatsChange(e : UniSwitchChangeEvent) {
|
||
privacy.value.anonymous_stats = e.detail.value
|
||
}
|
||
|
||
function onLocationDataChange(e : UniSwitchChangeEvent) {
|
||
privacy.value.location_data = e.detail.value
|
||
}
|
||
|
||
function showFrequencyPicker(index : number) {
|
||
uni.showActionSheet({
|
||
itemList: frequencyOptions,
|
||
success: (res) => {
|
||
if (res.tapIndex >= 0) {
|
||
onFrequencyChange(res.tapIndex, index)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
|
||
function onReportTimeInput(e : UniInputEvent) {
|
||
notifications.value.report_time = e.detail.value
|
||
}
|
||
|
||
function showRetentionPicker() {
|
||
uni.showActionSheet({
|
||
itemList: retentionOptions,
|
||
success: (res) => {
|
||
if (res.tapIndex >= 0) {
|
||
onRetentionChange(res.tapIndex)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 校准相关函数
|
||
function startCalibrationProcess(type : string, instruction : string) {
|
||
calibrationType.value = type
|
||
calibrationInstruction.value = instruction
|
||
showCalibration.value = true
|
||
calibrationProgress.value = 0
|
||
}
|
||
|
||
function calibrateHeartRate() {
|
||
startCalibrationProcess('心率传感器', '请保持静坐,放松心情,校准过程约需2分钟')
|
||
}
|
||
|
||
function calibrateSteps() {
|
||
startCalibrationProcess('步数传感器', '请在平地上正常步行20步,保持匀速')
|
||
}
|
||
|
||
function calibrateBloodPressure() {
|
||
startCalibrationProcess('血压计', '请使用标准血压计测量,确保袖带位置正确')
|
||
}
|
||
|
||
function calibrateTemperature() {
|
||
startCalibrationProcess('体温计', '请使用医用体温计对比校准,测量时间需2分钟')
|
||
}
|
||
|
||
function stopCalibration() {
|
||
if (calibrationTimer !== 0) {
|
||
clearInterval(calibrationTimer)
|
||
calibrationTimer = 0
|
||
}
|
||
calibrationProgress.value = 0
|
||
}
|
||
|
||
function closeCalibration() {
|
||
showCalibration.value = false
|
||
stopCalibration()
|
||
}
|
||
|
||
function startCalibration() {
|
||
calibrationProgress.value = 1
|
||
|
||
// 模拟校准进度
|
||
calibrationTimer = setInterval(() => {
|
||
if (calibrationProgress.value < 100) {
|
||
calibrationProgress.value += Math.random() * 10
|
||
if (calibrationProgress.value > 100) {
|
||
calibrationProgress.value = 100
|
||
}
|
||
} else {
|
||
stopCalibration()
|
||
uni.showToast({
|
||
title: '校准完成',
|
||
icon: 'success'
|
||
})
|
||
closeCalibration()
|
||
}
|
||
}, 500)
|
||
}
|
||
|
||
// 数据导出
|
||
function exportData(format : string) {
|
||
uni.showLoading({
|
||
title: '准备导出数据...'
|
||
})
|
||
|
||
setTimeout(() => {
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: `${format.toUpperCase()}文件已生成`,
|
||
icon: 'success'
|
||
})
|
||
}, 2000)
|
||
}
|
||
|
||
// 重置功能
|
||
function resetSettings() {
|
||
uni.showModal({
|
||
title: '确认重置',
|
||
content: '确定要恢复所有设置到默认状态吗?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
// 重置所有设置到默认值
|
||
settings.value.realtime_enabled = true
|
||
settings.value.auto_backup = true
|
||
settings.value.anomaly_detection = true
|
||
settings.value.ai_analysis = false
|
||
|
||
notifications.value.anomaly_alert = true
|
||
notifications.value.daily_report = false
|
||
notifications.value.device_offline = true
|
||
notifications.value.report_time = '09:00'
|
||
|
||
dataManagement.value.retention_index = 2
|
||
dataManagement.value.cloud_sync = true
|
||
dataManagement.value.data_compression = false
|
||
|
||
privacy.value.data_encryption = true
|
||
privacy.value.anonymous_stats = false
|
||
privacy.value.location_data = false
|
||
|
||
initializeSensorFrequencies()
|
||
initializeThresholds()
|
||
|
||
uni.showToast({
|
||
title: '设置已重置',
|
||
icon: 'success'
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
function clearAllData() {
|
||
uni.showModal({
|
||
title: '危险操作',
|
||
content: '确定要清除所有传感器数据吗?此操作不可恢复!',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.showToast({
|
||
title: '数据清除完成',
|
||
icon: 'success'
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
function goBack() {
|
||
uni.navigateBack()
|
||
}
|
||
onMounted(() => {
|
||
loadSettings()
|
||
initializeSensorFrequencies()
|
||
initializeThresholds()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.settings-container {
|
||
flex: 1;
|
||
padding: 20rpx;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.header {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
padding: 20rpx;
|
||
background-color: #ffffff;
|
||
border-radius: 12rpx;
|
||
}
|
||
|
||
.back-btn {
|
||
padding: 12rpx;
|
||
background-color: #f0f0f0;
|
||
border-radius: 8rpx;
|
||
border: none;
|
||
}
|
||
|
||
.back-icon {
|
||
font-size: 32rpx;
|
||
color: #666666;
|
||
}
|
||
|
||
.title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333333;
|
||
}
|
||
|
||
.save-btn {
|
||
padding: 16rpx 24rpx;
|
||
background-color: #409EFF;
|
||
color: #ffffff;
|
||
border-radius: 8rpx;
|
||
font-size: 28rpx;
|
||
border: none;
|
||
}
|
||
|
||
.section {
|
||
margin-bottom: 20rpx;
|
||
padding: 24rpx;
|
||
background-color: #ffffff;
|
||
border-radius: 12rpx;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 30rpx;
|
||
font-weight: bold;
|
||
color: #333333;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.setting-item,
|
||
.notification-item,
|
||
.management-item,
|
||
.privacy-item {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 20rpx 0;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.setting-item:last-child,
|
||
.notification-item:last-child,
|
||
.management-item:last-child,
|
||
.privacy-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.setting-label {
|
||
font-size: 28rpx;
|
||
color: #333333;
|
||
}
|
||
|
||
.frequency-grid {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.frequency-item {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 20rpx 0;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.frequency-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.sensor-name {
|
||
font-size: 28rpx;
|
||
color: #333333;
|
||
width: 200rpx;
|
||
}
|
||
|
||
.picker-view {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 16rpx 20rpx;
|
||
background-color: #f8f9fa;
|
||
border-radius: 8rpx;
|
||
min-width: 200rpx;
|
||
}
|
||
|
||
.picker-button {
|
||
background: none;
|
||
border: none;
|
||
padding: 0;
|
||
}
|
||
|
||
.picker-arrow {
|
||
color: #999999;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.threshold-list {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.threshold-item {
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.threshold-label {
|
||
font-size: 28rpx;
|
||
color: #333333;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.threshold-inputs {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.input-group {
|
||
width: 48%;
|
||
}
|
||
|
||
.input-label {
|
||
font-size: 24rpx;
|
||
color: #666666;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.threshold-input {
|
||
width: 100%;
|
||
padding: 16rpx;
|
||
border: 2rpx solid #e0e0e0;
|
||
border-radius: 8rpx;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.calibration-list {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.calibration-btn {
|
||
padding: 20rpx;
|
||
margin-bottom: 16rpx;
|
||
background-color: #E6A23C;
|
||
color: #ffffff;
|
||
border-radius: 8rpx;
|
||
font-size: 28rpx;
|
||
border: none;
|
||
}
|
||
|
||
.calibration-btn:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.export-options {
|
||
flex-direction: row;
|
||
justify-content: space-around;
|
||
}
|
||
|
||
.export-btn {
|
||
flex: 1;
|
||
padding: 20rpx;
|
||
margin: 0 8rpx;
|
||
background-color: #67C23A;
|
||
color: #ffffff;
|
||
border-radius: 8rpx;
|
||
font-size: 26rpx;
|
||
border: none;
|
||
}
|
||
|
||
.reset-options {
|
||
flex-direction: row;
|
||
justify-content: space-around;
|
||
}
|
||
|
||
.reset-btn {
|
||
flex: 1;
|
||
padding: 20rpx;
|
||
margin-right: 16rpx;
|
||
background-color: #909399;
|
||
color: #ffffff;
|
||
border-radius: 8rpx;
|
||
font-size: 28rpx;
|
||
border: none;
|
||
}
|
||
|
||
.clear-btn {
|
||
flex: 1;
|
||
padding: 20rpx;
|
||
background-color: #F56C6C;
|
||
color: #ffffff;
|
||
border-radius: 8rpx;
|
||
font-size: 28rpx;
|
||
border: none;
|
||
}
|
||
|
||
.calibration-modal {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.modal-content {
|
||
width: 85%;
|
||
background-color: #ffffff;
|
||
border-radius: 16rpx;
|
||
padding: 32rpx;
|
||
}
|
||
|
||
.modal-header {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 24rpx;
|
||
border-bottom: 2rpx solid #f0f0f0;
|
||
padding-bottom: 16rpx;
|
||
}
|
||
|
||
.modal-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333333;
|
||
}
|
||
|
||
.close-btn {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
border-radius: 30rpx;
|
||
background-color: #f0f0f0;
|
||
color: #666666;
|
||
font-size: 36rpx;
|
||
border: none;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.modal-body {
|
||
align-items: center;
|
||
}
|
||
|
||
.calibration-instruction {
|
||
font-size: 28rpx;
|
||
color: #666666;
|
||
line-height: 1.6;
|
||
text-align: center;
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.calibration-progress {
|
||
width: 100%;
|
||
align-items: center;
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.progress-bar {
|
||
width: 100%;
|
||
height: 16rpx;
|
||
background-color: #f0f0f0;
|
||
border-radius: 8rpx;
|
||
overflow: hidden;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.progress-fill {
|
||
height: 100%;
|
||
background-color: #409EFF;
|
||
border-radius: 8rpx;
|
||
transition: width 0.3s ease;
|
||
}
|
||
|
||
.progress-text {
|
||
font-size: 24rpx;
|
||
color: #666666;
|
||
}
|
||
|
||
.calibration-actions {
|
||
width: 100%;
|
||
}
|
||
|
||
.start-btn,
|
||
.stop-btn {
|
||
width: 100%;
|
||
padding: 24rpx;
|
||
border-radius: 12rpx;
|
||
font-size: 28rpx;
|
||
border: none;
|
||
}
|
||
|
||
.start-btn {
|
||
background-color: #67C23A;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.stop-btn {
|
||
background-color: #F56C6C;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.time-input {
|
||
padding: 16rpx 20rpx;
|
||
background-color: #f8f9fa;
|
||
border: 1px solid #e9ecef;
|
||
border-radius: 8rpx;
|
||
font-size: 28rpx;
|
||
color: #333333;
|
||
min-width: 200rpx;
|
||
text-align: center;
|
||
}
|
||
</style> |