fix:增加测试代码、更新npm包版本

This commit is contained in:
liyi 2025-04-15 15:47:33 +08:00
parent 150a002560
commit 2911b32664
20 changed files with 3517 additions and 274 deletions

View File

@ -1,5 +1,66 @@
// app.js
const {
init,
Result,
register
} = requirePlugin('starCloud')
App({
onLaunch() {
globalData: {
accountInfo: null,
deviceList: [],
lock: null // 添加lock属性
},
onLaunch: function() {
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]
})
try {
// 请求蓝牙权限
await wx.authorize({
scope: 'scope.bluetooth'
})
} catch (error) {
wx.showModal({
title: '提示',
content: '获取蓝牙权限失败',
showCancel: false
})
}
}
} catch (error) {
wx.showModal({
title: '提示',
content: '蓝牙初始化失败,请检查蓝牙权限',
showCancel: false
})
}
}
})

View File

@ -1,7 +1,16 @@
{
"pages": [
"pages/index/index"
"pages/index/index",
"pages/password/password",
"pages/card/card",
"pages/fingerprint/fingerprint"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "门锁管理",
"navigationBarTextStyle": "black"
},
"plugins": {
"starCloud": {
"version": "dev",

View File

@ -0,0 +1,685 @@
const {
registerExtendedProducts,
starEventOn,
getIcCardList,
selectLock
} = requirePlugin('starCloud')
Page({
data: {
cardId: -1,
cardNumber: 0,
cardName: '',
cardType: 1,
isAdmin: 1,
isForce: 0,
isRound: 0,
weekDayInput: '', // 新增输入框的值
weekDay: [], // 修改为空数组,不再使用 0/1 数组
weekdayPattern: 'workday', // 添加周期模式选择
weekdayPatterns: ['工作日循环(星期一到星期五)', '周末循环(星期六日)', '每天循环'],
weekdayPatternIndex: 0,
startDate: 0,
endDate: 0,
startDateDisplay: '', // 添加用于显示的日期字符串
endDateDisplay: '', // 添加用于显示的日期字符串
startTime: '00:00',
endTime: '00:00',
cardList: [],
currentParams: null,
editMode: false,
selectedCard: null,
dateTimeArray: [], // 日期时间选择器的数据
startDateTimeArray: [0, 0, 0, 0, 0], // 开始时间选择器的当前值
endDateTimeArray: [0, 0, 0, 0, 0] // 结束时间选择器的当前值
},
observers: {
'weekDayInput': function(val) {
if (!val) {
this.setData({
weekDay: [],
isRound: 0
});
return;
}
const weekDay = val.split(',')
.map(item => item.trim())
.filter(item => /^[0-6]$/.test(item))
.filter((item, index, self) => self.indexOf(item) === index)
.sort((a, b) => Number(a) - Number(b));
console.log('Observer - Input:', val);
console.log('Observer - Processed weekDay:', weekDay);
this.setData({
weekDay,
isRound: weekDay.length > 0 ? 1 : 0
});
}
},
onLoad() {
starEventOn('registerCardConfirm', async (data) => {
if (data.code === 0) {
this.setData({
cardId: data.data.cardId,
cardNumber: data.data.cardNumber
})
console.log('收到卡片确认事件', data, this.data.cardId, this.data.cardNumber);
// 添加成功后自动刷新列表
await this.findAllCard();
}
});
// 初始化时间
const now = new Date();
const nextHour = new Date(now.getTime() + 60 * 60 * 1000);
const nowArr = [
now.getFullYear(),
now.getMonth(),
now.getDate(),
now.getHours(),
now.getMinutes()
];
const nextArr = [
nextHour.getFullYear(),
nextHour.getMonth(),
nextHour.getDate(),
nextHour.getHours(),
nextHour.getMinutes()
];
this.setData({
startTime: now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0'),
endTime: nextHour.getHours().toString().padStart(2, '0') + ':' + nextHour.getMinutes().toString().padStart(2, '0'),
startDateTimeArray: nowArr,
endDateTimeArray: nextArr,
startDate: now.getTime(),
endDate: nextHour.getTime(),
startDateDisplay: this.formatDateTime(now),
endDateDisplay: this.formatDateTime(nextHour)
});
// 页面加载时获取卡片列表
this.findAllCard();
// 初始化日期时间选择器数据
this.initDateTimeArray();
},
// 修改格式化日期时间的方法,增加更多格式化选项
formatDateTime(date, format = 'display') {
if (!date || !(date instanceof Date)) {
return '';
}
if (format === 'display') {
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
} else if (format === 'params') {
return `${date.getFullYear()}${(date.getMonth() + 1).toString().padStart(2, '0')}${date.getDate().toString().padStart(2, '0')}${date.getHours().toString().padStart(2, '0')}${date.getMinutes().toString().padStart(2, '0')}`;
}
},
// 初始化日期时间选择器数据
initDateTimeArray() {
const date = new Date();
const years = [];
const months = [];
const days = [];
const hours = [];
const minutes = [];
// 生成年份选项当前年份往后5年
for(let i = date.getFullYear(); i <= date.getFullYear() + 5; i++) {
years.push(i + '年');
}
// 生成月份选项 - 显示1-12月
for(let i = 1; i <= 12; i++) {
months.push(i.toString().padStart(2, '0') + '月');
}
// 生成天数选项默认31天
for(let i = 1; i <= 31; i++) {
days.push(i.toString().padStart(2, '0') + '日');
}
// 生成小时选项
for(let i = 0; i < 24; i++) {
hours.push(i.toString().padStart(2, '0') + '时');
}
// 生成分钟选项
for(let i = 0; i < 60; i++) {
minutes.push(i.toString().padStart(2, '0') + '分');
}
this.setData({
dateTimeArray: [years, months, days, hours, minutes]
});
},
onStartDateTimeChange(e) {
const val = e.detail.value;
const dateTimeArray = this.data.dateTimeArray;
const yearStr = dateTimeArray[0][val[0]];
const year = parseInt(yearStr);
const month = parseInt(dateTimeArray[1][val[1]]) - 1; // 转换为0-11的月份
const day = parseInt(dateTimeArray[2][val[2]]);
const hour = parseInt(dateTimeArray[3][val[3]]);
const minute = parseInt(dateTimeArray[4][val[4]]);
const date = new Date();
date.setFullYear(year);
date.setMonth(month); // 使用0-11的月份
date.setDate(day);
date.setHours(hour);
date.setMinutes(minute);
date.setSeconds(0);
date.setMilliseconds(0);
const timestamp = date.getTime();
this.setData({
startDateTimeArray: val,
startDate: timestamp,
startDateDisplay: this.formatDateTime(date)
});
},
onEndDateTimeChange(e) {
const val = e.detail.value;
const dateTimeArray = this.data.dateTimeArray;
const yearStr = dateTimeArray[0][val[0]];
const year = parseInt(yearStr);
const month = parseInt(dateTimeArray[1][val[1]]) - 1; // 转换为0-11的月份
const day = parseInt(dateTimeArray[2][val[2]]);
const hour = parseInt(dateTimeArray[3][val[3]]);
const minute = parseInt(dateTimeArray[4][val[4]]);
const date = new Date();
date.setFullYear(year);
date.setMonth(month); // 使用0-11的月份
date.setDate(day);
date.setHours(hour);
date.setMinutes(minute);
date.setSeconds(59);
date.setMilliseconds(999);
const timestamp = date.getTime();
this.setData({
endDateTimeArray: val,
endDate: timestamp,
endDateDisplay: this.formatDateTime(date)
});
},
// 处理日期时间选择器列变化
onStartDateTimeColumnChange(e) {
// 处理月份天数联动
if (e.detail.column === 1) {
this.updateDays('startDateTimeArray', e.detail.value);
}
},
onEndDateTimeColumnChange(e) {
if (e.detail.column === 1) {
this.updateDays('endDateTimeArray', e.detail.value);
}
},
// 更新天数
updateDays(arrayName, monthIndex) {
const year = this.data.dateTimeArray[0][this.data[arrayName][0]];
const month = monthIndex + 1;
const days = new Date(parseInt(year), month, 0).getDate();
const newDays = [];
for(let i = 1; i <= days; i++) {
newDays.push(i.toString().padStart(2, '0') + '日');
}
const dateTimeArray = this.data.dateTimeArray;
dateTimeArray[2] = newDays;
this.setData({
dateTimeArray: dateTimeArray
});
},
async icCardOperate(e) {
const operate = e.currentTarget.dataset.operate;
const cardData = e.currentTarget.dataset.card;
if (cardData) {
this.setData({
cardId: cardData.cardId,
cardNumber: cardData.cardNumber,
cardName: cardData.cardName,
cardType: cardData.cardType,
isAdmin: cardData.isAdmin,
isForce: cardData.isForce,
isRound: cardData.isRound,
weekDay: cardData.weekDay || [],
startDate: cardData.startDate || 0,
endDate: cardData.endDate || 0,
startDateDisplay: cardData.startDate ? new Date(cardData.startDate).toISOString().split('T')[0] : '',
endDateDisplay: cardData.endDate ? new Date(cardData.endDate).toISOString().split('T')[0] : '',
startTime: cardData.startTime || '00:00',
endTime: cardData.endTime || '00:00'
});
}
await this._icCardOperate(Number(operate));
},
async _icCardOperate(operate) {
// 如果是修改操作,需要验证 cardNumber
if (operate === 1) {
if (!this.data.cardNumber || this.data.cardNumber === '0' || this.data.cardNumber === 0) {
wx.showToast({
title: '卡号无效',
icon: 'error',
duration: 2000
});
return;
}
}
// 新增:验证循环卡的周期设置
if (this.data.cardType === 4) {
if (!this.data.weekDay || !this.data.weekDay.length) {
wx.showToast({
title: '请设置循环周期',
icon: 'none',
duration: 2000
});
return;
}
}
wx.showLoading({
title: '处理中...',
mask: true
});
try {
const app = getApp();
const { accountInfo } = app.globalData;
const { lock } = app.globalData;
await selectLock({
accountInfo: accountInfo,
lockId: lock.lockId
});
let finalStartDate = this.data.startDate;
let finalEndDate = this.data.endDate;
// 处理循环卡的时间戳
if (this.data.cardType === 4) {
if (!this.data.weekDay || !this.data.weekDay.length) {
wx.showToast({
title: '请输入循环周期',
icon: 'none',
duration: 2000
});
return;
}
// 将时间部分应用到日期时间戳中
if (this.data.startDate && this.data.startTime) {
const [startHours, startMinutes] = this.data.startTime.split(':');
const startDate = new Date(this.data.startDate);
startDate.setHours(parseInt(startHours), parseInt(startMinutes), 0, 0);
finalStartDate = startDate.getTime();
}
if (this.data.endDate && this.data.endTime) {
const [endHours, endMinutes] = this.data.endTime.split(':');
const endDate = new Date(this.data.endDate);
endDate.setHours(parseInt(endHours), parseInt(endMinutes), 59, 999);
finalEndDate = endDate.getTime();
}
}
console.log('发送请求前的 weekDay:', this.data.weekDay);
// 转换 weekDay 为数字数组
const weekDayNumbers = this.data.weekDay.map(Number);
console.log('转换后的 weekDayNumbers:', weekDayNumbers);
const parms = {
type: 'card',
keyId: String(lock.keyId),
uid: String(accountInfo.uid),
userCountLimit: 0xFFFF,
operate: operate,
isAdmin: this.data.isAdmin,
isForce: this.data.isForce,
isRound: this.data.isRound,
weekDay: weekDayNumbers,
cardId: this.data.cardId,
cardNumber: Number(this.data.cardNumber), // 确保 cardNumber 是数字类型
cardName: this.data.cardName,
cardType: this.data.cardType,
cardUserNo: 999,
startDate: finalStartDate,
endDate: finalEndDate,
startTime: this.data.cardType === 4 ? this.data.startTime : undefined,
endTime: this.data.cardType === 4 ? this.data.endTime : undefined
};
// 更新参数显示,添加格式化的时间显示
if (this.data.cardType === 2) {
const startDateTime = new Date(this.data.startDate);
const endDateTime = new Date(this.data.endDate);
parms.displayTimeRange = `${this.formatDateTime(startDateTime, 'params')}-${this.formatDateTime(endDateTime, 'params')}`;
}
this.setData({
currentParams: parms
});
const registerExtendedProductsResult = await registerExtendedProducts(parms)
console.log('registerExtendedProductsResult', registerExtendedProductsResult);
if (registerExtendedProductsResult.code === 0) {
// 操作成功后清空表单
this.setData({
cardName: '',
cardType: 1,
isAdmin: 1,
isForce: 0,
isRound: 0,
weekDayInput: '',
weekDay: [],
startDate: 0,
endDate: 0,
startDateDisplay: '',
endDateDisplay: '',
startTime: '00:00',
endTime: '00:00'
});
// 刷新列表
await this.findAllCard();
wx.showToast({
title: '操作成功',
icon: 'success'
});
} else {
wx.showToast({
title: '操作失败',
icon: 'error'
});
}
} catch (error) {
console.error('操作失败:', error);
wx.showToast({
title: '操作失败',
icon: 'error'
});
} finally {
wx.hideLoading();
}
},
async findAllCard() {
const app = getApp();
const { lock } = app.globalData;
const result = await getIcCardList({lockId: lock.lockId})
console.log('findAllCard', result)
// 处理返回的卡片列表数据
const processedList = (result.data.list || []).map(card => {
// 将周期数字转换为周几文本
const weekDayText = card.weekDay ? card.weekDay.map(day => {
if (day === 7) return '周日';
return `${['一', '二', '三', '四', '五', '六'][day - 1]}`;
}).join(', ') : '';
return {
...card,
isAdmin: card.cardRight === 1 ? 1 : 0,
isForce: card.isCoerced === 1 ? 1 : 0,
isRound: (card.weekDay && card.weekDay.length > 0) ? 1 : 0,
weekDayText // 添加文本形式的周期显示
};
});
this.setData({
cardList: processedList
});
},
onCardTypeChange(e) {
const type = Number(e.detail.value);
this.setData({
cardType: type,
// 当选择永久类型时,重置时间
startTime: type === 1 ? '00:00' : this.data.startTime,
endTime: type === 1 ? '00:00' : this.data.endTime
})
},
onAdminChange(e) {
this.setData({
isAdmin: e.detail.value ? 1 : 0
})
},
onForceChange(e) {
this.setData({
isForce: e.detail.value ? 1 : 0
})
},
onStartDateChange(e) {
const dateStr = e.detail.value;
const timestamp = new Date(dateStr).getTime();
this.setData({
startDate: timestamp,
startDateDisplay: dateStr
});
},
onEndDateChange(e) {
const dateStr = e.detail.value;
const timestamp = new Date(dateStr).getTime();
this.setData({
endDate: timestamp,
endDateDisplay: dateStr
});
},
onStartTimeChange(e) {
this.setData({
startTime: e.detail.value
})
},
onEndTimeChange(e) {
this.setData({
endTime: e.detail.value
})
},
// 修改周期模式切换处理方法
onWeekdayPatternChange(e) {
const index = e.detail.value;
let weekDay = [];
switch (index) {
case '0': // 工作日循环
weekDay = [2, 3, 4, 5, 6]; // 星期一到星期五
break;
case '1': // 周末循环
weekDay = [1, 7]; // 星期日和星期六
break;
case '2': // 每天循环
weekDay = [1, 2, 3, 4, 5, 6, 7]; // 所有天
break;
}
this.setData({
weekdayPatternIndex: index,
weekDay,
isRound: weekDay.length > 0 ? 1 : 0
});
console.log('周期模式变更:', this.data.weekdayPatterns[index], weekDay);
},
onWeekDayChange(e) {
const selectedDays = e.detail.value.map(Number);
// 创建一个长度为7的数组初始值都是0
const weekDay = selectedDays.sort((a, b) => a - b);
this.setData({
weekDay,
isRound: weekDay.length > 0 ? 1 : 0
});
console.log('自定义周期变更:', weekDay);
},
// 修改选择卡片方法中的处理
selectCard(e) {
const cardData = e.currentTarget.dataset.card;
// 处理循环数据
let weekDay = [];
let weekdayPatternIndex = 0;
if (cardData.weekDay && Array.isArray(cardData.weekDay)) {
weekDay = cardData.weekDay;
weekDay.sort((a, b) => a - b); // 确保顺序一致
// 判断是否符合预设模式
const weekDayStr = weekDay.join(',');
if (weekDayStr === '2,3,4,5,6') {
weekdayPatternIndex = 0; // 工作日
} else if (weekDayStr === '1,7') {
weekdayPatternIndex = 1; // 周末
} else if (weekDayStr === '1,2,3,4,5,6,7') {
weekdayPatternIndex = 2; // 每天
}
}
// 处理时间戳转换
const startDateTime = new Date(Number(cardData.startDate) || 0);
const endDateTime = new Date(Number(cardData.endDate) || 0);
// 提取时间部分
const startTime = cardData.startTime ||
`${startDateTime.getHours().toString().padStart(2, '0')}:${startDateTime.getMinutes().toString().padStart(2, '0')}`;
const endTime = cardData.endTime ||
`${endDateTime.getHours().toString().padStart(2, '0')}:${endDateTime.getMinutes().toString().padStart(2, '0')}`;
// 格式化日期显示
const startDateDisplay = this.formatDateTime(startDateTime).split(' ')[0];
const endDateDisplay = this.formatDateTime(endDateTime).split(' ')[0];
this.setData({
cardId: cardData.cardId,
cardNumber: this.data.cardNumber || cardData.cardNumber || 0,
cardName: cardData.cardName,
cardType: cardData.cardType,
isAdmin: cardData.isAdmin || (cardData.cardRight === 1 ? 1 : 0),
isForce: cardData.isForce || (cardData.isCoerced === 1 ? 1 : 0),
isRound: weekDay.length > 0 ? 1 : 0,
weekDay,
weekdayPatternIndex,
startTime,
endTime,
startDateDisplay,
endDateDisplay,
startDate: cardData.startDate || 0,
endDate: cardData.endDate || 0,
editMode: true,
selectedCard: cardData
}, () => {
// 强制触发一次视图更新
this.setData({ _forceUpdate: Date.now() });
});
},
// 添加辅助方法,计算年份索引
getYearIndex(year) {
const currentYear = new Date().getFullYear();
return year - currentYear;
},
// 修改确认编辑方法,直接使用时间戳
async confirmEdit() {
// 验证 cardNumber
if (!this.data.cardNumber || this.data.cardNumber === '0' || this.data.cardNumber === 0) {
wx.showToast({
title: '卡号无效',
icon: 'error',
duration: 2000
});
return;
}
// 直接使用当前的时间戳,不再重新构造
const formData = {
cardId: this.data.cardId,
cardNumber: this.data.cardNumber,
cardName: this.data.cardName,
cardType: this.data.cardType,
isAdmin: this.data.isAdmin,
isForce: this.data.isForce,
isRound: this.data.isRound,
weekDay: this.data.weekDay,
startDate: this.data.startDate,
endDate: this.data.endDate
};
// 更新当前数据
this.setData(formData);
// 执行修改操作
await this._icCardOperate(1);
// 修改完成后,退出编辑模式
if (this.data.editMode) {
this.cancelEdit();
}
},
// 修改取消编辑方法
cancelEdit() {
this.setData({
editMode: false,
selectedCard: null,
cardId: -1,
cardNumber: 0,
cardName: '',
cardType: 1,
isAdmin: 1,
isForce: 0,
isRound: 0,
weekDayInput: '',
weekdayPatternIndex: 0,
weekDay: [], // 默认为空数组
startDate: 0,
endDate: 0,
startDateDisplay: '',
endDateDisplay: '',
startTime: '00:00',
endTime: '00:00'
});
},
// 删除单个卡片
async deleteCard(e) {
const cardData = e.currentTarget.dataset.card;
this.setData({
cardId: cardData.cardId,
cardNumber: cardData.cardNumber
});
await this._icCardOperate(2); // 2 表示删除操作
}
})

View File

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

View File

@ -0,0 +1,171 @@
<view class="card-page">
<view class="card-form">
<view class="form-item">
<text>卡片名称:</text>
<input type="text" model:value="{{cardName}}" placeholder="请输入卡片名称"/>
</view>
<view class="form-item">
<text>卡片类型:</text>
<radio-group bindchange="onCardTypeChange">
<radio value="1" checked="{{cardType === 1}}">永久</radio>
<radio value="2" checked="{{cardType === 2}}">期限</radio>
<radio value="4" checked="{{cardType === 4}}">循环</radio>
</radio-group>
</view>
<view class="form-item">
<text>管理员:</text>
<switch checked="{{isAdmin === 1}}" bindchange="onAdminChange"/>
</view>
<view class="form-item">
<text>胁迫模式:</text>
<switch checked="{{isForce === 1}}" bindchange="onForceChange"/>
</view>
<block wx:if="{{cardType === 2}}">
<view class="form-item">
<text>期限时间:</text>
<picker mode="multiSelector" value="{{startDateTimeArray}}" bindchange="onStartDateTimeChange" bindcolumnchange="onStartDateTimeColumnChange" range="{{dateTimeArray}}">
<view>{{startDateDisplay || '请选择开始时间'}}</view>
</picker>
</view>
<view class="form-item">
<text> </text>
<picker mode="multiSelector" value="{{endDateTimeArray}}" bindchange="onEndDateTimeChange" bindcolumnchange="onEndDateTimeColumnChange" range="{{dateTimeArray}}">
<view>{{endDateDisplay || '请选择结束时间'}}</view>
</picker>
</view>
</block>
<block wx:if="{{cardType === 4}}">
<view class="form-item">
<text>循环周期:</text>
<picker bindchange="onWeekdayPatternChange" range="{{weekdayPatterns}}" value="{{weekdayPatternIndex}}">
<view class="picker-view">{{weekdayPatterns[weekdayPatternIndex]}}</view>
</picker>
</view>
<view class="form-item" wx:if="{{weekdayPattern === 'custom'}}">
<text>自定义:</text>
<checkbox-group bindchange="onWeekDayChange" class="weekday-group">
<label wx:for="{{['周一', '周二', '周三', '周四', '周五', '周六', '周日']}}" wx:key="index" class="weekday-item">
<checkbox value="{{index + 1}}" checked="{{weekDay[index + 1] === 1}}">{{item}}</checkbox>
</label>
</checkbox-group>
</view>
<view class="form-item">
<text>生效日期:</text>
<picker mode="date" value="{{startDateDisplay}}" bindchange="onStartDateChange">
<view>{{startDateDisplay || '请选择开始日期'}}</view>
</picker>
</view>
<view class="form-item">
<text>失效日期:</text>
<picker mode="date" value="{{endDateDisplay}}" bindchange="onEndDateChange">
<view>{{endDateDisplay || '请选择结束日期'}}</view>
</picker>
</view>
<view class="form-item">
<text>生效时间:</text>
<picker mode="time" value="{{startTime}}" bindchange="onStartTimeChange">
<view>{{startTime || '请选择'}}</view>
</picker>
</view>
<view class="form-item">
<text>失效时间:</text>
<picker mode="time" value="{{endTime}}" bindchange="onEndTimeChange">
<view>{{endTime || '请选择'}}</view>
</picker>
</view>
</block>
</view>
<view class="params-display" wx:if="{{currentParams}}">
<view class="params-title">当前操作参数</view>
<view class="params-content">
<view class="param-item">
<text class="param-label">卡片类型:</text>
<text>{{currentParams.type}}</text>
</view>
<view class="param-item">
<text class="param-label">操作类型:</text>
<text>{{currentParams.operate}}</text>
</view>
<view class="param-item">
<text class="param-label">管理员权限:</text>
<text>{{currentParams.isAdmin}}</text>
</view>
<view class="param-item">
<text class="param-label">胁迫模式:</text>
<text>{{currentParams.isForce}}</text>
</view>
<view class="param-item">
<text class="param-label">循环模式:</text>
<text>{{currentParams.isRound}}</text>
</view>
<block wx:if="{{currentParams.weekDay && currentParams.weekDay.length}}">
<view class="param-item">
<text class="param-label">循环周期:</text>
<text>{{currentParams.weekDay}}</text>
</view>
</block>
<block wx:if="{{cardType === 2 && currentParams.displayTimeRange}}">
<view class="param-item">
<text class="param-label">期限时间:</text>
<text>{{currentParams.displayTimeRange}}</text>
</view>
</block>
<block wx:if="{{currentParams.startDate && cardType !== 1}}">
<view class="param-item">
<text class="param-label">有效期:</text>
<text>{{currentParams.startDate}} 至 {{currentParams.endDate}}</text>
</view>
</block>
<block wx:if="{{cardType !== 1}}">
<view class="param-item">
<text class="param-label">生效时间:</text>
<text>{{currentParams.startTime}} - {{currentParams.endTime}}</text>
</view>
</block>
</view>
</view>
<view class="button-group">
<block wx:if="{{editMode}}">
<button type="primary" bind:tap="confirmEdit">确认修改</button>
<button bind:tap="cancelEdit">取消</button>
</block>
<block wx:else>
<button type="primary" bind:tap="icCardOperate" data-operate="0">添加卡片</button>
<button bind:tap="icCardOperate" data-operate="3">删除全部卡片</button>
</block>
</view>
<view class="card-list">
<view class="list-header">
<text>卡片列表</text>
<button bind:tap="findAllCard" size="mini">刷新</button>
</view>
<view class="list-content">
<block wx:for="{{cardList}}" wx:key="cardId">
<view class="card-item {{selectedCard.cardId === item.cardId ? 'selected' : ''}}"
bind:tap="selectCard" data-card="{{item}}">
<view class="card-info">
<text>名称:{{item.cardName}}</text>
<text>类型:{{item.cardType === 1 ? '永久' : item.cardType === 2 ? '期限' : '循环'}}</text>
<text>管理员:{{item.isAdmin === 1 ? '是' : '否'}}</text>
<text>胁迫:{{item.isForce === 1 ? '是' : '否'}}</text>
</view>
<view class="card-time">
<block wx:if="{{item.cardType === 2}}">
<text>{{item.startDate}} - {{item.endDate}}</text>
</block>
<block wx:if="{{item.cardType === 4}}">
<text>周期:{{item.weekDayText}}</text>
</block>
<text>{{item.startTime}} - {{item.endTime}}</text>
</view>
<view class="card-actions">
<button size="mini" type="warn" bind:tap="deleteCard" data-card="{{item}}">删除</button>
</view>
</view>
</block>
</view>
</view>
</view>

View File

@ -0,0 +1,208 @@
.card-page {
padding: 20rpx;
background-color: #f5f5f5;
}
.card-form {
background-color: #fff;
border-radius: 12rpx;
padding: 20rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.form-item {
margin-bottom: 20rpx;
display: flex;
align-items: center;
}
.form-item text {
width: 180rpx;
color: #333;
font-size: 28rpx;
}
.form-item input {
flex: 1;
height: 70rpx;
border: 1rpx solid #ddd;
border-radius: 6rpx;
padding: 0 20rpx;
font-size: 28rpx;
}
.form-item .tips {
font-size: 24rpx;
color: #999;
margin-left: 20rpx;
width: auto;
}
.form-item radio-group,
.form-item checkbox-group {
flex: 1;
display: flex;
flex-wrap: wrap;
gap: 20rpx;
}
.form-item picker {
flex: 1;
}
.form-item picker view {
height: 70rpx;
line-height: 70rpx;
border: 1rpx solid #ddd;
border-radius: 6rpx;
padding: 0 20rpx;
font-size: 28rpx;
}
.picker-view {
flex: 1;
height: 70rpx;
line-height: 70rpx;
border: 1rpx solid #ddd;
border-radius: 6rpx;
padding: 0 20rpx;
font-size: 28rpx;
background-color: #fff;
}
.form-item.week-tips {
margin-top: -10rpx;
margin-bottom: 30rpx;
color: #666;
}
.form-item.week-tips text:first-child {
width: 180rpx;
color: #666;
font-size: 24rpx;
}
.form-item.week-tips .tips {
flex: 1;
font-size: 24rpx;
color: #999;
width: auto;
line-height: 1.4;
}
.pattern-group {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.pattern-group radio {
margin-right: 20rpx;
}
.weekday-group {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
}
.weekday-item {
display: flex;
align-items: center;
margin-right: 20rpx;
}
.weekday-group {
display: flex;
flex-wrap: wrap;
gap: 10rpx;
}
.weekday-item {
margin-right: 20rpx;
display: inline-flex;
align-items: center;
}
.weekday-item checkbox {
margin-right: 5rpx;
}
.button-group {
display: flex;
gap: 20rpx;
padding: 20rpx;
}
.button-group button {
flex: 1;
margin: 0;
background-color: #007aff;
color: #fff;
font-size: 28rpx;
border-radius: 8rpx;
}
.button-group button:nth-child(2) {
background-color: #28cd41;
}
.button-group button:nth-child(3) {
background-color: #ff3b30;
}
.button-group button:nth-child(4) {
background-color: #ff9500;
}
.card-list {
background-color: #fff;
border-radius: 12rpx;
padding: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.list-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
padding-bottom: 20rpx;
border-bottom: 1rpx solid #eee;
}
.list-header text {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.card-item {
background-color: #f8f8f8;
border-radius: 8rpx;
padding: 20rpx;
margin-bottom: 20rpx;
}
.card-item.selected {
background-color: rgba(0, 0, 0, 0.05);
border: 1px solid #1aad19;
}
.card-info {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10rpx;
margin-bottom: 10rpx;
}
.card-info text {
font-size: 26rpx;
color: #666;
}
.card-time {
font-size: 24rpx;
color: #999;
}

View File

@ -0,0 +1,628 @@
const {
registerExtendedProducts,
starEventOn,
getFingerprintList,
selectLock
} = requirePlugin('starCloud')
Page({
data: {
fingerprintId: -1,
fingerprintNumber: 0,
fingerprintName: '',
fingerprintType: 1,
isAdmin: 1,
isForce: 0,
isRound: 0,
weekDay: [], // 确保初始化为空数组
startDate: 0,
endDate: 0,
startTime: '00:00',
endTime: '00:00',
fingerprintList: [],
currentParams: null,
dateTimeArray: [], // 日期时间选择器数据
startDateTimeArray: [], // 开始时间数组
endDateTimeArray: [], // 结束时间数组
startDateDisplay: '', // 显示用的开始日期
endDateDisplay: '', // 显示用的结束日期
weekdayPatterns: ['工作日循环(星期一到星期五)', '周末循环(星期六日)', '每天循环'],
weekdayPatternIndex: 0,
selectedFingerprint: null, // 当前选中的指纹
editMode: false // 编辑模式标识
},
onLoad() {
starEventOn('registerFingerprintConfirm', async (data) => {
if (data.code === 0) {
this.setData({
fingerprintId: data.data.fingerprintId,
fingerprintNumber: data.data.fingerprintNumber
})
console.log('收到指纹确认事件', data, this.data.fingerprintId, this.data.fingerprintNumber);
// 添加成功后自动刷新列表
await this.findAllFingerprint();
}
});
// 初始化时间
const now = new Date();
const nextHour = new Date(now.getTime() + 60 * 60 * 1000);
const nowArr = [
now.getFullYear(),
now.getMonth(),
now.getDate(),
now.getHours(),
now.getMinutes()
];
const nextArr = [
nextHour.getFullYear(),
nextHour.getMonth(),
nextHour.getDate(),
nextHour.getHours(),
nextHour.getMinutes()
];
this.setData({
startTime: now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0'),
endTime: nextHour.getHours().toString().padStart(2, '0') + ':' + nextHour.getMinutes().toString().padStart(2, '0'),
startDateTimeArray: nowArr,
endDateTimeArray: nextArr,
startDate: now.getTime(),
endDate: nextHour.getTime(),
startDateDisplay: this.formatDateTime(now),
endDateDisplay: this.formatDateTime(nextHour)
});
starEventOn('registerFingerprintProcess', async (data) => {
console.log('收到指纹过程事件', data);
});
this.initDateTimeArray();
},
async fingerprintOperate(e) {
const operate = e.currentTarget.dataset.operate;
const fingerprintData = e.currentTarget.dataset.fingerprint;
if (operate === 'edit') {
// 点击修改按钮,将数据同步到表单
if (fingerprintData) {
const weekDay = Array.isArray(fingerprintData.weekDay) ? fingerprintData.weekDay : [];
// 判断周期模式
let weekdayPatternIndex = 0;
if (weekDay.length > 0) {
const weekDayStr = weekDay.sort().join(',');
if (weekDayStr === '1,2,3,4,5') {
weekdayPatternIndex = 0; // 工作日
} else if (weekDayStr === '0,6') {
weekdayPatternIndex = 1; // 周末
} else if (weekDayStr === '0,1,2,3,4,5,6') {
weekdayPatternIndex = 2; // 每天
}
}
// 处理日期和时间显示
const startDate = fingerprintData.startDate ? new Date(fingerprintData.startDate) : null;
const endDate = fingerprintData.endDate ? new Date(fingerprintData.endDate) : null;
this.setData({
editMode: true, // 使用 editMode 替代 isEditing
fingerprintId: fingerprintData.fingerprintId,
fingerprintNumber: fingerprintData.fingerprintNumber,
fingerprintName: fingerprintData.fingerprintName,
fingerprintType: fingerprintData.fingerprintType,
isAdmin: fingerprintData.fingerRight,
isForce: fingerprintData.isCoerced,
isRound: weekDay.length > 0 ? 1 : 0,
weekDay: weekDay,
weekdayPatternIndex: weekdayPatternIndex,
startDate: fingerprintData.startDate || 0,
endDate: fingerprintData.endDate || 0,
startDateDisplay: startDate ? this.formatDateTime(startDate) : '',
endDateDisplay: endDate ? this.formatDateTime(endDate) : '',
startTime: fingerprintData.startTime || '00:00',
endTime: fingerprintData.endTime || '00:00'
});
}
return;
}
// 执行实际的操作
await this._fingerprintOperate(Number(operate));
},
async _fingerprintOperate(operate) {
// 修改验证逻辑,移除单次类型相关的判断
if (this.data.fingerprintType === 2) {
if (!this.data.startDateDisplay || !this.data.endDateDisplay) {
wx.showToast({
title: '请选择期限时间',
icon: 'none'
});
return;
}
// 添加时间范围验证
if (this.data.startDate >= this.data.endDate) {
wx.showToast({
title: '结束时间必须大于开始时间',
icon: 'none'
});
return;
}
}
if (this.data.fingerprintType === 4) {
if (!this.data.weekDay || !this.data.weekDay.length) {
wx.showToast({
title: '请选择循环周期',
icon: 'none'
});
return;
}
if (!this.data.startDate || !this.data.endDate) {
wx.showToast({
title: '请选择日期范围',
icon: 'none'
});
return;
}
if (!this.data.startTime || !this.data.endTime) {
wx.showToast({
title: '请选择时间范围',
icon: 'none'
});
return;
}
}
wx.showLoading({
title: '处理中...',
mask: true
});
try {
const app = getApp();
const { accountInfo } = app.globalData;
const { lock } = app.globalData;
await selectLock({
accountInfo: accountInfo,
lockId: lock.lockId
});
let finalStartDate = this.data.startDate;
let finalEndDate = this.data.endDate;
// 处理循环指纹的时间戳
if (this.data.fingerprintType === 4) {
if (this.data.startDate && this.data.startTime) {
const [startHours, startMinutes] = this.data.startTime.split(':');
const startDate = new Date(this.data.startDate);
startDate.setHours(parseInt(startHours), parseInt(startMinutes), 0, 0);
finalStartDate = startDate.getTime();
}
if (this.data.endDate && this.data.endTime) {
const [endHours, endMinutes] = this.data.endTime.split(':');
const endDate = new Date(this.data.endDate);
endDate.setHours(parseInt(endHours), parseInt(endMinutes), 59, 999);
finalEndDate = endDate.getTime();
}
}
// 根据不同类型构造不同的参数
const baseParams = {
type: 'fingerprint',
keyId: String(lock.keyId),
uid: String(accountInfo.uid),
userCountLimit: 0xFFFF,
operate: operate,
isAdmin: this.data.isAdmin,
isForce: this.data.isForce,
fingerprintId: this.data.fingerprintId,
fingerprintNumber: this.data.fingerprintNumber,
fingerprintName: this.data.fingerprintName,
fingerprintType: this.data.fingerprintType,
fingerprintUserNo: 999
};
let timeParams = {};
if (this.data.fingerprintType === 1) {
// 永久类型不需要时间参数
timeParams = {
startDate: 0,
endDate: 0,
isRound: 0,
weekDay: []
};
} else if (this.data.fingerprintType === 2) {
// 期限类型
timeParams = {
startDate: this.data.startDate,
endDate: this.data.endDate,
isRound: 0,
weekDay: []
};
// 添加显示用的时间范围
const startDateTime = new Date(this.data.startDate);
const endDateTime = new Date(this.data.endDate);
timeParams.displayTimeRange = `${this.formatDateTime(startDateTime, 'params')}-${this.formatDateTime(endDateTime, 'params')}`;
} else if (this.data.fingerprintType === 4) {
// 循环类型
timeParams = {
startDate: finalStartDate,
endDate: finalEndDate,
startTime: this.data.startTime,
endTime: this.data.endTime,
isRound: this.data.isRound,
weekDay: this.data.weekDay
};
}
const parms = {
...baseParams,
...timeParams
};
this.setData({
currentParams: parms
});
const registerExtendedProductsResult = await registerExtendedProducts(parms)
console.log('registerExtendedProductsResult', registerExtendedProductsResult);
if (registerExtendedProductsResult.code === 0) {
// 操作成功后清空表单
this.setData({
editMode: false, // 使用 editMode 替代 isEditing
fingerprintName: '',
fingerprintType: 1,
isAdmin: 1,
isForce: 0,
isRound: 0,
weekDay: [],
startDate: 0,
endDate: 0,
startTime: '00:00',
endTime: '00:00'
});
// 刷新列表
await this.findAllFingerprint();
wx.showToast({
title: '操作成功',
icon: 'success'
});
} else {
wx.showToast({
title: '操作失败',
icon: 'error'
});
}
} catch (error) {
console.error('操作失败:', error);
wx.showToast({
title: '操作失败',
icon: 'error'
});
} finally {
wx.hideLoading();
}
},
async findAllFingerprint() {
const app = getApp();
const { lock } = app.globalData;
const result = await getFingerprintList({lockId: lock.lockId})
console.log('findAllFingerprint', result)
const processedList = (result.data.list || []).map(fingerprint => {
// 转换周期显示,与卡片保持一致
const weekDayText = fingerprint.weekDay ? fingerprint.weekDay.map(day => {
if (day === 7 || day === 0) return '周日';
return `${['一','二','三','四','五','六'][day - 1]}`;
}).join(', ') : '';
// 处理时间显示
const startDate = new Date(Number(fingerprint.startDate));
const endDate = new Date(Number(fingerprint.endDate));
const startDateDisplay = this.formatDateTime(startDate);
const endDateDisplay = this.formatDateTime(endDate);
return {
...fingerprint,
isAdmin: fingerprint.fingerRight === 1 ? 1 : 0,
isForce: fingerprint.isCoerced === 1 ? 1 : 0,
isRound: (fingerprint.weekDay && fingerprint.weekDay.length > 0) ? 1 : 0,
weekDayText,
startDateDisplay,
endDateDisplay
};
});
this.setData({
fingerprintList: processedList
});
},
onFingerprintTypeChange(e) {
this.setData({
fingerprintType: Number(e.detail.value)
})
},
onAdminChange(e) {
this.setData({
isAdmin: e.detail.value ? 1 : 0
})
},
onForceChange(e) {
this.setData({
isForce: e.detail.value ? 1 : 0
})
},
onStartDateChange(e) {
const dateStr = e.detail.value; // 格式为 "YYYY-MM-DD"
const date = new Date(dateStr);
this.setData({
startDate: date.getTime(),
startDateDisplay: dateStr // 直接使用选择器返回的日期字符串
});
},
onEndDateChange(e) {
const dateStr = e.detail.value; // 格式为 "YYYY-MM-DD"
const date = new Date(dateStr);
this.setData({
endDate: date.getTime(),
endDateDisplay: dateStr // 直接使用选择器返回的日期字符串
});
},
onWeekDayChange(e) {
const selectedDays = e.detail.value;
this.setData({
weekDay: selectedDays.map(Number),
isRound: selectedDays.length > 0 ? 1 : 0
});
},
onWeekdayPatternChange(e) {
const index = Number(e.detail.value);
let weekDay = [];
switch (index) {
case 0: // 工作日循环
weekDay = [2, 3, 4, 5, 6]; // 修改为与卡片一致的周期值
break;
case 1: // 周末循环
weekDay = [1, 7]; // 修改为与卡片一致的周期值
break;
case 2: // 每天循环
weekDay = [1, 2, 3, 4, 5, 6, 7]; // 修改为与卡片一致的周期值
break;
}
this.setData({
weekdayPatternIndex: index,
weekDay,
isRound: 1
});
},
onStartTimeChange(e) {
this.setData({
startTime: e.detail.value
})
},
onEndTimeChange(e) {
this.setData({
endTime: e.detail.value
})
},
// 初始化日期时间选择器数据
initDateTimeArray() {
const date = new Date();
const years = [];
const months = [];
const days = [];
const hours = [];
const minutes = [];
for (let i = date.getFullYear(); i <= date.getFullYear() + 10; i++) {
years.push(i + '年');
}
for (let i = 1; i <= 12; i++) {
months.push(i + '月');
}
for (let i = 1; i <= 31; i++) {
days.push(i + '日');
}
for (let i = 0; i < 24; i++) {
hours.push(i + '时');
}
for (let i = 0; i < 60; i++) {
minutes.push(i + '分');
}
this.setData({
dateTimeArray: [years, months, days, hours, minutes]
});
},
// 日期时间选择器列变化
onStartDateTimeColumnChange(e) {
const { column, value } = e.detail;
const data = {
dateTimeArray: this.data.dateTimeArray,
startDateTimeArray: this.data.startDateTimeArray
};
data.startDateTimeArray[column] = value;
this.setData(data);
},
onEndDateTimeColumnChange(e) {
const { column, value } = e.detail;
const data = {
dateTimeArray: this.data.dateTimeArray,
endDateTimeArray: this.data.endDateTimeArray
};
data.endDateTimeArray[column] = value;
this.setData(data);
},
// 日期时间确认选择
onStartDateTimeChange(e) {
const val = e.detail.value;
const dateTimeArray = this.data.dateTimeArray;
const year = parseInt(dateTimeArray[0][val[0]]);
const month = parseInt(dateTimeArray[1][val[1]]) - 1;
const day = parseInt(dateTimeArray[2][val[2]]);
const hour = parseInt(dateTimeArray[3][val[3]]);
const minute = parseInt(dateTimeArray[4][val[4]]);
const date = new Date();
date.setFullYear(year);
date.setMonth(month);
date.setDate(day);
date.setHours(hour);
date.setMinutes(minute);
date.setSeconds(0);
date.setMilliseconds(0);
const startDate = date.getTime();
const startDateDisplay = this.formatDateTime(date);
this.setData({
startDate,
startDateDisplay,
startDateTimeArray: val
});
},
onEndDateTimeChange(e) {
const val = e.detail.value;
const dateTimeArray = this.data.dateTimeArray;
const year = parseInt(dateTimeArray[0][val[0]]);
const month = parseInt(dateTimeArray[1][val[1]]) - 1;
const day = parseInt(dateTimeArray[2][val[2]]);
const hour = parseInt(dateTimeArray[3][val[3]]);
const minute = parseInt(dateTimeArray[4][val[4]]);
const date = new Date();
date.setFullYear(year);
date.setMonth(month);
date.setDate(day);
date.setHours(hour);
date.setMinutes(minute);
date.setSeconds(59);
date.setMilliseconds(999);
const endDate = date.getTime();
const endDateDisplay = this.formatDateTime(date);
this.setData({
endDate,
endDateDisplay,
endDateTimeArray: val
});
},
// 取消编辑
cancelEdit() {
this.setData({
editMode: false,
selectedFingerprint: null,
fingerprintName: '',
fingerprintType: 1,
isAdmin: 1,
isForce: 0,
isRound: 0,
weekDay: [],
startDate: 0,
endDate: 0,
startTime: '00:00',
endTime: '00:00'
});
},
// 确认编辑
async confirmEdit() {
await this._fingerprintOperate(1); // 1 表示修改操作
},
formatDateTime(date) {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const hour = date.getHours().toString().padStart(2, '0');
const minute = date.getMinutes().toString().padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}`;
},
// 选择指纹记录
selectFingerprint(e) {
const fingerprintData = e.currentTarget.dataset.fingerprint;
if (fingerprintData) {
let weekDay = [];
let weekdayPatternIndex = 0;
if (fingerprintData.weekDay && Array.isArray(fingerprintData.weekDay)) {
weekDay = fingerprintData.weekDay.map(day => day === 7 ? 7 : day); // 确保周日使用7
weekDay.sort((a, b) => a - b);
const weekDayStr = weekDay.join(',');
if (weekDayStr === '2,3,4,5,6') {
weekdayPatternIndex = 0; // 工作日
} else if (weekDayStr === '1,7') {
weekdayPatternIndex = 1; // 周末
} else if (weekDayStr === '1,2,3,4,5,6,7') {
weekdayPatternIndex = 2; // 每天
}
}
// 处理时间戳和显示
const startDateTime = new Date(Number(fingerprintData.startDate) || 0);
const endDateTime = new Date(Number(fingerprintData.endDate) || 0);
// 日期和时间分开处理
let startDate = startDateTime;
let endDate = endDateTime;
let startTime = '00:00';
let endTime = '00:00';
if (fingerprintData.fingerprintType === 4) {
startTime = `${startDateTime.getHours().toString().padStart(2, '0')}:${startDateTime.getMinutes().toString().padStart(2, '0')}`;
endTime = `${endDateTime.getHours().toString().padStart(2, '0')}:${endDateTime.getMinutes().toString().padStart(2, '0')}`;
// 重置日期部分的时间为0点
startDate.setHours(0, 0, 0, 0);
endDate.setHours(23, 59, 59, 999);
}
this.setData({
fingerprintId: fingerprintData.fingerprintId,
fingerprintNumber: fingerprintData.fingerprintNumber || 0,
fingerprintName: fingerprintData.fingerprintName,
fingerprintType: fingerprintData.fingerprintType,
isAdmin: fingerprintData.isAdmin || (fingerprintData.fingerRight === 1 ? 1 : 0),
isForce: fingerprintData.isForce || (fingerprintData.isCoerced === 1 ? 1 : 0),
isRound: weekDay.length > 0 ? 1 : 0,
weekDay,
weekdayPatternIndex,
startDate: startDate.getTime(),
endDate: endDate.getTime(),
startTime,
endTime,
startDateDisplay: this.formatDateTime(startDateTime),
endDateDisplay: this.formatDateTime(endDateTime),
editMode: true,
selectedFingerprint: fingerprintData
});
}
}
})

View File

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

View File

@ -0,0 +1,163 @@
<view class="fingerprint-page">
<view class="fingerprint-form">
<view class="form-item">
<text>指纹名称:</text>
<input type="text" model:value="{{fingerprintName}}" placeholder="请输入指纹名称"/>
</view>
<view class="form-item">
<text>指纹类型:</text>
<radio-group bindchange="onFingerprintTypeChange">
<radio value="1" checked="{{fingerprintType === 1}}">永久</radio>
<radio value="2" checked="{{fingerprintType === 2}}">期限</radio>
<radio value="4" checked="{{fingerprintType === 4}}">循环</radio>
</radio-group>
</view>
<view class="form-item">
<text>管理员:</text>
<switch checked="{{isAdmin === 1}}" bindchange="onAdminChange"/>
</view>
<view class="form-item">
<text>胁迫模式:</text>
<switch checked="{{isForce === 1}}" bindchange="onForceChange"/>
</view>
<block wx:if="{{fingerprintType === 2}}">
<view class="form-item">
<text>期限时间:</text>
<picker mode="multiSelector" value="{{startDateTimeArray}}" bindchange="onStartDateTimeChange" bindcolumnchange="onStartDateTimeColumnChange" range="{{dateTimeArray}}">
<view>{{startDateDisplay || '请选择开始时间'}}</view>
</picker>
</view>
<view class="form-item">
<text> </text>
<picker mode="multiSelector" value="{{endDateTimeArray}}" bindchange="onEndDateTimeChange" bindcolumnchange="onEndDateTimeColumnChange" range="{{dateTimeArray}}">
<view>{{endDateDisplay || '请选择结束时间'}}</view>
</picker>
</view>
</block>
<block wx:if="{{fingerprintType === 4}}">
<view class="form-item">
<text>循环周期:</text>
<picker bindchange="onWeekdayPatternChange" range="{{weekdayPatterns}}" value="{{weekdayPatternIndex}}">
<view class="picker-view">{{weekdayPatterns[weekdayPatternIndex]}}</view>
</picker>
</view>
<view class="form-item">
<text>生效日期:</text>
<picker mode="date" value="{{startDateDisplay}}" bindchange="onStartDateChange">
<view>{{startDateDisplay || '请选择开始日期'}}</view>
</picker>
</view>
<view class="form-item">
<text>失效日期:</text>
<picker mode="date" value="{{endDateDisplay}}" bindchange="onEndDateChange">
<view>{{endDateDisplay || '请选择结束日期'}}</view>
</picker>
</view>
<view class="form-item">
<text>生效时间:</text>
<picker mode="time" value="{{startTime}}" bindchange="onStartTimeChange">
<view>{{startTime}}</view>
</picker>
</view>
<view class="form-item">
<text>失效时间:</text>
<picker mode="time" value="{{endTime}}" bindchange="onEndTimeChange">
<view>{{endTime}}</view>
</picker>
</view>
</block>
</view>
<view class="params-display" wx:if="{{currentParams}}">
<view class="params-title">当前操作参数</view>
<view class="params-content">
<view class="param-item">
<text class="param-label">指纹类型:</text>
<text>{{currentParams.type}}</text>
</view>
<view class="param-item">
<text class="param-label">操作类型:</text>
<text>{{currentParams.operate}}</text>
</view>
<view class="param-item">
<text class="param-label">管理员权限:</text>
<text>{{currentParams.isAdmin}}</text>
</view>
<view class="param-item">
<text class="param-label">胁迫模式:</text>
<text>{{currentParams.isForce}}</text>
</view>
<view class="param-item">
<text class="param-label">循环模式:</text>
<text>{{currentParams.isRound}}</text>
</view>
<block wx:if="{{currentParams.weekDay && currentParams.weekDay.length}}">
<view class="param-item">
<text class="param-label">循环周期:</text>
<text>{{currentParams.weekDay}}</text>
</view>
</block>
<block wx:if="{{fingerprintType === 2 && currentParams.displayTimeRange}}">
<view class="param-item">
<text class="param-label">期限时间:</text>
<text>{{currentParams.displayTimeRange}}</text>
</view>
</block>
<block wx:if="{{fingerprintType !== 1}}">
<block wx:if="{{currentParams.startDate}}">
<view class="param-item">
<text class="param-label">有效期:</text>
<text>{{currentParams.startDate}} 至 {{currentParams.endDate}}</text>
</view>
</block>
<view class="param-item">
<text class="param-label">生效时间:</text>
<text>{{currentParams.startTime}} - {{currentParams.endTime}}</text>
</view>
</block>
</view>
</view>
<view class="button-group">
<block wx:if="{{!editMode}}">
<button bind:tap="fingerprintOperate" data-operate="0">添加指纹</button>
<button bind:tap="fingerprintOperate" data-operate="3">删除全部指纹</button>
</block>
<block wx:else>
<button bind:tap="fingerprintOperate" data-operate="1">确认修改</button>
<button bind:tap="cancelEdit">取消修改</button>
</block>
</view>
<view class="fingerprint-list">
<view class="list-header">
<text>指纹列表</text>
<button bind:tap="findAllFingerprint" size="mini">刷新</button>
</view>
<view class="list-content">
<block wx:for="{{fingerprintList}}" wx:key="fingerprintId">
<view class="fingerprint-item {{selectedFingerprint.fingerprintId === item.fingerprintId ? 'selected' : ''}}"
bind:tap="selectFingerprint" data-fingerprint="{{item}}">
<view class="fingerprint-info">
<text>名称:{{item.fingerprintName}}</text>
<text>类型:{{item.fingerprintType === 1 ? '永久' : item.fingerprintType === 2 ? '期限' : '循环'}}</text>
<text>管理员:{{item.isAdmin === 1 ? '是' : '否'}}</text>
<text>胁迫:{{item.isForce === 1 ? '是' : '否'}}</text>
</view>
<view class="fingerprint-time">
<block wx:if="{{item.fingerprintType === 2}}">
<text>{{item.startDateDisplay}} - {{item.endDateDisplay}}</text>
</block>
<block wx:if="{{item.fingerprintType === 4}}">
<text>周期:{{item.weekDayText}}</text>
</block>
<text>{{item.startTime}} - {{item.endTime}}</text>
</view>
<view class="fingerprint-actions">
<button size="mini" bind:tap="fingerprintOperate" data-operate="2" data-fingerprint="{{item}}">删除</button>
</view>
</view>
</block>
</view>
</view>
</view>

View File

@ -0,0 +1,156 @@
.fingerprint-page {
padding: 20rpx;
background-color: #f5f5f5;
}
.fingerprint-form {
background-color: #fff;
border-radius: 12rpx;
padding: 20rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.form-item {
margin-bottom: 20rpx;
display: flex;
align-items: center;
}
.form-item text {
width: 180rpx;
color: #333;
font-size: 28rpx;
}
.form-item input {
flex: 1;
height: 70rpx;
border: 1rpx solid #ddd;
border-radius: 6rpx;
padding: 0 20rpx;
font-size: 28rpx;
}
.form-item radio-group,
.form-item checkbox-group {
flex: 1;
display: flex;
flex-wrap: wrap;
gap: 20rpx;
}
.form-item picker {
flex: 1;
}
.form-item picker view {
height: 70rpx;
line-height: 70rpx;
border: 1rpx solid #ddd;
border-radius: 6rpx;
padding: 0 20rpx;
font-size: 28rpx;
}
.picker-view {
flex: 1;
height: 70rpx;
line-height: 70rpx;
border: 1rpx solid #ddd;
border-radius: 6rpx;
padding: 0 20rpx;
font-size: 28rpx;
background-color: #fff;
}
.weekday-group {
display: flex;
flex-wrap: wrap;
gap: 10rpx;
}
.weekday-item {
flex: 0 0 auto;
margin-right: 20rpx;
display: inline-flex;
align-items: center;
}
.button-group {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
margin: 30rpx 0;
}
.button-group button {
margin: 0;
background-color: #007aff;
color: #fff;
font-size: 28rpx;
border-radius: 8rpx;
}
.button-group button:nth-child(2) {
background-color: #28cd41;
}
.button-group button:nth-child(3) {
background-color: #ff3b30;
}
.button-group button:nth-child(4) {
background-color: #ff9500;
}
.fingerprint-list {
background-color: #fff;
border-radius: 12rpx;
padding: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.list-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
padding-bottom: 20rpx;
border-bottom: 1rpx solid #eee;
}
.list-header text {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.fingerprint-item {
background-color: #f8f8f8;
border-radius: 8rpx;
padding: 20rpx;
margin-bottom: 20rpx;
}
.fingerprint-item.selected {
background-color: rgba(0, 0, 0, 0.05);
border: 1px solid #1aad19;
}
.fingerprint-info {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10rpx;
margin-bottom: 10rpx;
}
.fingerprint-info text {
font-size: 26rpx;
color: #666;
}
.fingerprint-time {
font-size: 24rpx;
color: #999;
}

View File

@ -9,68 +9,114 @@ const {
stopSearchDevice,
selectLock,
getLockSupportFeatures,
getOfflinePassword,
registerExtendedProducts,
starEventOn,
starEventEmit,
getFingerprintList,
getIcCardList,
customPassword,
updateAdminPassword,
updateSupportFunctionsWithParams,
remoteUnLock,
readSupportFunctions,
} = requirePlugin('starCloud')
Page({
data: {
list: [],
accountInfo: {},
lock: null,
cardId: -1,
cardNumber: 0,
fingerprintId: -1,
fingerprintNumber: 0,
eventChannel: null,
pwdId: -1,
pwdNo: 0,
isSearching: false,
loading: false, // 添加loading状态
showPassageMode: false,
passageModeForm: {
passageMode: '1',
startDate: '',
endDate: '',
isAllDay: '0',
cycleType: 'workday', // 添加循环类型workday/weekend/everyday
autoUnlock: '1'
},
async onLoad() {
init({
clientId: 'Tmj4XoB9dkXjD7t7OHKHl564rJdmvPm9',
clientSecret: 'g1VvSbN0Ya3IqPkA8j9Xn54PE1L8zGiy',
env: 'SKY',
isReportLog: true,
accounts: []
},
startSearching() {
if (this.data.isSearching) {
wx.showToast({
title: '正在搜索中',
icon: 'none'
})
const result = await register()
return
}
this.setData({ isSearching: true })
searchDevice((result) => {
if (result.code === Result.Success.code) {
this.setData({
accountInfo: result.data
list: result.data.list
})
init({
clientId: 'Tmj4XoB9dkXjD7t7OHKHl564rJdmvPm9',
clientSecret: 'g1VvSbN0Ya3IqPkA8j9Xn54PE1L8zGiy',
env: 'SKY',
isReportLog: true,
accounts: [this.data.accountInfo]
getApp().globalData.deviceList = result.data.list
}
})
wx.showToast({
title: '开始搜索设备',
icon: 'none'
})
console.log('初始化成功:', this.data.accountInfo)
}
starEventOn('registerFingerprintConfirm', async (data) => {
if (data.code === 0) {
this.data.fingerprintId = data.data.fingerprintId
this.data.fingerprintNumber = data.data.fingerprintNumber
console.log('收到指纹确认事件', data, this.fingerprintId, this.fingerprintNumber);
}
});
starEventOn('registerCardConfirm', async (data) => {
if (data.code === 0) {
this.data.cardId = data.data.cardId
this.data.cardNumber = data.data.cardNumber
console.log('收到卡片确认事件', data, this.cardId, this.cardNumber);
}
});
starEventOn('registerFingerprintProcess', async (data) => {
console.log('收到指纹过程事件', data);
});
},
stopSearching() {
if (!this.data.isSearching) return
stopSearchDevice()
this.setData({ isSearching: false })
wx.showToast({
title: '已停止搜索',
icon: 'none'
})
},
onHide() {
this.stopSearching()
},
onUnload() {
this.stopSearching()
},
onLoad() {
// 从app.globalData获取数据
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);
},
async requestBluetoothPermission() {
try {
const res = await wx.authorize({
scope: 'scope.bluetooth'
});
this.startSearching();
} catch (error) {
wx.showModal({
title: '提示',
content: '请授权蓝牙权限以使用设备功能',
success: (res) => {
if (res.confirm) {
wx.openSetting();
}
}
});
}
},
// 搜索设备
searchDevice() {
searchDevice(this.searchDeviceCallback)
@ -83,7 +129,21 @@ Page({
})
}
},
// 绑定设备
// 添加通用的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({
@ -91,26 +151,38 @@ Page({
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) {
stopSearchDevice()
// 合并设备信息,确保显示完整信息
const lockInfo = {
...device, // 包含搜索时的设备信息name, mac, uid等
...result.data.lock // 包含绑定后的信息lockId, keyId等
}
this.setData({
lock: result.data.lock
lock: lockInfo,
list: [] // 清空搜索列表
})
// 更新app.globalData中的lock
getApp().globalData.lock = lockInfo
this.stopSearching()
wx.showToast({
title: '绑定成功',
icon: 'none',
icon: 'success',
})
}
}, '绑定中...')
}
}
})
},
// 开门
// 修改开门方法
async openDoor() {
await this.handleApiCall(async () => {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
@ -120,16 +192,23 @@ Page({
disconnect: true,
type: 'open'
})
console.log('开门结果', result)
if (result.code === Result.Success.code) {
wx.showToast({
title: '开门成功',
icon: 'none',
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
@ -137,17 +216,22 @@ Page({
const result = await deleteLock({
accountInfo: this.data.accountInfo,
})
console.log('删除结果', result)
if (result.code === Result.Success.code) {
this.setData({
lock: null
lock: null,
list: []
})
wx.showToast({
title: '删除成功',
icon: 'none',
icon: 'success',
})
}
}, '删除中...')
}
}
})
},
// 获取锁支持项
async getLockSupportFeatures() {
const result = await getLockSupportFeatures({
@ -156,152 +240,284 @@ Page({
})
console.log('锁支持项', result)
},
// 获取锁离线密码
async getOfflinePassword() {
const result = await getOfflinePassword({
accountInfo: this.data.accountInfo,
password: {
lockId: this.data.lock.lockId,
keyboardPwdName: `单次密码${new Date().getTime()}`,
keyboardPwdType: 1,
isCoerced: 2,
startDate: 0,
endDate: 0,
hoursStart: 0,
hoursEnd: 0,
}
})
console.log('锁离线密码', result)
},
// ...其他代码
icCardOperate(e) {
const operate = e.currentTarget.dataset.operate;
this._icCardOperate(Number(operate)); // 调用实际的逻辑函数
},
async _icCardOperate(operate) {
const parms = {
type: 'card',
keyId: String(this.data.lock.keyId),
uid: String(this.data.accountInfo.uid),
userCountLimit: 0xFFFF,
operate: operate,
isAdmin: 0,
isForce: 0,
isRound: 0,
weekDays: [1, 2, 3, 4, 5],
startDate: 1743991211000,
endDate: 1745978411000,
// startTime: '12:00',
// endTime: '20:00',
cardId: this.data.cardId,
cardNumber: this.data.cardNumber,
cardName: '15161195',
cardType: 2,
cardUserNo: 999
};
if (operate === 1) {
parms.cardName = '测试卡片2'
}
console.log('parms', parms)
const registerExtendedProductsResult = await registerExtendedProducts(parms)
console.log('registerExtendedProductsResult', registerExtendedProductsResult);
},
fingerprintOperate(e) {
const operate = e.currentTarget.dataset.operate;
this._fingerprintOperate(Number(operate)); // 调用实际的逻辑函数
},
async _fingerprintOperate(operate) {
console.log('this.data', this.data)
const parms = {
type: 'fingerprint',
keyId: String(this.data.lock.keyId),
uid: String(this.data.accountInfo.uid),
userCountLimit: 0xFFFF,
operate: operate,
isAdmin: 1,
isForce: 0,
isRound: 0,
weekDays: [],
startDate: 0,
endDate: 0,
startTime: '00:00',
endTime: '00:00',
fingerprintId: this.data.fingerprintId,
fingerprintNumber: this.data.fingerprintNumber,
fingerprintName: '测试指纹1',
fingerprintType: 1,
fingerprintUserNo: 999,
deleteType: 1
};
if (operate === 1) {
parms.fingerprintName = '测试指纹2'
}
console.log('parms', parms)
const registerExtendedProductsResult = await registerExtendedProducts(parms)
console.log('registerExtendedProductsResult', registerExtendedProductsResult);
},
async findAllCard() {
const result = await getIcCardList({lockId: this.data.lock.lockId})
console.log('findAllCard', result)
},
async findAllFingerprint() {
const result = await getFingerprintList({lockId: this.data.lock.lockId})
console.log('findAllFingerprint', result)
},
customPasswordOperate(e) {
const operate = e.currentTarget.dataset.operate;
this._customPasswordOperate(Number(operate)); // 调用实际的逻辑函数
},
async _customPasswordOperate(operate) {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
navigateToCard() {
wx.navigateTo({
url: '/pages/card/card'
})
const parms = {
accountInfo: this.data.accountInfo,
password: {
keyboardPwdName: '测试密码',
keyboardPwdType: 3,
keyboardPwd: 123456,
addType: 2,
isCoerced: 2,
startDate: 1743997466000,
endDate: 1743997646000,
operate: operate,
pwdRight: 0,
lockId: this.data.lock.lockId,
}
};
if (parms.password.operate === 1 || parms.password.operate === 2) {
parms.password.keyboardPwdId = this.data.pwdId
parms.password.pwdNo = this.data.pwdNo
parms.password.keyboardPwd = 112233
parms.password.keyboardPwdName = '测试密码1'
}
const result = await customPassword(parms);
if (parms.password.operate === 0) {
this.data.pwdId = result.data.keyboardPwdId
this.data.pwdNo = result.data.pwdNo
}
console.log('customPassword', result)
},
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;
this._setAdminPasswordOperate(Number(operate)); // 调用实际的逻辑函数
wx.showModal({
title: '设置管理员密码',
editable: true,
placeholderText: '请输入管理员密码',
success: (res) => {
if (res.confirm && res.content) {
this._setAdminPasswordOperate(Number(operate), res.content);
}
}
});
},
async _setAdminPasswordOperate(operate) {
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() {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
})
console.log('remoteUnLockRequest', await remoteUnLock());
},
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,
uid: String(this.data.accountInfo.uid),
adminPwd: '11223345'
featureBit: 30,
featureEnable: operate,
withParams: false
};
const reslut= await updateAdminPassword(parms);
console.log('_setAdminPasswordOperate', reslut)
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) {
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 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,26 +1,154 @@
<view>
<button bind:tap="searchDevice">搜索设备</button>
<view wx:for="{{list}}" wx:for-item="device" class="device" bind:tap="bindDevice" data-device="{{device}}">
{{device.name}}
<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>
<button wx:if="{{lock}}" bind:tap="openDoor">开门</button>
<button wx:if="{{lock}}" bind:tap="deleteLock">删除设备</button>
<button wx:if="{{lock}}" bind:tap="getLockSupportFeatures">获取锁支持项</button>
<button wx:if="{{lock}}" bind:tap="getOfflinePassword">获取锁离线密码</button>
<button wx:if="{{lock}}" bind:tap="icCardOperate" data-operate="0">添加卡片</button>
<button wx:if="{{lock}}" bind:tap="icCardOperate" data-operate="1">修改卡片</button>
<button wx:if="{{lock}}" bind:tap="icCardOperate" data-operate="2">删除卡片</button>
<button wx:if="{{lock}}" bind:tap="icCardOperate" data-operate="3">删除全部卡片</button>
<button wx:if="{{lock}}" bind:tap="findAllCard" >查询卡片列表</button>
<button wx:if="{{lock}}" bind:tap="fingerprintOperate" data-operate="0">添加指纹</button>
<button wx:if="{{lock}}" bind:tap="fingerprintOperate" data-operate="1">修改指纹</button>
<button wx:if="{{lock}}" bind:tap="fingerprintOperate" data-operate="2">删除指纹</button>
<button wx:if="{{lock}}" bind:tap="fingerprintOperate" data-operate="3">删除全部指纹</button>
<button wx:if="{{lock}}" bind:tap="findAllFingerprint" >查询指纹列表</button>
<button wx:if="{{lock}}" bind:tap="setAdminPasswordOperate" data-operate="0">设置管理员密码</button>
<button wx:if="{{lock}}" bind:tap="customPasswordOperate" data-operate="0">添加自定义密码</button>
<button wx:if="{{lock}}" bind:tap="customPasswordOperate" data-operate="1">修改自定义密码</button>
<button wx:if="{{lock}}" bind:tap="customPasswordOperate" data-operate="2">删除自定义密码</button>
<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>
<!-- 已绑定设备时显示操作界面 -->
<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>
</view>
</view>

View File

@ -1,5 +1,347 @@
.device {
.container {
padding: 32rpx;
margin: 32rpx;
border: #2c405a 1px solid;
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 {
display: flex;
align-items: center;
margin: 10px 0;
}
.picker-item text {
width: 80px;
}
.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;
color: #333;
margin-bottom: 8px;
}
.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;
display: flex;
flex-direction: column;
overflow: hidden;
}
.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;
background: #f8f9fa;
}
.form-item .hint {
display: block;
font-size: 24rpx;
color: #666;
margin-top: 12rpx;
line-height: 1.5;
}
.radio-group {
display: flex;
gap: 30rpx;
}
.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;
}

View File

@ -0,0 +1,264 @@
const {
selectLock,
getOfflinePassword,
customPassword
} = requirePlugin('starCloud')
Page({
data: {
accountInfo: null,
lock: null,
pwdId: -1,
pwdNo: 0,
currentStartDate: '',
currentStartTime: '',
currentEndDate: '',
currentEndTime: '',
pwdTypeList: [
{id: 1, name: '单次密码'},
{id: 2, name: '永久密码'},
{id: 3, name: '限时密码'},
{id: 4, name: '删除密码'},
{id: 5, name: '周末循环'},
{id: 6, name: '每日循环'},
{id: 7, name: '工作日循环'},
{id: 8, name: '周一循环'},
{id: 9, name: '周二循环'},
{id: 10, name: '周三循环'},
{id: 11, name: '周四循环'},
{id: 12, name: '周五循环'},
{id: 13, name: '周六循环'},
{id: 14, name: '周日循环'}
],
currentPwdType: 3, // 默认为限时密码
passwordName: '', // 密码名称
password: '', // 密码
isCoerced: false, // 是否胁迫密码
addTypeList: [
{id: 1, name: '管理员添加'},
{id: 2, name: '用户添加'}
],
currentAddType: 1,
isAdmin: false // 是否管理员
},
onLoad(options) {
const eventChannel = this.getOpenerEventChannel()
eventChannel.on('acceptDataFromOpenerPage', data => {
this.setData({
accountInfo: data.accountInfo,
lock: data.lock
})
})
},
bindPwdTypeChange(e) {
this.setData({
currentPwdType: Number(e.detail.value) + 1
})
},
bindCustomPwdTypeChange(e) {
const index = Number(e.detail.value);
// 因为只提供永久和限时两种选择所以index为0时是永久(2)为1时是限时(3)
this.setData({
currentPwdType: index === 0 ? 2 : 3
});
},
bindStartDateChange(e) {
this.setData({
currentStartDate: e.detail.value
})
},
bindStartTimeChange(e) {
this.setData({
currentStartTime: e.detail.value
})
},
bindEndDateChange(e) {
this.setData({
currentEndDate: e.detail.value
})
},
bindEndTimeChange(e) {
this.setData({
currentEndTime: e.detail.value
})
},
bindAddTypeChange(e) {
this.setData({
currentAddType: Number(e.detail.value) + 1
})
},
onCoercedChange(e) {
this.setData({
isCoerced: e.detail.value
})
},
onAdminChange(e) {
this.setData({
isAdmin: e.detail.value
})
},
_convertToTimestamp(date, time) {
if (!date || !time) return 0;
const dateTimeStr = `${date} ${time}:00`;
return new Date(dateTimeStr).getTime();
},
_getDefaultTimeRange() {
const now = new Date();
const start = new Date();
const end = new Date();
end.setHours(now.getHours() + 1, 0, 0, 0);
return {
hoursStart: now.getHours(),
hoursEnd: end.getHours(),
startDate: start.getTime(),
endDate: end.getTime()
};
},
async getOfflinePassword() {
wx.showLoading({
title: '处理中...',
mask: true
});
try {
let params = {
accountInfo: this.data.accountInfo,
password: {
lockId: this.data.lock.lockId,
keyboardPwdName: `密码${new Date().getTime()}`,
keyboardPwdType: this.data.currentPwdType,
isCoerced: 2,
startDate: 0,
endDate: 0,
hoursStart: 0,
hoursEnd: 0,
isAdmin: this.data.isAdmin ? 1 : 0, // 添加管理员标识
}
};
// 处理限时密码
if (this.data.currentPwdType === 3) {
params.password.startDate = this._convertToTimestamp(this.data.currentStartDate, this.data.currentStartTime);
params.password.endDate = this._convertToTimestamp(this.data.currentEndDate, this.data.currentEndTime);
}
// 处理循环密码(周末、每日、工作日、周一到周日循环)
if (params.password.keyboardPwdType >= 5) {
if (this.data.currentStartTime && this.data.currentEndTime) {
params.password.hoursStart = parseInt(this.data.currentStartTime.split(':')[0]);
params.password.hoursEnd = parseInt(this.data.currentEndTime.split(':')[0]);
const startDate = this.data.currentStartDate ? new Date(this.data.currentStartDate) : new Date();
const endDate = this.data.currentEndDate ? new Date(this.data.currentEndDate) : new Date();
params.password.startDate = startDate.getTime();
params.password.endDate = endDate.getTime();
} else {
// 使用默认时间范围
const defaultTime = this._getDefaultTimeRange();
params.password.hoursStart = defaultTime.hoursStart;
params.password.hoursEnd = defaultTime.hoursEnd;
params.password.startDate = defaultTime.startDate;
params.password.endDate = defaultTime.endDate;
}
}
const result = await getOfflinePassword(params)
if (result.code === 0) {
wx.showModal({
title: '离线密码',
content: result.data.keyboardPwd,
showCancel: false
})
}
} finally {
wx.hideLoading();
}
},
customPasswordOperate(e) {
const operate = e.currentTarget.dataset.operate;
this._customPasswordOperate(Number(operate));
},
async _customPasswordOperate(operate) {
if (!this.validateInput()) return;
wx.showLoading({
title: '处理中...',
mask: true
});
try {
await selectLock({
accountInfo: this.data.accountInfo,
lockId: this.data.lock.lockId
})
const params = {
accountInfo: this.data.accountInfo,
password: {
keyboardPwdName: this.data.passwordName,
keyboardPwdType: this.data.currentPwdType, // 使用选择的密码类型
keyboardPwd: Number(this.data.password),
addType: this.data.currentAddType,
isCoerced: this.data.isCoerced ? 1 : 2,
startDate: this._convertToTimestamp(this.data.currentStartDate, this.data.currentStartTime),
endDate: this._convertToTimestamp(this.data.currentEndDate, this.data.currentEndTime),
operate: operate,
pwdRight: 0,
lockId: this.data.lock.lockId,
}
};
if (params.password.operate === 1 || params.password.operate === 2) {
params.password.keyboardPwdId = this.data.pwdId
params.password.pwdNo = this.data.pwdNo
}
const result = await customPassword(params);
if (operate === 0 && result.code === 0) {
this.setData({
pwdId: result.data.keyboardPwdId,
pwdNo: result.data.pwdNo
})
}
wx.showToast({
title: ['添加', '修改', '删除'][operate] + (result.code === 0 ? '成功' : '失败'),
icon: 'none'
})
} finally {
wx.hideLoading();
}
},
validateInput() {
if (!this.data.passwordName) {
wx.showToast({
title: '请输入密码名称',
icon: 'none'
})
return false
}
if (!this.data.password || !/^\d{6,12}$/.test(this.data.password)) {
wx.showToast({
title: '请输入6-12位数字密码',
icon: 'none'
})
return false
}
return true
}
})

View File

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

View File

@ -0,0 +1,78 @@
<view class="container">
<view class="password-section">
<view class="section-header">离线密码</view>
<view class="form-item">
<text class="label">管理员</text>
<switch checked="{{isAdmin}}" bindchange="onAdminChange"/>
</view>
<view class="pwd-type-picker">
<picker range="{{pwdTypeList}}" range-key="name" bindchange="bindPwdTypeChange">
<view class="picker">
{{pwdTypeList[currentPwdType-1].name}}
</view>
</picker>
<button class="action-button" bind:tap="getOfflinePassword">获取离线密码</button>
</view>
</view>
<view class="password-section">
<view class="section-header">自定义密码</view>
<view class="form-item">
<text class="label">密码名称</text>
<input class="input" model:value="{{passwordName}}" placeholder="请输入密码名称"/>
</view>
<view class="form-item">
<text class="label">密码</text>
<input class="input" type="number" model:value="{{password}}" placeholder="请输入6-12位数字密码"/>
</view>
<view class="form-item">
<text class="label">胁迫密码</text>
<switch checked="{{isCoerced}}" bindchange="onCoercedChange"/>
</view>
<view class="form-item">
<text class="label">添加类型</text>
<picker range="{{addTypeList}}" range-key="name" bindchange="bindAddTypeChange">
<view class="picker">{{addTypeList[currentAddType-1].name}}</view>
</picker>
</view>
<view class="form-item">
<text class="label">密码类型</text>
<picker range="{{[pwdTypeList[1], pwdTypeList[2]]}}" range-key="name" bindchange="bindCustomPwdTypeChange">
<view class="picker">{{currentPwdType === 2 ? '永久密码' : '限时密码'}}</view>
</picker>
</view>
<view class="password-time-picker" wx:if="{{currentPwdType === 3}}">
<view class="time-section">
<view class="section-title">时间设置</view>
<view class="picker-group">
<picker mode="date" value="{{currentStartDate}}" start="2024-01-01" end="2025-12-31" bindchange="bindStartDateChange">
<view class="picker">{{currentStartDate || '开始日期'}}</view>
</picker>
<picker mode="time" value="{{currentStartTime}}" bindchange="bindStartTimeChange">
<view class="picker">{{currentStartTime || '开始时间'}}</view>
</picker>
</view>
<view class="picker-group">
<picker mode="date" value="{{currentEndDate}}" start="2024-01-01" end="2025-12-31" bindchange="bindEndDateChange">
<view class="picker">{{currentEndDate || '结束日期'}}</view>
</picker>
<picker mode="time" value="{{currentEndTime}}" bindchange="bindEndTimeChange">
<view class="picker">{{currentEndTime || '结束时间'}}</view>
</picker>
</view>
</view>
</view>
<view class="button-group">
<button class="action-button" bind:tap="customPasswordOperate" data-operate="0">添加</button>
<button class="action-button" bind:tap="customPasswordOperate" data-operate="1">修改</button>
<button class="action-button" bind:tap="customPasswordOperate" data-operate="2">删除</button>
</view>
</view>
</view>

View File

@ -0,0 +1,104 @@
.container {
padding: 24rpx;
background-color: #f5f7fa;
min-height: 100vh;
}
.password-section {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 32rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.section-header {
font-size: 36rpx;
font-weight: 600;
margin-bottom: 32rpx;
padding-bottom: 20rpx;
border-bottom: 2rpx solid #eef0f5;
color: #2c405a;
}
.pwd-type-picker {
margin-bottom: 32rpx;
}
.picker {
background: #f8f9fc;
padding: 24rpx;
border-radius: 12rpx;
margin-bottom: 20rpx;
flex: 1;
font-size: 30rpx;
color: #333;
border: 2rpx solid #e5e7eb;
}
.time-section {
margin: 32rpx 0;
}
.section-title {
font-size: 32rpx;
color: #2c405a;
margin-bottom: 20rpx;
font-weight: 500;
}
.picker-group {
display: flex;
gap: 24rpx;
margin-bottom: 20rpx;
}
.button-group {
display: flex;
gap: 24rpx;
margin-top: 40rpx;
}
.action-button {
flex: 1;
margin: 0;
background: #2c405a;
color: white;
font-size: 30rpx;
padding: 20rpx 0;
border-radius: 12rpx;
transition: all 0.3s;
}
.action-button:active {
opacity: 0.8;
transform: scale(0.98);
}
.form-item {
display: flex;
align-items: center;
padding: 28rpx 0;
border-bottom: 2rpx solid #eef0f5;
}
.label {
width: 180rpx;
font-size: 30rpx;
color: #2c405a;
font-weight: 500;
}
.input {
flex: 1;
font-size: 30rpx;
padding: 16rpx 24rpx;
background: #f8f9fc;
border-radius: 8rpx;
border: 2rpx solid #e5e7eb;
}
.input:focus {
border-color: #2c405a;
background: #fff;
}

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.11",
"star-cloud-uni": "^1.0.12",
"vue": "^3.5.13",
"vue-i18n": "^9.1.9"
},
@ -10046,9 +10046,9 @@
}
},
"node_modules/star-cloud-uni": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/star-cloud-uni/-/star-cloud-uni-1.0.11.tgz",
"integrity": "sha512-4FrCDxiMka+vCG9ac9yvBJAIxHy3FqTOykdGaIrzzztU3pbWo8W+4ASkrvfP9mIVs35UoKPN94OBsGGUGNN40g==",
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/star-cloud-uni/-/star-cloud-uni-1.0.12.tgz",
"integrity": "sha512-QhBsp8kF0aOaveFohaDTVibX6v9zJAczMvC5G1faPH++B48LmgPTmsqihlD0TwEip8nTWuNQDT89xZ84nbWg5g==",
"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.11",
"star-cloud-uni": "^1.0.12",
"vue": "^3.5.13",
"vue-i18n": "^9.1.9"
},

View File

@ -19,9 +19,13 @@ import {
updateAdminPassword,
syncOpenDoorRecord,
getServerTime, getLockSupportFeatures,
Result, registerExtendedProductsCancel
Result, registerExtendedProductsCancel,
updateSupportFunctionsWithParams,
remoteUnLock,
readSupportFunctionsSetting,
} from 'star-cloud-uni'
// 用于存储事件监听器的Map
const eventListeners = new Map();
module.exports = {
Result,
/**
@ -195,6 +199,16 @@ module.exports = {
return await getRemoteList(params)
},
async updateSupportFunctionsWithParams(params) {
return await updateSupportFunctionsWithParams(params)
},
async readSupportFunctions(params) {
return await readSupportFunctionsSetting(params)
},
/**
* 事件触发
* @param eventName 事件昵称
@ -210,7 +224,6 @@ module.exports = {
uni.$emit(eventName, object)
},
/**
* 事件监听
* @param eventName 事件昵称
@ -224,20 +237,28 @@ module.exports = {
return;
}
// 使用uni.$on监听事件
// 检查是否已经存在该事件的监听器
if (eventListeners.has(eventName)) {
console.warn(`Event ${eventName} already has a listener registered.`);
return;
}
// 创建监听器函数
const listener = async (data) => {
try {
// 调用外部传入的回调函数
await callBack(data);
} catch (error) {
console.error(`Error executing callback for event ${eventName}:`, error);
}
};
// 存储监听器
eventListeners.set(eventName, listener);
// 注册事件监听
uni.$on(eventName, listener);
},
/**
* 事件监听但仅触发一次在第一次触发之后移除该监听器
* @param eventName 事件昵称
@ -265,7 +286,7 @@ module.exports = {
},
/**
* 移除全局自定义事件监听器
* 移除全局自定义事件监听器
* @param eventName
* @param callBack
* @returns {Promise<void>}
@ -277,15 +298,15 @@ module.exports = {
return;
}
const listener = async (data) => {
try {
// 调用外部传入的回调函数
await callBack(data);
} catch (error) {
console.error(`Error executing callback for event ${eventName}:`, error);
}
};
// 获取存储的监听器
const listener = eventListeners.get(eventName);
if (listener) {
uni.$off(eventName, listener);
eventListeners.delete(eventName);
}
},
async remoteUnLock(){
return await remoteUnLock()
}
}