fix:调整demo测试页面

This commit is contained in:
liyi 2025-04-18 15:43:17 +08:00
parent 891ba072d0
commit 690849af88
16 changed files with 1343 additions and 1008 deletions

View File

@ -5,60 +5,61 @@ const {
register
} = requirePlugin('starCloud')
// 添加硬编码的账户信息
const MOCK_ACCOUNT = {
username: 'nhueg_3MWghkBljd8WoDUmeBm',
password: '91936ff7ede730fb7befc33dc749543d',
uid: 17162
};
App({
globalData: {
accountInfo: null,
accountInfo: MOCK_ACCOUNT, // 直接使用硬编码的账户信息
deviceList: [],
lock: null // 添加lock属性
lock: null, // 确保初始化为 null
currentLockId: null // 可选添加当前选中的锁ID
},
onLaunch: function() {
this.initBluetooth()
// 可以添加一个更新锁信息的方法
updateLockInfo(lock) {
if (lock && lock.lockId) {
this.globalData.lock = lock;
this.globalData.currentLockId = lock.lockId;
console.log('全局锁信息已更新:', lock);
}
},
onLaunch: async function() {
await this.initBluetooth()
},
async initBluetooth() {
// 初始化SDK
init({
clientId: 'Tmj4XoB9dkXjD7t7OHKHl564rJdmvPm9',
clientSecret: 'g1VvSbN0Ya3IqPkA8j9Xn54PE1L8zGiy',
env: 'SKY',
isReportLog: true,
accounts: []
})
try {
// 注册账号
const registerResult = await register()
if (registerResult.code === Result.Success.code) {
this.globalData.accountInfo = registerResult.data
// 使用注册的账号重新初始化
init({
clientId: 'Tmj4XoB9dkXjD7t7OHKHl564rJdmvPm9',
clientSecret: 'g1VvSbN0Ya3IqPkA8j9Xn54PE1L8zGiy',
env: 'SKY',
isReportLog: true,
accounts: [this.globalData.accountInfo]
})
wx.showLoading({
title: '初始化中...',
mask: true
});
try {
// 请求蓝牙权限
await wx.authorize({
scope: 'scope.bluetooth'
})
} catch (error) {
wx.showModal({
title: '提示',
content: '获取蓝牙权限失败',
showCancel: false
})
}
}
// 使用硬编码的账户初始化SDK
init({
clientId: 'Tmj4XoB9dkXjD7t7OHKHl564rJdmvPm9',
clientSecret: 'g1VvSbN0Ya3IqPkA8j9Xn54PE1L8zGiy',
env: 'SKY',
isReportLog: true,
accounts: [MOCK_ACCOUNT] // 使用硬编码的账户
});
// 跳过注册流程,直接请求蓝牙权限
await wx.authorize({ scope: 'scope.bluetooth' });
wx.hideLoading();
console.log('初始化完成,使用硬编码账户:', this.globalData.accountInfo);
} catch (error) {
console.error('初始化失败:', error)
wx.hideLoading()
wx.showModal({
title: '提示',
content: '蓝牙初始化失败,请检查蓝牙权限',
title: '初始化失败',
content: '请检查网络连接和蓝牙权限后重试',
showCancel: false
})
}

View File

@ -1,9 +1,11 @@
{
"pages": [
"pages/index/index",
"pages/password/password",
"pages/lock/lock",
"pages/lockDetail/lockDetail",
"pages/card/card",
"pages/fingerprint/fingerprint"
"pages/fingerprint/fingerprint",
"pages/password/password"
],
"window": {
"backgroundTextStyle": "light",

View File

@ -13,511 +13,107 @@ const {
updateSupportFunctionsWithParams,
remoteUnLock,
readSupportFunctions,
getLockList,
} = requirePlugin('starCloud')
Page({
data: {
list: [],
accountInfo: {},
lock: null,
eventChannel: null,
isSearching: false,
loading: false, // 添加loading状态
showPassageMode: false,
passageModeForm: {
passageMode: '1',
startDate: '',
endDate: '',
isAllDay: '0',
cycleType: 'workday', // 添加循环类型workday/weekend/everyday
autoUnlock: '1'
},
},
startSearching() {
if (this.data.isSearching) {
wx.showToast({
title: '正在搜索中',
icon: 'none'
})
return
}
this.setData({isSearching: true})
searchDevice((result) => {
if (result.code === Result.Success.code) {
this.setData({
list: result.data.list
})
getApp().globalData.deviceList = result.data.list
}
})
wx.showToast({
title: '开始搜索设备',
icon: 'none'
})
},
stopSearching() {
if (!this.data.isSearching) return
stopSearchDevice()
this.setData({isSearching: false})
wx.showToast({
title: '已停止搜索',
icon: 'none'
})
},
onHide() {
this.stopSearching()
},
onUnload() {
this.stopSearching()
deviceType: 'lock', // 默认选择锁
retryCount: 0,
maxRetries: 3
},
onLoad() {
// 从app.globalData获取数据
this.checkAccountInfo();
},
// 检查账户信息
checkAccountInfo() {
const app = getApp();
// 确保accountInfo包含必要的字段
const accountInfo = {
...app.globalData.accountInfo,
account: app.globalData.accountInfo.account || '',
password: app.globalData.accountInfo.password || '',
aesKey: app.globalData.accountInfo.aesKey || '',
};
this.setData({
list: app.globalData.deviceList,
accountInfo: accountInfo
});
// 打印检查accountInfo结构
console.log('Current accountInfo:', accountInfo);
console.log('检查账户信息:', app.globalData.accountInfo);
// 修正字段检查
if (app.globalData.accountInfo && app.globalData.accountInfo.username) {
const accountInfo = {
username: app.globalData.accountInfo.username,
password: app.globalData.accountInfo.password,
uid: app.globalData.accountInfo.uid
};
this.setData({ accountInfo });
console.log('设置账户信息成功:', this.data.accountInfo);
return true;
} else {
if (this.data.retryCount < this.data.maxRetries) {
wx.showLoading({
title: '正在初始化...'
});
setTimeout(() => {
this.setData({
retryCount: this.data.retryCount + 1
});
wx.hideLoading();
this.checkAccountInfo();
}, 1500);
return false;
} else {
wx.hideLoading();
wx.showModal({
title: '初始化失败',
content: '账户信息获取失败,请重启小程序',
showCancel: false
});
return false;
}
}
},
async requestBluetoothPermission() {
try {
const res = await wx.authorize({
scope: 'scope.bluetooth'
});
this.startSearching();
} catch (error) {
wx.showModal({
title: '提示',
content: '请授权蓝牙权限以使用设备功能',
// 切换设备类型
onTypeChange(e) {
this.setData({
deviceType: e.detail.value
});
},
// 进入设备页面
enterDevice() {
console.log('当前账户信息:', this.data.accountInfo);
if (!this.data.accountInfo || !this.data.accountInfo.username) {
// 再次尝试获取账户信息
if (this.checkAccountInfo()) {
this.navigateToDevicePage();
} else {
wx.showToast({
title: '账户信息未准备好',
icon: 'none'
});
}
return;
}
this.navigateToDevicePage();
},
// 添加导航方法
navigateToDevicePage() {
if (this.data.deviceType === 'lock') {
wx.navigateTo({
url: '/pages/lock/lock',
success: (res) => {
if (res.confirm) {
wx.openSetting();
}
// 确保传递完整的账户信息
res.eventChannel.emit('acceptDataFromOpenerPage', {
accountInfo: this.data.accountInfo
});
}
});
}
},
// 搜索设备
searchDevice() {
searchDevice(this.searchDeviceCallback)
},
// 搜索设备回调
searchDeviceCallback(result) {
if (result.code === Result.Success.code) {
this.setData({
list: result.data.list
})
}
},
// 添加通用的loading处理方法
async handleApiCall(apiFunc, loadingText = '处理中...') {
this.setData({loading: true})
wx.showLoading({title: loadingText, mask: true})
try {
await apiFunc()
} finally {
this.setData({loading: false})
wx.hideLoading()
}
},
// 修改绑定设备方法
bindDevice(e) {
const {device} = e.currentTarget.dataset
wx.showModal({
title: '提示',
content: `确定绑定${device.name}吗?`,
success: async (res) => {
if (res.confirm) {
await this.handleApiCall(async () => {
const result = await bindDevice({
accountInfo: this.data.accountInfo,
name: device.name,
})
if (result.code === Result.Success.code) {
// 合并设备信息,确保显示完整信息
const lockInfo = {
...device, // 包含搜索时的设备信息name, mac, uid等
...result.data.lock // 包含绑定后的信息lockId, keyId等
}
this.setData({
lock: lockInfo,
list: [] // 清空搜索列表
})
// 更新app.globalData中的lock
getApp().globalData.lock = lockInfo
this.stopSearching()
wx.showToast({
title: '绑定成功',
icon: 'success',
})
}
}, '绑定中...')
}
}
})
},
// 修改开门方法
async openDoor() {
await this.handleApiCall(async () => {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
})
const result = await openDoor({
accountInfo: this.data.accountInfo,
disconnect: true,
type: 'open'
})
if (result.code === Result.Success.code) {
wx.showToast({
title: '开门成功',
icon: 'success',
})
}
}, '开门中...')
},
// 修改删除锁方法
async deleteLock() {
wx.showModal({
title: '提示',
content: '确定要删除此设备吗?',
success: async (res) => {
if (res.confirm) {
await this.handleApiCall(async () => {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
})
const result = await deleteLock({
accountInfo: this.data.accountInfo,
})
if (result.code === Result.Success.code) {
this.setData({
lock: null,
list: []
})
wx.showToast({
title: '删除成功',
icon: 'success',
})
}
}, '删除中...')
}
}
})
},
// 获取锁支持项
async getLockSupportFeatures() {
const result = await getLockSupportFeatures({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
})
console.log('锁支持项', result)
},
navigateToCard() {
wx.navigateTo({
url: '/pages/card/card'
})
},
navigateToFingerprint() {
wx.navigateTo({
url: '/pages/fingerprint/fingerprint'
})
},
navigateToPassword() {
wx.navigateTo({
url: '/pages/password/password',
success: (res) => {
res.eventChannel.emit('acceptDataFromOpenerPage', {
accountInfo: this.data.accountInfo,
lock: this.data.lock
})
}
});
},
setAdminPasswordOperate(e) {
const operate = e.currentTarget.dataset.operate;
wx.showModal({
title: '设置管理员密码',
editable: true,
placeholderText: '请输入管理员密码',
success: (res) => {
if (res.confirm && res.content) {
this._setAdminPasswordOperate(Number(operate), res.content);
}
}
});
},
async _setAdminPasswordOperate(operate, password) {
await this.handleApiCall(async () => {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
});
const params = {
accountInfo: this.data.accountInfo,
adminPwd: password
};
const result = await updateAdminPassword(params);
if (result.code === Result.Success.code) {
wx.showToast({
title: '设置成功',
icon: 'success'
});
}
console.log('_setAdminPasswordOperate', result);
}, '设置中...');
},
async updateAutoLock() {
wx.showModal({
title: '自动闭锁设置',
editable: true,
showCancel: true,
placeholderText: '请输入自动闭锁时间(秒)',
success: async (res) => {
if (res.confirm) {
const value = parseInt(res.content);
if (!res.content) {
// 用户未输入数值,视为关闭自动闭锁
this._updateAutoLockSetting(0);
} else if (isNaN(value) || value < 1) {
wx.showToast({
title: '请输入大于1的整数',
icon: 'none'
});
} else {
// 输入合法,更新设置
this._updateAutoLockSetting(value);
}
}
}
});
},
async _updateAutoLockSetting(seconds) {
await this.handleApiCall(async () => {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
});
const params = {
accountInfo: this.data.accountInfo,
featureBit: 29,
data: Number(seconds),
withParams: true
};
const result = await updateSupportFunctionsWithParams(params);
if (result.code === Result.Success.code) {
wx.showToast({
title: seconds === 0 ? '已关闭自动闭锁' : '设置成功',
icon: 'success'
});
}
console.log('updateAutoLock', result);
}, '设置中...');
},
async updateLockSound() {
wx.showActionSheet({
itemList: ['关闭', '1档', '2档', '3档', '4档', '5档'],
success: (res) => {
// res.tapIndex 从0开始正好对应音量级别
this._updateLockSoundSetting(res.tapIndex);
}
});
},
async _updateLockSoundSetting(volume) {
await this.handleApiCall(async () => {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
});
const params = {
accountInfo: this.data.accountInfo,
featureBit: 33,
data: Number(volume),
withParams: true
};
const result = await updateSupportFunctionsWithParams(params);
if (result.code === Result.Success.code) {
wx.showToast({
title: volume === 0 ? '已关闭提示音' : `已设置为${volume}`,
icon: 'success'
});
}
console.log('updateLockSound', result);
}, '设置中...');
},
async remoteUnLockRequest() {
console.log('remoteUnLockRequest', await remoteUnLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
}));
},
async enableAntiPryAlarm(e) {
const operate = e.currentTarget.dataset.operate;
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
})
const parms = {
accountInfo: this.data.accountInfo,
featureBit: 30,
featureEnable: operate,
withParams: false
};
const reslut = await updateSupportFunctionsWithParams(parms);
console.log('enableAntiPryAlarm', reslut)
},
async updatePassageMode() {
this.setData({
showPassageMode: true
});
},
hidePassageMode() {
this.setData({
showPassageMode: false
});
},
onPassageModeInput(e) {
const {field} = e.currentTarget.dataset;
const {value} = e.detail;
this.setData({
[`passageModeForm.${field}`]: value
});
},
async submitPassageMode() {
try {
const form = this.data.passageModeForm;
const startTime = parseInt(form.startDate);
const endTime = parseInt(form.endDate);
// 验证输入
if (!form.startDate || !form.endDate) {
throw new Error('请输入开始和结束时间');
}
if (isNaN(startTime) || startTime < 0 || startTime > 1440 ||
isNaN(endTime) || endTime < 0 || endTime > 1440) {
throw new Error('时间必须在0-1440分钟之间');
}
if (endTime <= startTime) {
throw new Error('结束时间必须大于开始时间');
}
// 根据循环类型设置weekDay
let weekDay;
switch (form.cycleType) {
case 'workday':
weekDay = [1, 2, 3, 4, 5];
break;
case 'weekend':
weekDay = [6, 7];
break;
case 'everyday':
weekDay = [1, 2, 3, 4, 5, 6, 7];
break;
default:
throw new Error('请选择循环类型');
}
const params = {
passageMode: parseInt(form.passageMode),
startDate: startTime,
endDate: endTime,
isAllDay: parseInt(form.isAllDay),
weekDay,
autoUnlock: parseInt(form.autoUnlock)
};
await this._updatePassageModeSetting(params);
this.hidePassageMode();
} catch (error) {
} else {
wx.showToast({
title: error.message,
title: '网关功能开发中',
icon: 'none'
});
}
},
async _updatePassageModeSetting(params) {
await this.handleApiCall(async () => {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
});
const result = await updateSupportFunctionsWithParams({
accountInfo: this.data.accountInfo,
featureBit: 50,
data: params,
withParams: true
});
if (result.code === Result.Success.code) {
wx.showToast({
title: '设置成功',
icon: 'success'
});
} else {
throw new Error(result.msg || '设置失败');
}
}, '设置中...');
},
async readSupportFunctionsSet() {
await this.handleApiCall(async () => {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
});
const result = await readSupportFunctions({
lockId: this.data.lock.lockId
});
console.log('readSupportFunctionsSet', result);
wx.showModal({
title: '功能设置',
content: JSON.stringify(result.data, null, 2),
showCancel: false
});
}, '读取中...');
}
})
});

View File

@ -1,154 +1,25 @@
<view class="container">
<!-- 未绑定设备时显示搜索界面 -->
<view class="device-list" wx:if="{{!lock}}">
<view class="section-title">可用设备</view>
<view class="search-section">
<button class="search-btn {{isSearching ? 'searching' : ''}}"
bind:tap="startSearching"
disabled="{{loading}}">
{{isSearching ? '正在搜索...' : '搜索设备'}}
</button>
<button wx:if="{{isSearching}}"
class="stop-btn"
bind:tap="stopSearching"
disabled="{{loading}}">停止搜索</button>
</view>
<view class="form-card">
<view class="card-title">选择设备类型</view>
<view wx:for="{{list}}"
wx:key="name"
class="device"
bind:tap="bindDevice"
data-device="{{item}}">
<view class="device-name">{{item.name}}</view>
<view class="device-status">点击绑定</view>
</view>
<view wx:if="{{list.length === 0}}" class="empty-tip">
{{isSearching ? '正在搜索设备...' : '未找到设备,点击搜索按钮开始搜索'}}
</view>
</view>
<radio-group class="device-types" bind:change="onTypeChange">
<label class="type-item {{deviceType === 'lock' ? 'active' : ''}}">
<radio value="lock" checked="{{deviceType === 'lock'}}"/>
<view class="type-content">
<text class="type-name">智能门锁</text>
<text class="type-desc">管理您的智能门锁设备</text>
</view>
</label>
<label class="type-item {{deviceType === 'gateway' ? 'active' : ''}}">
<radio value="gateway" checked="{{deviceType === 'gateway'}}"/>
<view class="type-content">
<text class="type-name">智能网关</text>
<text class="type-desc">管理您的智能网关设备</text>
</view>
</label>
</radio-group>
<!-- 已绑定设备时显示操作界面 -->
<view class="action-section" wx:else>
<view class="device-info">
<view class="info-item">
<text class="label">设备名称:</text>
<text class="value">{{lock.lockName || lock.name || '未知'}}</text>
</view>
<view class="info-item" wx:if="{{lock.mac}}">
<text class="label">MAC地址</text>
<text class="value">{{lock.mac}}</text>
</view>
<view class="info-item" wx:if="{{lock.uid}}">
<text class="label">UID</text>
<text class="value">{{lock.uid}}</text>
</view>
<view class="info-item" wx:if="{{lock.keyId}}">
<text class="label">钥匙ID</text>
<text class="value">{{lock.keyId}}</text>
</view>
<view class="info-item" wx:if="{{lock.lockId}}">
<text class="label">锁ID</text>
<text class="value">{{lock.lockId}}</text>
</view>
</view>
<view class="main-actions">
<button class="primary-btn" bind:tap="openDoor">开门</button>
<button class="primary-btn" bind:tap="remoteUnLockRequest">远程开锁</button>
</view>
<view class="management-section">
<view class="section-title">管理功能</view>
<view class="grid-buttons">
<button bind:tap="navigateToCard">卡片管理</button>
<button bind:tap="navigateToFingerprint">指纹管理</button>
<button bind:tap="navigateToPassword">密码管理</button>
<button bind:tap="setAdminPasswordOperate" data-operate="0">设置管理员密码</button>
</view>
</view>
<view class="settings-section">
<view class="section-title">设置选项</view>
<view class="grid-buttons">
<button bind:tap="updateAutoLock">自动闭锁设置</button>
<button bind:tap="updateLockSound">提示音量</button>
<button bind:tap="updatePassageMode">常开模式</button>
<button bind:tap="readSupportFunctionsSet">读取功能设置</button>
</view>
</view>
<button class="danger-btn" bind:tap="deleteLock">删除设备</button>
</view>
</view>
<!-- 常开模式设置弹出层 -->
<view class="passage-mode-popup {{showPassageMode ? 'show' : ''}}">
<view class="popup-mask" catch:tap="hidePassageMode"></view>
<view class="popup-content">
<view class="popup-header">
<text class="title">常开模式设置</text>
<text class="close" bind:tap="hidePassageMode">×</text>
</view>
<scroll-view class="popup-body" scroll-y>
<view class="form-item">
<text class="label">常开模式状态</text>
<radio-group class="radio-group" bind:change="onPassageModeInput" data-field="passageMode">
<radio value="1" checked="{{passageModeForm.passageMode === '1'}}">开启</radio>
<radio value="0" checked="{{passageModeForm.passageMode === '0'}}">关闭</radio>
</radio-group>
</view>
<view class="form-item">
<text class="label">是否全天</text>
<radio-group class="radio-group" bind:change="onPassageModeInput" data-field="isAllDay">
<radio value="1" checked="{{passageModeForm.isAllDay === '1'}}">是</radio>
<radio value="0" checked="{{passageModeForm.isAllDay === '0'}}">否</radio>
</radio-group>
</view>
<view class="form-item" wx:if="{{passageModeForm.isAllDay === '0'}}">
<text class="label">开始时间(分钟)</text>
<input type="number" class="input"
value="{{passageModeForm.startDate}}"
bind:input="onPassageModeInput"
data-field="startDate"
placeholder="输入0-1440之间的数字"/>
<text class="hint">例如:\n480 = 08:00\n720 = 12:00\n1080 = 18:00</text>
</view>
<view class="form-item" wx:if="{{passageModeForm.isAllDay === '0'}}">
<text class="label">结束时间(分钟)</text>
<input type="number" class="input"
value="{{passageModeForm.endDate}}"
bind:input="onPassageModeInput"
data-field="endDate"
placeholder="输入0-1440之间的数字"/>
<text class="hint">注意:结束时间必须大于开始时间</text>
</view>
<view class="form-item">
<text class="label">循环方式</text>
<radio-group class="radio-group" bind:change="onPassageModeInput" data-field="cycleType">
<radio value="workday" checked="{{passageModeForm.cycleType === 'workday'}}">工作日循环</radio>
<radio value="weekend" checked="{{passageModeForm.cycleType === 'weekend'}}">周末循环</radio>
<radio value="everyday" checked="{{passageModeForm.cycleType === 'everyday'}}">每天循环</radio>
</radio-group>
</view>
<view class="form-item">
<text class="label">自动开锁</text>
<radio-group class="radio-group" bind:change="onPassageModeInput" data-field="autoUnlock">
<radio value="1" checked="{{passageModeForm.autoUnlock === '1'}}">开启</radio>
<radio value="0" checked="{{passageModeForm.autoUnlock === '0'}}">关闭</radio>
</radio-group>
</view>
</scroll-view>
<view class="popup-footer">
<button class="cancel-btn" bind:tap="hidePassageMode">取消</button>
<button class="confirm-btn" bind:tap="submitPassageMode">确定</button>
</view>
<button class="submit-btn" bind:tap="enterDevice">进入设备管理</button>
</view>
</view>

View File

@ -1,347 +1,85 @@
.container {
padding: 32rpx;
background: #f5f5f5;
}
.device-list {
background: white;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 32rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
margin-bottom: 24rpx;
}
.device {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
margin: 16rpx 0;
background: #f8f9fa;
border-radius: 8rpx;
}
.device-name {
font-size: 28rpx;
color: #333;
}
.device-status {
font-size: 24rpx;
color: #2c405a;
}
.action-section {
background: white;
border-radius: 16rpx;
padding: 24rpx;
}
.main-actions {
display: flex;
gap: 16rpx;
margin-bottom: 32rpx;
}
.primary-btn {
flex: 1;
background: #2c405a;
color: white;
}
.management-section,
.settings-section {
margin-bottom: 32rpx;
}
.grid-buttons {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16rpx;
}
.grid-buttons button {
margin: 0;
background: #f8f9fa;
color: #2c405a;
}
.danger-btn {
background: #dc3545;
color: white;
margin-top: 32rpx;
}
.password-time-picker {
padding: 15px;
background: #fff;
margin: 10px 0;
}
.picker-item {
background: #f7f7f7;
min-height: 100vh;
display: flex;
align-items: center;
margin: 10px 0;
justify-content: center;
}
.picker-item text {
width: 80px;
.form-card {
width: 100%;
background: white;
border-radius: 24rpx;
padding: 40rpx;
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.08);
}
.picker {
flex: 1;
padding: 5px 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.password-list {
margin: 15px;
padding: 10px;
background: #fff;
border-radius: 8px;
}
.password-list-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
padding-bottom: 5px;
border-bottom: 1px solid #eee;
}
.password-item {
padding: 10px;
margin: 5px 0;
background: #f5f5f5;
border-radius: 4px;
}
.password-item view {
margin: 3px 0;
font-size: 14px;
}
.password-time-picker {
padding: 15px;
background: #fff;
margin: 10px;
border-radius: 8px;
}
.time-section {
margin-bottom: 15px;
}
.section-title {
font-size: 14px;
.card-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-bottom: 8px;
margin-bottom: 40rpx;
text-align: center;
}
.picker-group {
display: flex;
gap: 10px;
}
.picker {
flex: 1;
padding: 8px 12px;
background: #f5f5f5;
border-radius: 4px;
font-size: 14px;
color: #333;
}
.pwd-type-picker {
padding: 15px;
background: #fff;
margin: 10px;
border-radius: 8px;
}
.section-title {
font-size: 14px;
color: #333;
margin-bottom: 8px;
}
.picker {
padding: 8px 12px;
background: #f5f5f5;
border-radius: 4px;
font-size: 14px;
color: #333;
}
.picker-value {
border: 1rpx solid #ddd;
border-radius: 8rpx;
padding: 20rpx;
font-size: 28rpx;
background: #f8f9fa;
color: #333;
}
.picker-value:active {
background: #eee;
}
.search-btn {
width: 90%;
margin: 10px auto;
background-color: #007AFF;
color: white;
border-radius: 8px;
}
.device-info {
background: #f5f5f5;
padding: 15px;
border-radius: 8px;
margin: 10px;
}
.info-item {
display: flex;
padding: 5px 0;
font-size: 14px;
}
.info-item .label {
color: #666;
width: 80px;
}
.info-item .value {
color: #333;
flex: 1;
word-break: break-all;
}
.passage-mode-popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
display: none;
}
.passage-mode-popup.show {
display: block;
}
.popup-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
}
.popup-content {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
border-radius: 24rpx 24rpx 0 0;
max-height: 85vh;
.device-types {
display: flex;
flex-direction: column;
overflow: hidden;
gap: 24rpx;
margin-bottom: 48rpx;
}
.popup-header {
padding: 30rpx;
border-bottom: 1rpx solid #eee;
}
.popup-header .title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.popup-header .close {
position: absolute;
right: 30rpx;
top: 30rpx;
font-size: 40rpx;
color: #999;
line-height: 1;
}
.popup-body {
flex: 1;
padding: 30rpx;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.form-item {
margin-bottom: 40rpx;
}
.form-item .label {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 20rpx;
font-weight: 500;
}
.form-item .input {
border: 1rpx solid #ddd;
border-radius: 8rpx;
padding: 20rpx;
font-size: 28rpx;
.type-item {
display: flex;
align-items: center;
padding: 24rpx;
border-radius: 16rpx;
background: #f8f9fa;
transition: all 0.3s;
}
.form-item .hint {
.type-item.active {
background: rgba(0,122,255,0.1);
border: 2rpx solid #007AFF;
}
.type-item radio {
transform: scale(0.8);
margin-right: 16rpx;
}
.type-content {
flex: 1;
}
.type-name {
display: block;
font-size: 32rpx;
color: #333;
font-weight: 500;
margin-bottom: 8rpx;
}
.type-desc {
font-size: 24rpx;
color: #666;
margin-top: 12rpx;
line-height: 1.5;
}
.radio-group {
display: flex;
gap: 30rpx;
.submit-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background: linear-gradient(135deg, #007AFF, #40a9ff);
color: white;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 500;
box-shadow: 0 4rpx 12rpx rgba(0,122,255,0.2);
transition: all 0.3s;
}
.popup-footer {
padding: 30rpx;
border-top: 1rpx solid #eee;
display: flex;
gap: 20rpx;
}
.popup-footer button {
flex: 1;
margin: 0;
height: 80rpx;
line-height: 80rpx;
font-size: 28rpx;
border-radius: 8rpx;
}
.cancel-btn {
background: #f5f5f5;
color: #333;
}
.confirm-btn {
background: #007aff;
color: #fff;
.submit-btn:active {
transform: scale(0.98);
box-shadow: 0 2rpx 6rpx rgba(0,122,255,0.2);
}

View File

@ -0,0 +1,165 @@
const {
searchDevice,
bindDevice,
getLockList,
stopSearchDevice,
Result
} = requirePlugin('starCloud')
Page({
data: {
searchList: [], // 搜索到的设备列表
boundList: [], // 已绑定的设备列表
accountInfo: {},
isSearching: false,
loading: false,
needRefresh: false, // 添加needRefresh标记
},
onLoad() {
// 页面加载时就获取账户信息并请求设备列表
const app = getApp();
if (!app.globalData.accountInfo) {
wx.showToast({
title: '账户信息未准备好',
icon: 'none'
});
return;
}
// 获取正确的账户信息字段
const accountInfo = {
username: app.globalData.accountInfo.username,
password: app.globalData.accountInfo.password,
uid: app.globalData.accountInfo.uid
};
// 设置accountInfo并立即获取设备列表
this.setData({ accountInfo }, () => {
this.getBoundDevices();
});
},
onShow() {
// 简化判断,避免使用可选链
if (this.data.accountInfo && this.data.accountInfo.username) {
this.getBoundDevices();
// 重置刷新标记
this.setData({ needRefresh: false });
}
},
// 获取已绑定设备列表
async getBoundDevices() {
try {
wx.showLoading({
title: '获取设备列表...'
});
// 确保使用正确的账户信息字段
if (!this.data.accountInfo || !this.data.accountInfo.username) {
throw new Error('账户信息未准备好');
}
const result = await getLockList({
accountInfo: this.data.accountInfo
});
if (result.code === Result.Success.code) {
this.setData({
boundList: result.data.groupList[0].lockList || []
});
} else {
throw new Error(result.msg || '获取设备列表失败');
}
} catch (error) {
wx.showToast({
title: error.message,
icon: 'none'
});
} finally {
wx.hideLoading();
}
},
startSearching() {
if (this.data.isSearching) return;
this.setData({
isSearching: true,
searchList: []
});
searchDevice((result) => {
if (result.code === Result.Success.code) {
const boundMacs = this.data.boundList.map(item => item.mac);
const newDevices = result.data.list.filter(item => !boundMacs.includes(item.mac));
this.setData({ searchList: newDevices });
}
});
},
stopSearching() {
if (!this.data.isSearching) return;
stopSearchDevice();
this.setData({ isSearching: false });
},
bindDevice(e) {
const { device } = e.currentTarget.dataset;
wx.showModal({
title: '提示',
content: `确定绑定${device.name}吗?`,
success: async (res) => {
if (res.confirm) {
wx.showLoading({ title: '绑定中...', mask: true });
try {
// 使用正确的账户信息字段
const result = await bindDevice({
accountInfo: {
username: this.data.accountInfo.username,
password: this.data.accountInfo.password,
uid: this.data.accountInfo.uid
},
name: device.name,
});
if (result.code === Result.Success.code) {
await this.getBoundDevices();
wx.showToast({
title: '绑定成功',
icon: 'success',
});
}
} catch (error) {
wx.showToast({
title: '绑定失败',
icon: 'none'
});
} finally {
wx.hideLoading();
}
}
}
});
},
selectLock(e) {
const { lock } = e.currentTarget.dataset;
// 直接更新全局数据
const app = getApp();
app.globalData.lock = lock;
// 简化跳转
wx.navigateTo({
url: '/pages/lockDetail/lockDetail'
});
},
onHide() {
this.stopSearching();
},
onUnload() {
this.stopSearching();
}
})

View File

@ -0,0 +1,3 @@
{
"usingComponents": {}
}

View File

@ -0,0 +1,39 @@
<view class="container">
<!-- 已绑定设备列表 -->
<view class="bound-list">
<view class="section-title">已绑定设备</view>
<view wx:for="{{boundList}}"
wx:key="lockId"
class="device"
bind:tap="selectLock"
data-lock="{{item}}">
<view class="device-name">{{item.lockName || item.name}}</view>
<view class="device-status">点击选择</view>
</view>
<view wx:if="{{boundList.length === 0}}" class="empty-tip">
暂无绑定设备
</view>
</view>
<!-- 搜索新设备 -->
<view class="search-section">
<view class="section-title">搜索新设备</view>
<button class="search-btn {{isSearching ? 'searching' : ''}}"
bind:tap="startSearching"
disabled="{{loading}}">
{{isSearching ? '正在搜索...' : '搜索设备'}}
</button>
<button wx:if="{{isSearching}}"
class="stop-btn"
bind:tap="stopSearching">停止搜索</button>
<view wx:for="{{searchList}}"
wx:key="mac"
class="device"
bind:tap="bindDevice"
data-device="{{item}}">
<view class="device-name">{{item.name}}</view>
<view class="device-status">点击绑定</view>
</view>
</view>
</view>

View File

@ -0,0 +1,87 @@
.container {
padding: 32rpx;
background: #f7f7f7;
min-height: 100vh;
}
.bound-list, .search-section {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 32rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05);
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 24rpx;
padding-left: 16rpx;
border-left: 8rpx solid #007AFF;
}
.device {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
margin: 16rpx 0;
background: #f8f9fa;
border-radius: 12rpx;
transition: all 0.3s;
}
.device:active {
background: #eef0f2;
transform: scale(0.98);
}
.device-name {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.device-status {
font-size: 24rpx;
color: #007AFF;
background: rgba(0,122,255,0.1);
padding: 8rpx 16rpx;
border-radius: 24rpx;
}
.search-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background: #007AFF;
color: white;
border-radius: 44rpx;
font-size: 28rpx;
font-weight: 500;
margin: 20rpx 0;
transition: opacity 0.3s;
}
.search-btn.searching {
background: #40a9ff;
}
.stop-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background: #f5f5f5;
color: #666;
border-radius: 44rpx;
font-size: 28rpx;
margin: 20rpx 0;
}
.empty-tip {
text-align: center;
color: #999;
font-size: 28rpx;
padding: 48rpx 0;
}

View File

@ -0,0 +1,405 @@
const {
Result,
openDoor,
deleteLock,
selectLock,
updateAdminPassword,
updateSupportFunctionsWithParams,
remoteUnLock,
readSupportFunctions,
} = requirePlugin('starCloud')
Page({
data: {
lock: null, // 当前选中的锁
accountInfo: {},
loading: false,
showPassageMode: false,
passageModeForm: {
passageMode: '1',
startDate: '',
endDate: '',
isAllDay: '0',
cycleType: 'workday',
autoUnlock: '1'
},
},
onLoad(options) {
const app = getApp();
// 尝试获取全局数据
const lock = app.globalData.lock;
const accountInfo = app.globalData.accountInfo;
console.log('lockDetail onLoad:', { lock, accountInfo });
if (!lock || !accountInfo) {
wx.showToast({
title: '获取设备信息失败',
icon: 'none'
});
setTimeout(() => {
wx.navigateBack();
}, 1500);
return;
}
// 设置数据到页面
this.setData({
lock,
accountInfo
});
},
// 导航到卡片管理
navigateToCard() {
wx.navigateTo({
url: '/pages/card/card'
})
},
// 导航到指纹管理
navigateToFingerprint() {
wx.navigateTo({
url: '/pages/fingerprint/fingerprint'
})
},
// 导航到密码管理
navigateToPassword() {
wx.navigateTo({
url: '/pages/password/password'
})
},
// 设置管理员密码
setAdminPasswordOperate(e) {
wx.showModal({
title: '设置管理员密码',
editable: true,
placeholderText: '请输入管理员密码',
success: (res) => {
if (res.confirm && res.content) {
this._setAdminPasswordOperate(res.content);
}
}
});
},
// 更新自动闭锁设置
async updateAutoLock() {
wx.showModal({
title: '自动闭锁设置',
editable: true,
showCancel: true,
placeholderText: '请输入自动闭锁时间(秒)',
success: async (res) => {
if (res.confirm) {
const value = parseInt(res.content);
if (!res.content) {
this._updateAutoLockSetting(0);
} else if (isNaN(value) || value < 1) {
wx.showToast({
title: '请输入大于1的整数',
icon: 'none'
});
} else {
this._updateAutoLockSetting(value);
}
}
}
});
},
// 更新声音设置
async updateLockSound() {
wx.showActionSheet({
itemList: ['关闭', '1档', '2档', '3档', '4档', '5档'],
success: (res) => {
this._updateLockSoundSetting(res.tapIndex);
}
});
},
// 远程开锁
async remoteUnLockRequest() {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
});
console.log('remoteUnLockRequest', await remoteUnLock());
},
// 读取锁设置
async readSupportFunctionsSet() {
await this.handleApiCall(async () => {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
});
const result = await readSupportFunctions({
lockId: this.data.lock.lockId
});
wx.showModal({
title: '功能设置',
content: JSON.stringify(result.data, null, 2),
showCancel: false
});
});
},
// 开门操作
async openDoor() {
await this.handleApiCall(async () => {
const app = getApp();
// 确保使用最新的锁信息
await selectLock({
accountInfo: this.data.accountInfo,
lockId: app.globalData.lock.lockId
});
const result = await openDoor({
accountInfo: this.data.accountInfo,
disconnect: true,
type: 'open'
});
if (result.code === Result.Success.code) {
wx.showToast({
title: '开门成功',
icon: 'success'
});
}
}, '开门中...');
},
// 常开模式设置
async updatePassageMode() {
this.setData({
showPassageMode: true,
passageModeForm: {
passageMode: '1',
startDate: '',
endDate: '',
isAllDay: '0',
cycleType: 'workday',
autoUnlock: '1'
}
});
},
hidePassageMode() {
this.setData({
showPassageMode: false
});
},
onPassageModeInput(e) {
const { field } = e.currentTarget.dataset;
const { value } = e.detail;
this.setData({
[`passageModeForm.${field}`]: value
});
},
async submitPassageMode() {
try {
const form = this.data.passageModeForm;
const startTime = parseInt(form.startDate);
const endTime = parseInt(form.endDate);
// 验证输入
if (!form.startDate || !form.endDate) {
throw new Error('请输入开始和结束时间');
}
if (isNaN(startTime) || startTime < 0 || startTime > 1440 ||
isNaN(endTime) || endTime < 0 || endTime > 1440) {
throw new Error('时间必须在0-1440分钟之间');
}
if (endTime <= startTime) {
throw new Error('结束时间必须大于开始时间');
}
// 根据循环类型设置weekDay
let weekDay;
switch (form.cycleType) {
case 'workday':
weekDay = [1, 2, 3, 4, 5];
break;
case 'weekend':
weekDay = [6, 7];
break;
case 'everyday':
weekDay = [1, 2, 3, 4, 5, 6, 7];
break;
default:
throw new Error('请选择循环类型');
}
await this._updatePassageModeSetting({
passageMode: parseInt(form.passageMode),
startDate: startTime,
endDate: endTime,
isAllDay: parseInt(form.isAllDay),
weekDay,
autoUnlock: parseInt(form.autoUnlock)
});
this.hidePassageMode();
} catch (error) {
wx.showToast({
title: error.message,
icon: 'none'
});
}
},
async _updatePassageModeSetting(params) {
await this.handleApiCall(async () => {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
});
const result = await updateSupportFunctionsWithParams({
accountInfo: this.data.accountInfo,
featureBit: 50,
data: params,
withParams: true
});
if (result.code === Result.Success.code) {
wx.showToast({
title: '设置成功',
icon: 'success'
});
} else {
throw new Error(result.msg || '设置失败');
}
}, '设置中...');
},
// 删除设备
async deleteLock() {
wx.showModal({
title: '删除设备',
content: '确定要删除此设备吗?此操作不可恢复。',
success: async (res) => {
if (res.confirm) {
await this.handleApiCall(async () => {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
});
const result = await deleteLock({
accountInfo: this.data.accountInfo
});
if (result.code === Result.Success.code) {
wx.showToast({
title: '删除成功',
icon: 'success'
});
const app = getApp();
app.globalData.lock = null;
setTimeout(() => {
wx.navigateBack();
}, 1500);
} else {
throw new Error(result.msg || '删除失败');
}
}, '删除中...');
}
}
});
},
// 通用的API调用方法
async handleApiCall(apiFunc, loadingText = '处理中...') {
const app = getApp();
this.setData({ loading: true });
wx.showLoading({ title: loadingText, mask: true });
try {
// 检查锁信息是否存在
if (!app.globalData.lock || !app.globalData.lock.lockId) {
throw new Error('锁信息不存在');
}
await apiFunc();
} catch (error) {
wx.showToast({
title: error.message || '操作失败',
icon: 'none'
});
} finally {
this.setData({ loading: false });
wx.hideLoading();
}
},
// 具体的设置实现方法
async _setAdminPasswordOperate(password) {
await this.handleApiCall(async () => {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
});
const result = await updateAdminPassword({
accountInfo: this.data.accountInfo,
adminPwd: password
});
if (result.code === Result.Success.code) {
wx.showToast({
title: '设置成功',
icon: 'success'
});
}
});
},
async _updateAutoLockSetting(seconds) {
await this.handleApiCall(async () => {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
});
const result = await updateSupportFunctionsWithParams({
accountInfo: this.data.accountInfo,
featureBit: 29,
data: Number(seconds),
withParams: true
});
if (result.code === Result.Success.code) {
wx.showToast({
title: seconds === 0 ? '已关闭自动闭锁' : '设置成功',
icon: 'success'
});
}
});
},
async _updateLockSoundSetting(volume) {
await this.handleApiCall(async () => {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
});
const result = await updateSupportFunctionsWithParams({
accountInfo: this.data.accountInfo,
featureBit: 33,
data: Number(volume),
withParams: true
});
if (result.code === Result.Success.code) {
wx.showToast({
title: volume === 0 ? '已关闭提示音' : `已设置为${volume}`,
icon: 'success'
});
}
});
}
});

View File

@ -0,0 +1,3 @@
{
"usingComponents": {}
}

View File

@ -0,0 +1,135 @@
<view class="container">
<view class="form-card">
<view class="device-info">
<view class="info-item">
<text class="label">设备名称:</text>
<text class="value">{{lock.lockName || lock.name || '未知'}}</text>
</view>
<view class="info-item" wx:if="{{lock.mac}}">
<text class="label">MAC地址</text>
<text class="value">{{lock.mac}}</text>
</view>
<view class="info-item" wx:if="{{lock.lockId}}">
<text class="label">锁ID</text>
<text class="value">{{lock.lockId}}</text>
</view>
</view>
<view class="section-title">快捷操作</view>
<view class="action-buttons">
<button class="main-btn primary" bind:tap="openDoor">开门</button>
<button class="main-btn primary" bind:tap="remoteUnLockRequest">远程开锁</button>
</view>
<view class="section-title">管理功能</view>
<view class="grid-buttons">
<button class="feature-btn" bind:tap="navigateToCard">
<view class="feature-icon card-icon"></view>
<text class="feature-name">卡片管理</text>
<text class="feature-desc">IC卡添加与删除</text>
</button>
<button class="feature-btn" bind:tap="navigateToFingerprint">
<view class="feature-icon fingerprint-icon"></view>
<text class="feature-name">指纹管理</text>
<text class="feature-desc">指纹录入与删除</text>
</button>
<button class="feature-btn" bind:tap="navigateToPassword">
<view class="feature-icon password-icon"></view>
<text class="feature-name">密码管理</text>
<text class="feature-desc">密码设置与更改</text>
</button>
<button class="feature-btn" bind:tap="setAdminPasswordOperate">
<view class="feature-icon admin-icon"></view>
<text class="feature-name">管理员密码</text>
<text class="feature-desc">设置管理员权限</text>
</button>
</view>
<view class="section-title">设置选项</view>
<view class="grid-buttons">
<button class="feature-btn" bind:tap="updateAutoLock">
<view class="feature-icon autolock-icon"></view>
<text class="feature-name">自动闭锁</text>
<text class="feature-desc">自动上锁时间</text>
</button>
<button class="feature-btn" bind:tap="updateLockSound">
<view class="feature-icon sound-icon"></view>
<text class="feature-name">提示音量</text>
<text class="feature-desc">调节锁具音量</text>
</button>
<button class="feature-btn" bind:tap="updatePassageMode">
<view class="feature-icon passage-icon"></view>
<text class="feature-name">常开模式</text>
<text class="feature-desc">设置通行时段</text>
</button>
<button class="feature-btn" bind:tap="readSupportFunctionsSet">
<view class="feature-icon settings-icon"></view>
<text class="feature-name">功能设置</text>
<text class="feature-desc">查看所有配置</text>
</button>
</view>
<button class="main-btn danger" bind:tap="deleteLock">删除设备</button>
</view>
<!-- 常开模式设置弹出层 -->
<view class="popup-mask" wx:if="{{showPassageMode}}" bind:tap="hidePassageMode"></view>
<view class="popup-content" wx:if="{{showPassageMode}}">
<view class="popup-header">
<text class="title">常开模式设置</text>
<text class="close" bind:tap="hidePassageMode">×</text>
</view>
<scroll-view class="popup-body" scroll-y>
<view class="form-item">
<text class="label">常开模式状态</text>
<radio-group class="radio-group" bind:change="onPassageModeInput" data-field="passageMode">
<radio value="1" checked="{{passageModeForm.passageMode === '1'}}">开启</radio>
<radio value="0" checked="{{passageModeForm.passageMode === '0'}}">关闭</radio>
</radio-group>
</view>
<view class="form-item">
<text class="label">是否全天</text>
<radio-group class="radio-group" bind:change="onPassageModeInput" data-field="isAllDay">
<radio value="1" checked="{{passageModeForm.isAllDay === '1'}}">是</radio>
<radio value="0" checked="{{passageModeForm.isAllDay === '0'}}">否</radio>
</radio-group>
</view>
<view class="form-item">
<text class="label">循环方式</text>
<radio-group class="radio-group" bind:change="onPassageModeInput" data-field="cycleType">
<radio value="workday" checked="{{passageModeForm.cycleType === 'workday'}}">工作日循环</radio>
<radio value="weekend" checked="{{passageModeForm.cycleType === 'weekend'}}">周末循环</radio>
<radio value="everyday" checked="{{passageModeForm.cycleType === 'everyday'}}">每天循环</radio>
</radio-group>
</view>
<block wx:if="{{passageModeForm.isAllDay === '0'}}">
<view class="form-item">
<text class="label">开始时间(分钟)</text>
<input type="number" class="input" bind:input="onPassageModeInput" data-field="startDate" placeholder="输入0-1440之间的数字"/>
<text class="hint">例如480 = 08:00, 720 = 12:00</text>
</view>
<view class="form-item">
<text class="label">结束时间(分钟)</text>
<input type="number" class="input" bind:input="onPassageModeInput" data-field="endDate" placeholder="输入0-1440之间的数字"/>
</view>
</block>
<view class="form-item">
<text class="label">自动开锁</text>
<radio-group class="radio-group" bind:change="onPassageModeInput" data-field="autoUnlock">
<radio value="1" checked="{{passageModeForm.autoUnlock === '1'}}">开启</radio>
<radio value="0" checked="{{passageModeForm.autoUnlock === '0'}}">关闭</radio>
</radio-group>
</view>
</scroll-view>
<view class="popup-footer">
<button class="cancel-btn" bind:tap="hidePassageMode">取消</button>
<button class="confirm-btn" bind:tap="submitPassageMode">确定</button>
</view>
</view>
</view>

View File

@ -0,0 +1,280 @@
.container {
padding: 32rpx;
background: #f7f7f7;
min-height: 100vh;
}
.form-card {
background: white;
border-radius: 24rpx;
padding: 40rpx;
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.08);
}
.device-info {
background: #f8f9fa;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 32rpx;
}
.info-item {
display: flex;
padding: 12rpx 0;
}
.info-item .label {
width: 160rpx;
color: #666;
font-size: 28rpx;
}
.info-item .value {
flex: 1;
color: #333;
font-size: 28rpx;
font-weight: 500;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin: 40rpx 0 24rpx;
padding-left: 16rpx;
border-left: 8rpx solid #007AFF;
}
.action-buttons {
display: flex;
gap: 24rpx;
margin-bottom: 32rpx;
}
.main-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 500;
transition: all 0.3s;
}
.main-btn.primary {
flex: 1;
background: linear-gradient(135deg, #007AFF, #40a9ff);
color: white;
box-shadow: 0 4rpx 12rpx rgba(0,122,255,0.2);
}
.main-btn.danger {
background: #ff4d4f;
color: white;
margin-top: 48rpx;
}
.main-btn:active {
transform: scale(0.98);
opacity: 0.9;
}
.grid-buttons {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24rpx;
padding: 0 0 24rpx;
}
.feature-btn {
height: 180rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
transition: all 0.3s;
position: relative;
border: 2rpx solid #f0f0f0;
overflow: hidden;
}
.feature-btn::after {
display: none;
}
.feature-btn:active {
transform: scale(0.98);
background: #f8f9fa;
}
.feature-icon {
width: 64rpx;
height: 64rpx;
margin-bottom: 16rpx;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.feature-name {
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 8rpx;
}
.feature-desc {
font-size: 24rpx;
color: #999;
}
/* 功能图标 */
.card-icon {
background-image: url("data:image/svg+xml,%3Csvg t='1677721600000' class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M844.8 115.2H179.2c-38.4 0-64 25.6-64 64v665.6c0 38.4 25.6 64 64 64h665.6c38.4 0 64-25.6 64-64V179.2c0-38.4-25.6-64-64-64z m-384 512H256v-204.8h204.8V627.2z m320-204.8h-256v-64h256v64z m0 128h-256v-64h256v64z' fill='%23007AFF'/%3E%3C/svg%3E");
}
.fingerprint-icon {
background-image: url("data:image/svg+xml,%3Csvg t='1677721600000' class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M512 928c-229.76 0-416-186.24-416-416S282.24 96 512 96s416 186.24 416 416-186.24 416-416 416z m0-64c194.4 0 352-157.6 352-352S706.4 160 512 160 160 317.6 160 512s157.6 352 352 352z m128-352c0-70.4-57.6-128-128-128s-128 57.6-128 128h-64c0-105.6 86.4-192 192-192s192 86.4 192 192h-64z' fill='%23007AFF'/%3E%3C/svg%3E");
}
.password-icon {
background-image: url("data:image/svg+xml,%3Csvg t='1677721600000' class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M768 384h-64V256c0-105.6-86.4-192-192-192s-192 86.4-192 192v128h-64V256c0-140.8 115.2-256 256-256s256 115.2 256 256v128z m64 64H192c-38.4 0-64 25.6-64 64v384c0 38.4 25.6 64 64 64h640c38.4 0 64-25.6 64-64V512c0-38.4-25.6-64-64-64z m-320 256c-38.4 0-64-25.6-64-64s25.6-64 64-64 64 25.6 64 64-25.6 64-64 64z' fill='%23007AFF'/%3E%3C/svg%3E");
}
.admin-icon {
background-image: url("data:image/svg+xml,%3Csvg t='1677721600000' class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M512 512c-105.6 0-192-86.4-192-192s86.4-192 192-192 192 86.4 192 192-86.4 192-192 192z m0-320c-70.4 0-128 57.6-128 128s57.6 128 128 128 128-57.6 128-128-57.6-128-128-128z m320 704H192c-38.4 0-64-25.6-64-64V704c0-140.8 115.2-256 256-256h256c140.8 0 256 115.2 256 256v128c0 38.4-25.6 64-64 64z' fill='%23007AFF'/%3E%3C/svg%3E");
}
.autolock-icon {
background-image: url("data:image/svg+xml,%3Csvg t='1677721600000' class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M512 928c-229.76 0-416-186.24-416-416S282.24 96 512 96s416 186.24 416 416-186.24 416-416 416z m32-416V320h-64v192h192v-64H544z' fill='%23007AFF'/%3E%3C/svg%3E");
}
.sound-icon {
background-image: url("data:image/svg+xml,%3Csvg t='1677721600000' class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M512 928c-229.76 0-416-186.24-416-416S282.24 96 512 96s416 186.24 416 416-186.24 416-416 416z m128-544c-19.2-19.2-51.2-19.2-70.4 0-19.2 19.2-19.2 51.2 0 70.4 19.2 19.2 19.2 51.2 0 70.4-19.2 19.2-51.2 19.2-70.4 0-19.2-19.2-19.2-51.2 0-70.4 19.2-19.2 19.2-51.2 0-70.4-19.2-19.2-51.2-19.2-70.4 0-19.2 19.2-19.2 51.2 0 70.4 57.6 57.6 57.6 150.4 0 208-19.2 19.2-19.2 51.2 0 70.4 19.2 19.2 51.2 19.2 70.4 0 96-96 96-252.8 0-348.8z' fill='%23007AFF'/%3E%3C/svg%3E");
}
.passage-icon {
background-image: url("data:image/svg+xml,%3Csvg t='1677721600000' class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M768 384H640V256c0-70.4-57.6-128-128-128s-128 57.6-128 128v128H256V256c0-140.8 115.2-256 256-256s256 115.2 256 256v128z m64 64H192c-38.4 0-64 25.6-64 64v384c0 38.4 25.6 64 64 64h640c38.4 0 64-25.6 64-64V512c0-38.4-25.6-64-64-64z' fill='%23007AFF'/%3E%3C/svg%3E");
}
.settings-icon {
background-image: url("data:image/svg+xml,%3Csvg t='1677721600000' class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M512 640c-70.4 0-128-57.6-128-128s57.6-128 128-128 128 57.6 128 128-57.6 128-128 128z m0-192c-35.2 0-64 28.8-64 64s28.8 64 64 64 64-28.8 64-64-28.8-64-64-64z m384 352H128c-38.4 0-64-25.6-64-64V288c0-38.4 25.6-64 64-64h768c38.4 0 64 25.6 64 64v448c0 38.4-25.6 64-64 64z' fill='%23007AFF'/%3E%3C/svg%3E");
}
/* 弹出层样式 */
.popup-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 1000;
}
.popup-content {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
border-radius: 24rpx 24rpx 0 0;
z-index: 1001;
max-height: 80vh;
display: flex;
flex-direction: column;
}
.popup-header {
padding: 32rpx;
border-bottom: 1rpx solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.popup-header .title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.popup-header .close {
font-size: 40rpx;
color: #999;
padding: 0 20rpx;
}
.popup-body {
flex: 1;
padding: 32rpx;
overflow-y: auto;
}
.popup-body .form-item {
margin-bottom: 32rpx;
}
.popup-body .label {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 16rpx;
}
.popup-body .radio-group {
display: flex;
flex-wrap: wrap;
gap: 24rpx;
}
.popup-body .input {
width: 100%;
height: 88rpx;
border: 2rpx solid #eee;
border-radius: 8rpx;
padding: 0 24rpx;
font-size: 28rpx;
margin-bottom: 8rpx;
}
.popup-body .hint {
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
display: block;
}
.popup-footer {
padding: 24rpx 32rpx;
border-top: 1rpx solid #eee;
display: flex;
gap: 24rpx;
}
.popup-footer button {
flex: 1;
height: 88rpx;
line-height: 88rpx;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 500;
}
.popup-footer .cancel-btn {
background: #f5f5f5;
color: #666;
}
.popup-footer .confirm-btn {
background: linear-gradient(135deg, #007AFF, #40a9ff);
color: white;
}
.popup-footer button:active {
transform: scale(0.98);
opacity: 0.9;
}

8
package-lock.json generated
View File

@ -24,7 +24,7 @@
"@dcloudio/uni-mp-xhs": "3.0.0-4030620241128001",
"@dcloudio/uni-quickapp-webview": "3.0.0-4030620241128001",
"pinia": "^2.2.8",
"star-cloud-uni": "^1.0.13",
"star-cloud-uni": "^1.0.14",
"vue": "^3.5.13",
"vue-i18n": "^9.1.9"
},
@ -10046,9 +10046,9 @@
}
},
"node_modules/star-cloud-uni": {
"version": "1.0.13",
"resolved": "https://registry.npmjs.org/star-cloud-uni/-/star-cloud-uni-1.0.13.tgz",
"integrity": "sha512-F1LLDwcEAqMkbUDwnbyvqYAbMMhiAJG3rQbduxkOdzdHT3lpxPOxqep6XVi4AfbRbRwbuWXChle81iBLZiK6vw==",
"version": "1.0.14",
"resolved": "https://registry.npmjs.org/star-cloud-uni/-/star-cloud-uni-1.0.14.tgz",
"integrity": "sha512-8KnuxvqiyvDcodaue0t0ofYuMNYqXv2HLw5P1HglEWZqiQyMCMwB8dcx9UxV4X1mdRlgj0/JbxdT5vE/eRT4Og==",
"dependencies": {
"buffer": "^6.0.3",
"crc": "^4.3.2",

View File

@ -52,7 +52,7 @@
"@dcloudio/uni-mp-xhs": "3.0.0-4030620241128001",
"@dcloudio/uni-quickapp-webview": "3.0.0-4030620241128001",
"pinia": "^2.2.8",
"star-cloud-uni": "^1.0.13",
"star-cloud-uni": "^1.0.14",
"vue": "^3.5.13",
"vue-i18n": "^9.1.9"
},

View File

@ -23,6 +23,7 @@ import {
updateSupportFunctionsWithParams,
remoteUnLock,
readSupportFunctionsSetting,
getLockList,
} from 'star-cloud-uni'
// 用于存储事件监听器的Map
const eventListeners = new Map();
@ -318,5 +319,14 @@ module.exports = {
*/
async remoteUnLock(params){
return await remoteUnLock(params)
},
/**
* 获取锁列表
* @param params
* @returns {Promise<*>}
*/
async getLockList(params){
return await getLockList(params);
}
}