fix:调整demo测试页面
This commit is contained in:
parent
891ba072d0
commit
690849af88
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
});
|
||||
}, '读取中...');
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
165
miniprogram/miniprogram/pages/lock/lock.js
Normal file
165
miniprogram/miniprogram/pages/lock/lock.js
Normal 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();
|
||||
}
|
||||
})
|
||||
3
miniprogram/miniprogram/pages/lock/lock.json
Normal file
3
miniprogram/miniprogram/pages/lock/lock.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"usingComponents": {}
|
||||
}
|
||||
39
miniprogram/miniprogram/pages/lock/lock.wxml
Normal file
39
miniprogram/miniprogram/pages/lock/lock.wxml
Normal 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>
|
||||
87
miniprogram/miniprogram/pages/lock/lock.wxss
Normal file
87
miniprogram/miniprogram/pages/lock/lock.wxss
Normal 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;
|
||||
}
|
||||
405
miniprogram/miniprogram/pages/lockDetail/lockDetail.js
Normal file
405
miniprogram/miniprogram/pages/lockDetail/lockDetail.js
Normal 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'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
3
miniprogram/miniprogram/pages/lockDetail/lockDetail.json
Normal file
3
miniprogram/miniprogram/pages/lockDetail/lockDetail.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"usingComponents": {}
|
||||
}
|
||||
135
miniprogram/miniprogram/pages/lockDetail/lockDetail.wxml
Normal file
135
miniprogram/miniprogram/pages/lockDetail/lockDetail.wxml
Normal 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>
|
||||
280
miniprogram/miniprogram/pages/lockDetail/lockDetail.wxss
Normal file
280
miniprogram/miniprogram/pages/lockDetail/lockDetail.wxss
Normal 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
8
package-lock.json
generated
@ -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",
|
||||
|
||||
@ -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"
|
||||
},
|
||||
|
||||
10
src/index.js
10
src/index.js
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user