diff --git a/miniprogram/miniprogram/app.js b/miniprogram/miniprogram/app.js index 7c52faf..5032278 100644 --- a/miniprogram/miniprogram/app.js +++ b/miniprogram/miniprogram/app.js @@ -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 + }) + } } }) diff --git a/miniprogram/miniprogram/app.json b/miniprogram/miniprogram/app.json index 41147cb..7b20773 100644 --- a/miniprogram/miniprogram/app.json +++ b/miniprogram/miniprogram/app.json @@ -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", diff --git a/miniprogram/miniprogram/pages/card/card.js b/miniprogram/miniprogram/pages/card/card.js new file mode 100644 index 0000000..d85bf68 --- /dev/null +++ b/miniprogram/miniprogram/pages/card/card.js @@ -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 表示删除操作 + } +}) \ No newline at end of file diff --git a/miniprogram/miniprogram/pages/card/card.json b/miniprogram/miniprogram/pages/card/card.json new file mode 100644 index 0000000..8835af0 --- /dev/null +++ b/miniprogram/miniprogram/pages/card/card.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/miniprogram/pages/card/card.wxml b/miniprogram/miniprogram/pages/card/card.wxml new file mode 100644 index 0000000..10feb53 --- /dev/null +++ b/miniprogram/miniprogram/pages/card/card.wxml @@ -0,0 +1,171 @@ + + + + 卡片名称: + + + + 卡片类型: + + 永久 + 期限 + 循环 + + + + 管理员: + + + + 胁迫模式: + + + + + 期限时间: + + {{startDateDisplay || '请选择开始时间'}} + + + + + + {{endDateDisplay || '请选择结束时间'}} + + + + + + 循环周期: + + {{weekdayPatterns[weekdayPatternIndex]}} + + + + 自定义: + + + + + + 生效日期: + + {{startDateDisplay || '请选择开始日期'}} + + + + 失效日期: + + {{endDateDisplay || '请选择结束日期'}} + + + + 生效时间: + + {{startTime || '请选择'}} + + + + 失效时间: + + {{endTime || '请选择'}} + + + + + + + 当前操作参数 + + + 卡片类型: + {{currentParams.type}} + + + 操作类型: + {{currentParams.operate}} + + + 管理员权限: + {{currentParams.isAdmin}} + + + 胁迫模式: + {{currentParams.isForce}} + + + 循环模式: + {{currentParams.isRound}} + + + + 循环周期: + {{currentParams.weekDay}} + + + + + 期限时间: + {{currentParams.displayTimeRange}} + + + + + 有效期: + {{currentParams.startDate}} 至 {{currentParams.endDate}} + + + + + 生效时间: + {{currentParams.startTime}} - {{currentParams.endTime}} + + + + + + + + + + + + + + + + + + + 卡片列表 + + + + + + + 名称:{{item.cardName}} + 类型:{{item.cardType === 1 ? '永久' : item.cardType === 2 ? '期限' : '循环'}} + 管理员:{{item.isAdmin === 1 ? '是' : '否'}} + 胁迫:{{item.isForce === 1 ? '是' : '否'}} + + + + {{item.startDate}} - {{item.endDate}} + + + 周期:{{item.weekDayText}} + + {{item.startTime}} - {{item.endTime}} + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/miniprogram/pages/card/card.wxss b/miniprogram/miniprogram/pages/card/card.wxss new file mode 100644 index 0000000..864af64 --- /dev/null +++ b/miniprogram/miniprogram/pages/card/card.wxss @@ -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; +} \ No newline at end of file diff --git a/miniprogram/miniprogram/pages/fingerprint/fingerprint.js b/miniprogram/miniprogram/pages/fingerprint/fingerprint.js new file mode 100644 index 0000000..245c179 --- /dev/null +++ b/miniprogram/miniprogram/pages/fingerprint/fingerprint.js @@ -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 + }); + } + } +}) \ No newline at end of file diff --git a/miniprogram/miniprogram/pages/fingerprint/fingerprint.json b/miniprogram/miniprogram/pages/fingerprint/fingerprint.json new file mode 100644 index 0000000..8835af0 --- /dev/null +++ b/miniprogram/miniprogram/pages/fingerprint/fingerprint.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/miniprogram/pages/fingerprint/fingerprint.wxml b/miniprogram/miniprogram/pages/fingerprint/fingerprint.wxml new file mode 100644 index 0000000..18eb88c --- /dev/null +++ b/miniprogram/miniprogram/pages/fingerprint/fingerprint.wxml @@ -0,0 +1,163 @@ + + + + 指纹名称: + + + + 指纹类型: + + 永久 + 期限 + 循环 + + + + 管理员: + + + + 胁迫模式: + + + + + 期限时间: + + {{startDateDisplay || '请选择开始时间'}} + + + + + + {{endDateDisplay || '请选择结束时间'}} + + + + + + 循环周期: + + {{weekdayPatterns[weekdayPatternIndex]}} + + + + 生效日期: + + {{startDateDisplay || '请选择开始日期'}} + + + + 失效日期: + + {{endDateDisplay || '请选择结束日期'}} + + + + 生效时间: + + {{startTime}} + + + + 失效时间: + + {{endTime}} + + + + + + + 当前操作参数 + + + 指纹类型: + {{currentParams.type}} + + + 操作类型: + {{currentParams.operate}} + + + 管理员权限: + {{currentParams.isAdmin}} + + + 胁迫模式: + {{currentParams.isForce}} + + + 循环模式: + {{currentParams.isRound}} + + + + 循环周期: + {{currentParams.weekDay}} + + + + + 期限时间: + {{currentParams.displayTimeRange}} + + + + + + 有效期: + {{currentParams.startDate}} 至 {{currentParams.endDate}} + + + + 生效时间: + {{currentParams.startTime}} - {{currentParams.endTime}} + + + + + + + + + + + + + + + + + + + 指纹列表 + + + + + + + 名称:{{item.fingerprintName}} + 类型:{{item.fingerprintType === 1 ? '永久' : item.fingerprintType === 2 ? '期限' : '循环'}} + 管理员:{{item.isAdmin === 1 ? '是' : '否'}} + 胁迫:{{item.isForce === 1 ? '是' : '否'}} + + + + {{item.startDateDisplay}} - {{item.endDateDisplay}} + + + 周期:{{item.weekDayText}} + + {{item.startTime}} - {{item.endTime}} + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/miniprogram/pages/fingerprint/fingerprint.wxss b/miniprogram/miniprogram/pages/fingerprint/fingerprint.wxss new file mode 100644 index 0000000..03910c0 --- /dev/null +++ b/miniprogram/miniprogram/pages/fingerprint/fingerprint.wxss @@ -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; +} \ No newline at end of file diff --git a/miniprogram/miniprogram/pages/index/index.js b/miniprogram/miniprogram/pages/index/index.js index 0f70640..5755e52 100644 --- a/miniprogram/miniprogram/pages/index/index.js +++ b/miniprogram/miniprogram/pages/index/index.js @@ -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: [] - }) - const result = await register() - if (result.code === Result.Success.code) { - this.setData({ - accountInfo: result.data + + startSearching() { + if (this.data.isSearching) { + wx.showToast({ + title: '正在搜索中', + icon: 'none' }) - init({ - clientId: 'Tmj4XoB9dkXjD7t7OHKHl564rJdmvPm9', - clientSecret: 'g1VvSbN0Ya3IqPkA8j9Xn54PE1L8zGiy', - env: 'SKY', - isReportLog: true, - accounts: [this.data.accountInfo] - }) - console.log('初始化成功:', this.data.accountInfo) + return } - 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); + + this.setData({ isSearching: true }) + searchDevice((result) => { + if (result.code === Result.Success.code) { + this.setData({ + list: result.data.list + }) + getApp().globalData.deviceList = result.data.list } - }); - 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); - }); + }) + + 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() + }, + + 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,63 +151,87 @@ Page({ content: `确定绑定${device.name}吗?`, success: async (res) => { if (res.confirm) { - const result = await bindDevice({ - accountInfo: this.data.accountInfo, - name: device.name, - }) - if (result.code === Result.Success.code) { - stopSearchDevice() - this.setData({ - lock: result.data.lock + await this.handleApiCall(async () => { + const result = await bindDevice({ + accountInfo: this.data.accountInfo, + name: device.name, }) - wx.showToast({ - title: '绑定成功', - icon: 'none', - }) - } + 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 selectLock({ - accountInfo: this.data.accountInfo, - lockId: this.data.lock.lockId - }) - const result = await openDoor({ - accountInfo: this.data.accountInfo, - disconnect: true, - type: 'open' - }) - console.log('开门结果', result) - if (result.code === Result.Success.code) { - wx.showToast({ - title: '开门成功', - icon: 'none', + 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() { - await selectLock({ - accountInfo: this.data.accountInfo, - lockId: this.data.lock.lockId + 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', + }) + } + }, '删除中...') + } + } }) - const result = await deleteLock({ - accountInfo: this.data.accountInfo, - }) - console.log('删除结果', result) - if (result.code === Result.Success.code) { - this.setData({ - lock: null - }) - wx.showToast({ - title: '删除成功', - icon: 'none', - }) - } }, + // 获取锁支持项 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); + navigateToCard() { + wx.navigateTo({ + url: '/pages/card/card' + }) }, - fingerprintOperate(e) { + + 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._fingerprintOperate(Number(operate)); // 调用实际的逻辑函数 + wx.showModal({ + title: '设置管理员密码', + editable: true, + placeholderText: '请输入管理员密码', + success: (res) => { + if (res.confirm && res.content) { + this._setAdminPasswordOperate(Number(operate), res.content); + } + } + }); }, - 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 _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 findAllCard() { - const result = await getIcCardList({lockId: this.data.lock.lockId}) - console.log('findAllCard', 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 findAllFingerprint() { - const result = await getFingerprintList({lockId: this.data.lock.lockId}) - console.log('findAllFingerprint', result) + + 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); + }, '设置中...'); }, - customPasswordOperate(e) { + + 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; - this._customPasswordOperate(Number(operate)); // 调用实际的逻辑函数 - }, - async _customPasswordOperate(operate) { await selectLock({ accountInfo: this.data.accountInfo, lockId: this.data.lock.lockId }) 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, - } + featureBit: 30, + featureEnable: operate, + withParams: false }; - 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 - } + const reslut = await updateSupportFunctionsWithParams(parms); + console.log('enableAntiPryAlarm', reslut) + }, + async updatePassageMode() { + this.setData({ + showPassageMode: true + }); + }, - console.log('customPassword', result) + hidePassageMode() { + this.setData({ + showPassageMode: false + }); }, - setAdminPasswordOperate(e) { - const operate = e.currentTarget.dataset.operate; - this._setAdminPasswordOperate(Number(operate)); // 调用实际的逻辑函数 + + onPassageModeInput(e) { + const { field } = e.currentTarget.dataset; + const { value } = e.detail; + this.setData({ + [`passageModeForm.${field}`]: value + }); }, - async _setAdminPasswordOperate(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' - }; - const reslut= await updateAdminPassword(parms); - console.log('_setAdminPasswordOperate', reslut) + + 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 + }); + }, '读取中...'); } + }) diff --git a/miniprogram/miniprogram/pages/index/index.wxml b/miniprogram/miniprogram/pages/index/index.wxml index ba9e0b0..cb6d639 100644 --- a/miniprogram/miniprogram/pages/index/index.wxml +++ b/miniprogram/miniprogram/pages/index/index.wxml @@ -1,26 +1,154 @@ - - - - {{device.name}} + + + + 可用设备 + + + + + + + {{item.name}} + 点击绑定 + + + + {{isSearching ? '正在搜索设备...' : '未找到设备,点击搜索按钮开始搜索'}} + - - - - - - - - - + + + + + 设备名称: + {{lock.lockName || lock.name || '未知'}} + + + MAC地址: + {{lock.mac}} + + + UID: + {{lock.uid}} + + + 钥匙ID: + {{lock.keyId}} + + + 锁ID: + {{lock.lockId}} + + - - - - - - - - - + + + + + + + 管理功能 + + + + + + + + + + 设置选项 + + + + + + + + + + + + + + + + + + 常开模式设置 + × + + + + + 常开模式状态 + + 开启 + 关闭 + + + + + 是否全天 + + + + + + + + 开始时间(分钟) + + 例如:\n480 = 08:00\n720 = 12:00\n1080 = 18:00 + + + + 结束时间(分钟) + + 注意:结束时间必须大于开始时间 + + + + 循环方式 + + 工作日循环 + 周末循环 + 每天循环 + + + + + 自动开锁 + + 开启 + 关闭 + + + + + + + + + diff --git a/miniprogram/miniprogram/pages/index/index.wxss b/miniprogram/miniprogram/pages/index/index.wxss index e46383b..f8ad8cd 100644 --- a/miniprogram/miniprogram/pages/index/index.wxss +++ b/miniprogram/miniprogram/pages/index/index.wxss @@ -1,5 +1,347 @@ -.device { - padding: 32rpx; - margin: 32rpx; - border: #2c405a 1px solid; +.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 { + 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; +} \ No newline at end of file diff --git a/miniprogram/miniprogram/pages/password/password.js b/miniprogram/miniprogram/pages/password/password.js new file mode 100644 index 0000000..86cc339 --- /dev/null +++ b/miniprogram/miniprogram/pages/password/password.js @@ -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 + } +}) diff --git a/miniprogram/miniprogram/pages/password/password.json b/miniprogram/miniprogram/pages/password/password.json new file mode 100644 index 0000000..8835af0 --- /dev/null +++ b/miniprogram/miniprogram/pages/password/password.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/miniprogram/pages/password/password.wxml b/miniprogram/miniprogram/pages/password/password.wxml new file mode 100644 index 0000000..e7f6ab7 --- /dev/null +++ b/miniprogram/miniprogram/pages/password/password.wxml @@ -0,0 +1,78 @@ + + + 离线密码 + + 管理员 + + + + + + {{pwdTypeList[currentPwdType-1].name}} + + + + + + + + 自定义密码 + + + 密码名称 + + + + + 密码 + + + + + 胁迫密码 + + + + + 添加类型 + + {{addTypeList[currentAddType-1].name}} + + + + + 密码类型 + + {{currentPwdType === 2 ? '永久密码' : '限时密码'}} + + + + + + 时间设置 + + + {{currentStartDate || '开始日期'}} + + + {{currentStartTime || '开始时间'}} + + + + + {{currentEndDate || '结束日期'}} + + + {{currentEndTime || '结束时间'}} + + + + + + + + + + + + diff --git a/miniprogram/miniprogram/pages/password/password.wxss b/miniprogram/miniprogram/pages/password/password.wxss new file mode 100644 index 0000000..fbb0006 --- /dev/null +++ b/miniprogram/miniprogram/pages/password/password.wxss @@ -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; +} diff --git a/package-lock.json b/package-lock.json index 000eba3..03f31da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 137cd50..4dc5190 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/src/index.js b/src/index.js index 05044a2..5c453b1 100644 --- a/src/index.js +++ b/src/index.js @@ -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} @@ -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); + } + }, - uni.$off(eventName, listener); + async remoteUnLock(){ + return await remoteUnLock() } }