初始化

This commit is contained in:
syruan 2025-11-10 13:32:14 +08:00
parent be6c80f198
commit c24113d96f
16 changed files with 805 additions and 32 deletions

335
app.js
View File

@ -1,10 +1,345 @@
import gulpError from './utils/gulpError';
App({
globalData: {
userInfo: null,
authInfo: null
},
onLaunch(options) {
console.log('小程序启动', options);
// 检查登录状态
this.checkLoginStatus();
// 检查更新
this.checkUpdate();
},
onShow() {
if (gulpError !== 'gulpErrorPlaceHolder') {
wx.redirectTo({
url: `/pages/gulp-error/index?gulpError=${gulpError}`,
});
return;
}
// 刷新登录状态
this.refreshLoginStatus();
},
// ==================== 认证相关方法 ====================
/**
* 检查是否已登录
*/
isLoggedIn() {
const userInfo = wx.getStorageSync('userInfo');
const authInfo = wx.getStorageSync('authInfo');
return !!(userInfo && authInfo);
},
/**
* 获取用户信息
*/
getUserInfo() {
return wx.getStorageSync('userInfo') || this.globalData.userInfo;
},
/**
* 保存用户信息
*/
saveUserInfo(userInfo) {
wx.setStorageSync('userInfo', userInfo);
this.globalData.userInfo = userInfo;
},
/**
* 获取认证信息
*/
getAuthInfo() {
return wx.getStorageSync('authInfo') || this.globalData.authInfo;
},
/**
* 保存认证信息
*/
saveAuthInfo(authInfo) {
wx.setStorageSync('authInfo', authInfo);
this.globalData.authInfo = authInfo;
},
/**
* 用户登录
*/
login() {
return new Promise((resolve, reject) => {
// 1. 先获取用户信息(必须在同步上下文中)
wx.getUserProfile({
desc: '用于完善用户资料',
success: userProfileRes => {
const userInfo = userProfileRes.userInfo;
this.saveUserInfo(userInfo);
console.log('获取用户信息成功', userInfo);
// 2. 再执行 wx.login
wx.login({
success: loginRes => {
console.log('登录成功code:', loginRes.code);
// TODO: 将 code 发送到后端,换取 openId 和 session_key
// 这里使用模拟数据
const authInfo = {
token: 'mock_token_' + Date.now(),
openId: 'mock_openid_' + Date.now(),
sessionKey: 'mock_session_key',
loginTime: Date.now()
};
this.saveAuthInfo(authInfo);
console.log('保存认证信息成功');
resolve({
success: true,
userInfo: userInfo,
authInfo: authInfo
});
},
fail: loginErr => {
console.error('wx.login 失败', loginErr);
reject(loginErr);
}
});
},
fail: userProfileErr => {
console.error('获取用户信息失败', userProfileErr);
reject(userProfileErr);
}
});
});
},
/**
* 静默登录不需要用户授权
*/
silentLogin() {
return new Promise((resolve, reject) => {
wx.login({
success: res => {
console.log('静默登录成功code:', res.code);
// TODO: 将 code 发送到后端
const authInfo = {
token: 'mock_token_' + Date.now(),
openId: 'mock_openid_' + Date.now(),
sessionKey: 'mock_session_key',
loginTime: Date.now()
};
this.saveAuthInfo(authInfo);
resolve({ success: true, authInfo });
},
fail: reject
});
});
},
/**
* 检查登录状态
*/
checkLoginStatus() {
if (this.isLoggedIn()) {
this.globalData.userInfo = this.getUserInfo();
this.globalData.authInfo = this.getAuthInfo();
console.log('用户已登录', this.globalData.userInfo);
} else {
console.log('用户未登录');
}
},
/**
* 刷新登录状态
*/
async refreshLoginStatus() {
try {
// 检查会话是否有效
const sessionValid = await this.checkSession();
if (!sessionValid) {
// 会话过期,静默登录
await this.silentLogin();
}
this.checkLoginStatus();
} catch (error) {
console.error('刷新登录状态失败', error);
}
},
/**
* 检查会话是否有效
*/
checkSession() {
return new Promise(resolve => {
wx.checkSession({
success: () => {
console.log('会话未过期');
resolve(true);
},
fail: () => {
console.log('会话已过期');
resolve(false);
}
});
});
},
/**
* 退出登录
*/
logout() {
wx.removeStorageSync('userInfo');
wx.removeStorageSync('authInfo');
this.globalData.userInfo = null;
this.globalData.authInfo = null;
wx.reLaunch({
url: '/pages/login/login'
});
},
// ==================== 消息推送相关方法 ====================
/**
* 订阅消息模板ID
*/
TEMPLATE_IDS: {
INSPECTION_REMINDER: 'YOUR_TEMPLATE_ID_1',
WARNING_NOTICE: 'YOUR_TEMPLATE_ID_2',
INSPECTION_COMPLETE: 'YOUR_TEMPLATE_ID_3'
},
/**
* 请求订阅消息
*/
requestSubscribeMessage(tmplIds) {
return new Promise((resolve, reject) => {
wx.requestSubscribeMessage({
tmplIds: tmplIds,
success: res => {
console.log('订阅消息成功', res);
resolve(res);
},
fail: err => {
console.error('订阅消息失败', err);
reject(err);
}
});
});
},
/**
* 订阅所有消息
*/
async subscribeAllMessages() {
try {
const result = await this.requestSubscribeMessage([
this.TEMPLATE_IDS.INSPECTION_REMINDER,
this.TEMPLATE_IDS.WARNING_NOTICE,
this.TEMPLATE_IDS.INSPECTION_COMPLETE
]);
// 保存订阅状态
const subscriptionStatus = {
inspectionReminder: result[this.TEMPLATE_IDS.INSPECTION_REMINDER] === 'accept',
warningNotice: result[this.TEMPLATE_IDS.WARNING_NOTICE] === 'accept',
inspectionComplete: result[this.TEMPLATE_IDS.INSPECTION_COMPLETE] === 'accept',
updateTime: Date.now()
};
wx.setStorageSync('subscription_status', subscriptionStatus);
return { success: true, subscriptionStatus };
} catch (error) {
console.error('订阅失败', error);
return { success: false, error };
}
},
/**
* 获取订阅状态
*/
getSubscriptionStatus() {
return wx.getStorageSync('subscription_status') || {
inspectionReminder: false,
warningNotice: false,
inspectionComplete: false
};
},
/**
* 检查是否已订阅
*/
isSubscribed() {
const status = this.getSubscriptionStatus();
return status.inspectionReminder || status.warningNotice || status.inspectionComplete;
},
/**
* 获取消息推送设置
*/
getNotificationSettings() {
return wx.getStorageSync('notification_settings') || {
enabled: true,
inspectionReminder: true,
warningNotice: true,
advanceDays: 7
};
},
/**
* 保存消息推送设置
*/
saveNotificationSettings(settings) {
wx.setStorageSync('notification_settings', settings);
},
// ==================== 其他方法 ====================
/**
* 检查小程序更新
*/
checkUpdate() {
if (wx.canIUse('getUpdateManager')) {
const updateManager = wx.getUpdateManager();
updateManager.onCheckForUpdate(res => {
console.log('检查更新', res.hasUpdate);
});
updateManager.onUpdateReady(() => {
wx.showModal({
title: '更新提示',
content: '新版本已经准备好,是否重启应用?',
success: res => {
if (res.confirm) {
updateManager.applyUpdate();
}
}
});
});
updateManager.onUpdateFailed(() => {
wx.showModal({
title: '更新失败',
content: '新版本下载失败,请检查网络后重试',
showCancel: false
});
});
}
},
/**
* 设置用户信息
*/
setUserInfo(userInfo) {
this.saveUserInfo(userInfo);
}
});

View File

@ -1,6 +1,7 @@
{
"darkmode": true,
"pages": [
"pages/login/login",
"pages/index/index",
"pages/device/list/list",
"pages/device/detail/detail",
@ -19,23 +20,33 @@
"list": [
{
"pagePath": "pages/index/index",
"text": "首页"
"text": "首页",
"iconPath": "assets/icons/home.png",
"selectedIconPath": "assets/icons/home-active.png"
},
{
"pagePath": "pages/device/list/list",
"text": "设备"
"text": "设备",
"iconPath": "assets/icons/device.png",
"selectedIconPath": "assets/icons/device-active.png"
},
{
"pagePath": "pages/statistics/index/index",
"text": "统计"
"text": "统计",
"iconPath": "assets/icons/chart.png",
"selectedIconPath": "assets/icons/chart-active.png"
},
{
"pagePath": "pages/warning/list/list",
"text": "预警"
"text": "预警",
"iconPath": "assets/icons/warning.png",
"selectedIconPath": "assets/icons/warning-active.png"
},
{
"pagePath": "pages/profile/index/index",
"text": "我的"
"text": "我的",
"iconPath": "assets/icons/user.png",
"selectedIconPath": "assets/icons/user-active.png"
}
]
},
@ -66,6 +77,14 @@
"navigationBarTextStyle": "white",
"backgroundColor": "#F5F5F5"
},
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于设备定位"
}
},
"requiredPrivateInfos": [
"getLocation"
],
"sitemapLocation": "sitemap.json",
"lazyCodeLoading": "requiredComponents"
}

View File

@ -1,4 +1,13 @@
{
"navigationBarTitleText": "添加设备"
"navigationBarTitleText": "添加设备",
"usingComponents": {
"t-input": "tdesign-miniprogram/input/input",
"t-picker": "tdesign-miniprogram/picker/picker",
"t-picker-item": "tdesign-miniprogram/picker-item/picker-item",
"t-date-time-picker": "tdesign-miniprogram/date-time-picker/date-time-picker",
"t-button": "tdesign-miniprogram/button/button",
"t-cell": "tdesign-miniprogram/cell/cell",
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group"
}
}

View File

@ -1,4 +1,11 @@
{
"navigationBarTitleText": "设备详情"
"navigationBarTitleText": "设备详情",
"usingComponents": {
"t-icon": "tdesign-miniprogram/icon/icon",
"t-tag": "tdesign-miniprogram/tag/tag",
"t-button": "tdesign-miniprogram/button/button",
"t-cell": "tdesign-miniprogram/cell/cell",
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group"
}
}

View File

@ -1,6 +1,15 @@
{
"navigationBarTitleText": "设备台账",
"enablePullDownRefresh": true,
"backgroundColor": "#F5F5F5"
"backgroundColor": "#F5F5F5",
"usingComponents": {
"t-icon": "tdesign-miniprogram/icon/icon",
"t-search": "tdesign-miniprogram/search/search",
"t-tabs": "tdesign-miniprogram/tabs/tabs",
"t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel",
"t-tag": "tdesign-miniprogram/tag/tag",
"t-empty": "tdesign-miniprogram/empty/empty",
"t-loading": "tdesign-miniprogram/loading/loading"
}
}

View File

@ -1,6 +1,12 @@
{
"navigationBarTitleText": "工作台",
"enablePullDownRefresh": true,
"backgroundColor": "#F5F5F5"
"backgroundColor": "#F5F5F5",
"usingComponents": {
"t-icon": "tdesign-miniprogram/icon/icon",
"t-tag": "tdesign-miniprogram/tag/tag",
"t-empty": "tdesign-miniprogram/empty/empty",
"t-button": "tdesign-miniprogram/button/button"
}
}

View File

@ -1,5 +1,117 @@
// pages/profile/index/index.js
const app = getApp();
Page({
data: {
isLoggedIn: false,
userInfo: null,
isSubscribed: false
},
onLoad() {
this.loadUserInfo();
},
onShow() {
this.loadUserInfo();
this.checkSubscriptionStatus();
},
/**
* 加载用户信息
*/
loadUserInfo() {
const isLoggedIn = app.isLoggedIn();
const userInfo = app.getUserInfo();
this.setData({
isLoggedIn,
userInfo: userInfo || {
nickName: '未登录',
avatarUrl: ''
}
});
},
/**
* 检查订阅状态
*/
checkSubscriptionStatus() {
const isSubscribed = app.isSubscribed();
this.setData({
isSubscribed
});
},
/**
* 点击头部区域
*/
handleHeaderTap() {
if (!this.data.isLoggedIn) {
wx.navigateTo({
url: '/pages/login/login'
});
}
},
/**
* 消息通知设置
*/
goToNotificationSettings() {
if (!this.data.isLoggedIn) {
wx.showModal({
title: '提示',
content: '请先登录',
confirmText: '去登录',
success: res => {
if (res.confirm) {
wx.navigateTo({
url: '/pages/login/login'
});
}
}
});
return;
}
wx.showModal({
title: '消息通知',
content: '是否开启消息推送通知?开启后可以及时接收设备检验预警提醒',
confirmText: '开启',
cancelText: '取消',
success: async res => {
if (res.confirm) {
try {
await app.subscribeAllMessages();
this.setData({ isSubscribed: true });
wx.showToast({
title: '订阅成功',
icon: 'success'
});
} catch (error) {
console.error('订阅失败', error);
wx.showToast({
title: '订阅失败',
icon: 'none'
});
}
}
}
});
},
/**
* 预警设置
*/
goToWarningSettings() {
wx.navigateTo({
url: '/pages/warning/settings/settings'
});
},
/**
* 单位管理
*/
goToUnitManage() {
wx.showToast({
title: '功能开发中',
@ -7,19 +119,35 @@ Page({
});
},
goToWarningSettings() {
wx.navigateTo({
url: '/pages/warning/settings/settings'
});
},
/**
* 数据导出
*/
exportData() {
if (!this.data.isLoggedIn) {
wx.showModal({
title: '提示',
content: '请先登录',
confirmText: '去登录',
success: res => {
if (res.confirm) {
wx.navigateTo({
url: '/pages/login/login'
});
}
}
});
return;
}
wx.showToast({
title: '功能开发中',
icon: 'none'
});
},
/**
* 关于我们
*/
goToAbout() {
wx.showModal({
title: '关于我们',
@ -28,11 +156,29 @@ Page({
});
},
/**
* 帮助中心
*/
goToHelp() {
wx.showToast({
title: '功能开发中',
icon: 'none'
});
},
/**
* 退出登录
*/
handleLogout() {
wx.showModal({
title: '确认退出',
content: '确定要退出登录吗?',
success: res => {
if (res.confirm) {
app.logout();
}
}
});
}
});

View File

@ -1,4 +1,11 @@
{
"navigationBarTitleText": "我的"
"navigationBarTitleText": "我的",
"usingComponents": {
"t-icon": "tdesign-miniprogram/icon/icon",
"t-cell": "tdesign-miniprogram/cell/cell",
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
"t-tag": "tdesign-miniprogram/tag/tag",
"t-button": "tdesign-miniprogram/button/button"
}
}

View File

@ -1,22 +1,31 @@
<!--pages/profile/index/index.wxml-->
<view class="container">
<view class="profile-header">
<view class="profile-header" bindtap="handleHeaderTap">
<view class="avatar">
<t-icon name="user" size="80rpx" color="#fff" />
<image wx:if="{{isLoggedIn && userInfo.avatarUrl}}" src="{{userInfo.avatarUrl}}" class="avatar-img" />
<t-icon wx:else name="user" size="80rpx" color="#fff" />
</view>
<view class="user-info">
<view class="username">管理员</view>
<view class="user-role">系统管理员</view>
<view class="username">{{isLoggedIn ? userInfo.nickName : '未登录'}}</view>
<view class="user-role">{{isLoggedIn ? '已登录' : '点击登录'}}</view>
</view>
<t-icon name="chevron-right" size="32rpx" color="rgba(255,255,255,0.8)" />
</view>
<view class="menu-section">
<t-cell-group>
<t-cell title="单位管理" arrow hover bindtap="goToUnitManage">
<t-icon slot="left-icon" name="building" size="40rpx" />
<t-cell title="消息通知" arrow hover bindtap="goToNotificationSettings">
<t-icon slot="left-icon" name="notification" size="40rpx" />
<view slot="note">
<t-tag wx:if="{{isSubscribed}}" theme="success" size="small">已开启</t-tag>
<t-tag wx:else theme="default" size="small">未开启</t-tag>
</view>
</t-cell>
<t-cell title="预警设置" arrow hover bindtap="goToWarningSettings">
<t-icon slot="left-icon" name="notification" size="40rpx" />
<t-icon slot="left-icon" name="time" size="40rpx" />
</t-cell>
<t-cell title="单位管理" arrow hover bindtap="goToUnitManage">
<t-icon slot="left-icon" name="building" size="40rpx" />
</t-cell>
<t-cell title="数据导出" arrow hover bindtap="exportData">
<t-icon slot="left-icon" name="download" size="40rpx" />
@ -35,6 +44,12 @@
</t-cell-group>
</view>
<view class="menu-section" wx:if="{{isLoggedIn}}">
<t-button theme="default" size="large" block bindtap="handleLogout">
退出登录
</t-button>
</view>
<view class="version-info">
<text>版本号v1.0.0</text>
</view>

View File

@ -20,6 +20,12 @@
align-items: center;
justify-content: center;
margin-right: 32rpx;
overflow: hidden;
}
.avatar-img {
width: 100%;
height: 100%;
}
.user-info {

View File

@ -1,4 +1,7 @@
{
"navigationBarTitleText": "统计分析"
"navigationBarTitleText": "统计分析",
"usingComponents": {
"t-icon": "tdesign-miniprogram/icon/icon"
}
}

View File

@ -1,4 +1,11 @@
{
"navigationBarTitleText": "预警中心"
"navigationBarTitleText": "预警中心",
"usingComponents": {
"t-icon": "tdesign-miniprogram/icon/icon",
"t-tabs": "tdesign-miniprogram/tabs/tabs",
"t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel",
"t-tag": "tdesign-miniprogram/tag/tag",
"t-empty": "tdesign-miniprogram/empty/empty"
}
}

View File

@ -1,9 +1,50 @@
// pages/warning/settings/settings.js
const app = getApp();
Page({
data: {
warningDays: 30
warningDays: 30,
subscriptionStatus: {
inspection: false,
warning: false
}
},
onLoad() {
this.loadSettings();
},
onShow() {
this.checkSubscriptionStatus();
},
/**
* 加载设置
*/
loadSettings() {
const settings = app.getNotificationSettings();
this.setData({
warningDays: settings.advanceDays || 30
});
},
/**
* 检查订阅状态
*/
checkSubscriptionStatus() {
const status = app.getSubscriptionStatus();
this.setData({
subscriptionStatus: {
inspection: status.inspectionReminder,
warning: status.warningNotice
}
});
},
/**
* 显示天数选择器
*/
showDaysPicker() {
wx.showActionSheet({
itemList: ['7天', '15天', '30天', '60天', '90天'],
@ -18,10 +59,125 @@ Page({
});
},
goToMessageSettings() {
/**
* 订阅检验提醒
*/
async subscribeInspection() {
if (!app.isLoggedIn()) {
this.showLoginTip();
return;
}
try {
await app.requestSubscribeMessage([
app.TEMPLATE_IDS.INSPECTION_REMINDER
]);
this.checkSubscriptionStatus();
wx.showToast({
title: '订阅成功',
icon: 'success'
});
} catch (error) {
console.error('订阅失败', error);
wx.showToast({
title: '订阅失败',
icon: 'none'
});
}
},
/**
* 订阅预警通知
*/
async subscribeWarning() {
if (!app.isLoggedIn()) {
this.showLoginTip();
return;
}
try {
await app.requestSubscribeMessage([
app.TEMPLATE_IDS.WARNING_NOTICE
]);
this.checkSubscriptionStatus();
wx.showToast({
title: '订阅成功',
icon: 'success'
});
} catch (error) {
console.error('订阅失败', error);
wx.showToast({
title: '订阅失败',
icon: 'none'
});
}
},
/**
* 订阅所有消息
*/
async subscribeAll() {
if (!app.isLoggedIn()) {
this.showLoginTip();
return;
}
try {
await app.subscribeAllMessages();
this.checkSubscriptionStatus();
wx.showToast({
title: '订阅成功',
icon: 'success'
});
} catch (error) {
console.error('订阅失败', error);
wx.showToast({
title: '订阅失败',
icon: 'none'
});
}
},
/**
* 保存设置
*/
saveSettings() {
const settings = {
enabled: true,
inspectionReminder: this.data.subscriptionStatus.inspection,
warningNotice: this.data.subscriptionStatus.warning,
advanceDays: this.data.warningDays
};
app.saveNotificationSettings(settings);
wx.showToast({
title: '功能开发中',
icon: 'none'
title: '保存成功',
icon: 'success'
});
},
/**
* 显示登录提示
*/
showLoginTip() {
wx.showModal({
title: '提示',
content: '请先登录',
confirmText: '去登录',
success: res => {
if (res.confirm) {
wx.navigateTo({
url: '/pages/login/login'
});
}
}
});
}
});

View File

@ -1,4 +1,10 @@
{
"navigationBarTitleText": "预警设置"
"navigationBarTitleText": "预警设置",
"usingComponents": {
"t-icon": "tdesign-miniprogram/icon/icon",
"t-cell": "tdesign-miniprogram/cell/cell",
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
"t-button": "tdesign-miniprogram/button/button"
}
}

View File

@ -4,7 +4,38 @@
<view class="section-title">预警设置</view>
<t-cell-group>
<t-cell title="提前预警天数" note="{{warningDays}}天" arrow hover bindtap="showDaysPicker" />
<t-cell title="消息推送" arrow hover bindtap="goToMessageSettings" />
</t-cell-group>
</view>
<view class="settings-section">
<view class="section-title">消息推送设置</view>
<t-cell-group>
<t-cell
title="检验到期提醒"
note="{{subscriptionStatus.inspection ? '已订阅' : '未订阅'}}"
arrow
hover
bindtap="subscribeInspection"
>
<t-icon slot="left-icon" name="notification" size="40rpx" />
</t-cell>
<t-cell
title="预警通知"
note="{{subscriptionStatus.warning ? '已订阅' : '未订阅'}}"
arrow
hover
bindtap="subscribeWarning"
>
<t-icon slot="left-icon" name="error-circle" size="40rpx" />
</t-cell>
<t-cell
title="订阅所有消息"
arrow
hover
bindtap="subscribeAll"
>
<t-icon slot="left-icon" name="notification-filled" size="40rpx" />
</t-cell>
</t-cell-group>
</view>
@ -15,9 +46,16 @@
</view>
<view class="tips-content">
<text>• 系统会在设备检验到期前提前预警</text>
<text>• 可以设置消息推送,及时接收预警通知</text>
<text>• 订阅消息后才能接收推送通知</text>
<text>• 建议提前30天进行预警设置</text>
<text>• 设置后立即生效</text>
</view>
</view>
<view class="save-section">
<t-button theme="primary" size="large" block bindtap="saveSettings">
保存设置
</t-button>
</view>
</view>

View File

@ -48,3 +48,7 @@
line-height: 1.6;
}
.save-section {
margin-top: 48rpx;
}