29 KiB
短信登录开关与通用系统配置 Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 新建通用系统配置表和管理后台设置页面,通过「短信登录开关」控制小程序登录页是否显示手机号短信登录入口。
Architecture: 新建 t_system_config key-value 配置表,后端提供管理端 CRUD 接口和公开查询接口,管理后台新增「系统设置」页面动态渲染配置项,小程序登录页调用公开接口获取登录方式配置并条件渲染。
Tech Stack: Spring Boot 2.7 + MyBatis-Plus + Redis / Vue 3 + Element Plus / UniApp (Vue 3)
Spec: docs/superpowers/specs/2026-06-27-sms-login-config-design.md
Task 1: 数据库建表和初始数据
Files:
-
Execute: SQL on MySQL database
-
Step 1: 执行建表 SQL
CREATE TABLE t_system_config (
id VARCHAR(64) PRIMARY KEY,
config_key VARCHAR(100) NOT NULL UNIQUE COMMENT '配置唯一标识',
config_value VARCHAR(500) NOT NULL DEFAULT '' COMMENT '配置值',
value_type VARCHAR(20) NOT NULL DEFAULT 'string' COMMENT '值类型: boolean/string/number/json',
config_group VARCHAR(50) NOT NULL DEFAULT 'system' COMMENT '配置分组: system/login/notification',
config_name VARCHAR(100) NOT NULL DEFAULT '' COMMENT '显示名称',
description VARCHAR(300) DEFAULT '' COMMENT '配置说明',
sort_order INT NOT NULL DEFAULT 0 COMMENT '排序号',
is_visible TINYINT NOT NULL DEFAULT 1 COMMENT '是否在管理后台可见: 0-隐藏, 1-显示',
create_by VARCHAR(64) DEFAULT NULL,
create_time DATETIME DEFAULT NULL,
update_by VARCHAR(64) DEFAULT NULL,
update_time DATETIME DEFAULT NULL,
is_deleted TINYINT NOT NULL DEFAULT 0
) COMMENT = '系统配置表';
- Step 2: 插入初始数据(短信登录开关)
INSERT INTO t_system_config (id, config_key, config_value, value_type, config_group, config_name, description, sort_order, is_visible, create_time, update_time, is_deleted)
VALUES (
REPLACE(UUID(), '-', ''),
'sms_login_enabled',
'false',
'boolean',
'login',
'短信登录',
'是否启用手机号+短信验证码登录方式',
1,
1,
NOW(),
NOW(),
0
);
- Step 3: 验证数据
SELECT * FROM t_system_config WHERE is_deleted = 0;
Expected: 一行数据,config_key = 'sms_login_enabled',config_value = 'false'
Task 2: 后端 Entity — SystemConfig.java
Files:
-
Create:
server/src/main/java/com/emotion/entity/SystemConfig.java -
Step 1: 创建 SystemConfig 实体
package com.emotion.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.emotion.common.BaseEntity;
import lombok.experimental.SuperBuilder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 系统配置实体
*
* @author system
* @date 2026-06-27
*/
@Data
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_system_config")
public class SystemConfig extends BaseEntity {
/**
* 配置唯一标识
*/
@TableField("config_key")
private String configKey;
/**
* 配置值
*/
@TableField("config_value")
private String configValue;
/**
* 值类型: boolean/string/number/json
*/
@TableField("value_type")
private String valueType;
/**
* 配置分组: system/login/notification
*/
@TableField("config_group")
private String configGroup;
/**
* 显示名称
*/
@TableField("config_name")
private String configName;
/**
* 配置说明
*/
@TableField("description")
private String description;
/**
* 排序号
*/
@TableField("sort_order")
private Integer sortOrder;
/**
* 是否在管理后台可见: 0-隐藏, 1-显示
*/
@TableField("is_visible")
private Integer isVisible;
}
- Step 2: Commit
git add server/src/main/java/com/emotion/entity/SystemConfig.java
git commit -m "feat:新增系统配置实体类"
Task 3: 后端 Mapper — SystemConfigMapper.java
Files:
-
Create:
server/src/main/java/com/emotion/mapper/SystemConfigMapper.java -
Step 1: 创建 SystemConfigMapper
package com.emotion.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.emotion.entity.SystemConfig;
import org.apache.ibatis.annotations.Mapper;
/**
* 系统配置Mapper接口
*
* @author system
* @date 2026-06-27
*/
@Mapper
public interface SystemConfigMapper extends BaseMapper<SystemConfig> {
}
- Step 2: Commit
git add server/src/main/java/com/emotion/mapper/SystemConfigMapper.java
git commit -m "feat:新增系统配置Mapper"
Task 4: 后端 DTOs
Files:
-
Create:
server/src/main/java/com/emotion/dto/request/SystemConfigUpdateRequest.java -
Create:
server/src/main/java/com/emotion/dto/response/SystemConfigResponse.java -
Create:
server/src/main/java/com/emotion/dto/response/LoginConfigResponse.java -
Step 1: 创建 SystemConfigUpdateRequest
package com.emotion.dto.request;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 系统配置更新请求
*
* @author system
* @date 2026-06-27
*/
@Data
public class SystemConfigUpdateRequest {
/**
* 配置唯一标识
*/
@NotBlank(message = "配置标识不能为空")
private String configKey;
/**
* 配置值
*/
@NotBlank(message = "配置值不能为空")
private String configValue;
}
- Step 2: 创建 SystemConfigResponse
package com.emotion.dto.response;
import lombok.Data;
/**
* 系统配置响应
*
* @author system
* @date 2026-06-27
*/
@Data
public class SystemConfigResponse {
/**
* 配置唯一标识
*/
private String configKey;
/**
* 配置值
*/
private String configValue;
/**
* 值类型: boolean/string/number/json
*/
private String valueType;
/**
* 配置分组
*/
private String configGroup;
/**
* 显示名称
*/
private String configName;
/**
* 配置说明
*/
private String description;
/**
* 排序号
*/
private Integer sortOrder;
}
- Step 3: 创建 LoginConfigResponse
package com.emotion.dto.response;
import lombok.Data;
/**
* 登录方式配置响应
* 返回给小程序,告知当前启用的登录方式
*
* @author system
* @date 2026-06-27
*/
@Data
public class LoginConfigResponse {
/**
* 是否启用微信登录
*/
private Boolean wechatLoginEnabled;
/**
* 是否启用短信登录
*/
private Boolean smsLoginEnabled;
}
- Step 4: Commit
git add server/src/main/java/com/emotion/dto/request/SystemConfigUpdateRequest.java
git add server/src/main/java/com/emotion/dto/response/SystemConfigResponse.java
git add server/src/main/java/com/emotion/dto/response/LoginConfigResponse.java
git commit -m "feat:新增系统配置相关DTO"
Task 5: 后端 Service 接口 — SystemConfigService.java
Files:
-
Create:
server/src/main/java/com/emotion/service/SystemConfigService.java -
Step 1: 创建 SystemConfigService 接口
package com.emotion.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.emotion.dto.request.SystemConfigUpdateRequest;
import com.emotion.dto.response.LoginConfigResponse;
import com.emotion.dto.response.SystemConfigResponse;
import com.emotion.entity.SystemConfig;
import java.util.List;
/**
* 系统配置服务接口
*
* @author system
* @date 2026-06-27
*/
public interface SystemConfigService extends IService<SystemConfig> {
/**
* 获取所有可见配置列表
*/
List<SystemConfigResponse> getVisibleConfigList();
/**
* 批量更新配置
*/
void batchUpdateConfigs(List<SystemConfigUpdateRequest> requests);
/**
* 获取配置值(带 Redis 缓存)
*
* @param configKey 配置标识
* @return 配置值字符串,不存在返回 null
*/
String getConfigValue(String configKey);
/**
* 获取登录方式配置
*/
LoginConfigResponse getLoginConfig();
}
- Step 2: Commit
git add server/src/main/java/com/emotion/service/SystemConfigService.java
git commit -m "feat:新增系统配置服务接口"
Task 6: 后端 Service 实现 — SystemConfigServiceImpl.java
Files:
-
Create:
server/src/main/java/com/emotion/service/impl/SystemConfigServiceImpl.java -
Step 1: 创建 SystemConfigServiceImpl
package com.emotion.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emotion.dto.request.SystemConfigUpdateRequest;
import com.emotion.dto.response.LoginConfigResponse;
import com.emotion.dto.response.SystemConfigResponse;
import com.emotion.entity.SystemConfig;
import com.emotion.mapper.SystemConfigMapper;
import com.emotion.service.SystemConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* 系统配置服务实现类
*
* @author system
* @date 2026-06-27
*/
@Slf4j
@Service
public class SystemConfigServiceImpl extends ServiceImpl<SystemConfigMapper, SystemConfig> implements SystemConfigService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String CACHE_PREFIX = "system_config:";
private static final long CACHE_EXPIRE_HOURS = 24;
private static final String SMS_LOGIN_ENABLED_KEY = "sms_login_enabled";
@Override
public List<SystemConfigResponse> getVisibleConfigList() {
LambdaQueryWrapper<SystemConfig> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SystemConfig::getIsVisible, 1)
.orderByAsc(SystemConfig::getSortOrder);
List<SystemConfig> configs = list(wrapper);
return configs.stream().map(this::convertToResponse).collect(Collectors.toList());
}
@Override
public void batchUpdateConfigs(List<SystemConfigUpdateRequest> requests) {
for (SystemConfigUpdateRequest request : requests) {
LambdaUpdateWrapper<SystemConfig> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(SystemConfig::getConfigKey, request.getConfigKey())
.set(SystemConfig::getConfigValue, request.getConfigValue());
update(updateWrapper);
// 清除缓存
redisTemplate.delete(CACHE_PREFIX + request.getConfigKey());
log.info("更新系统配置: key={}, value={}", request.getConfigKey(), request.getConfigValue());
}
}
@Override
public String getConfigValue(String configKey) {
// 先查 Redis 缓存
String cacheKey = CACHE_PREFIX + configKey;
String cached = (String) redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return cached;
}
// 查数据库
LambdaQueryWrapper<SystemConfig> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SystemConfig::getConfigKey, configKey);
SystemConfig config = getOne(wrapper);
if (config == null) {
return null;
}
// 写入缓存
redisTemplate.opsForValue().set(cacheKey, config.getConfigValue(), CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
return config.getConfigValue();
}
@Override
public LoginConfigResponse getLoginConfig() {
LoginConfigResponse response = new LoginConfigResponse();
response.setWechatLoginEnabled(true);
String smsEnabled = getConfigValue(SMS_LOGIN_ENABLED_KEY);
response.setSmsLoginEnabled("true".equalsIgnoreCase(smsEnabled));
return response;
}
/**
* 实体转响应对象
*/
private SystemConfigResponse convertToResponse(SystemConfig config) {
SystemConfigResponse response = new SystemConfigResponse();
response.setConfigKey(config.getConfigKey());
response.setConfigValue(config.getConfigValue());
response.setValueType(config.getValueType());
response.setConfigGroup(config.getConfigGroup());
response.setConfigName(config.getConfigName());
response.setDescription(config.getDescription());
response.setSortOrder(config.getSortOrder());
return response;
}
}
- Step 2: Commit
git add server/src/main/java/com/emotion/service/impl/SystemConfigServiceImpl.java
git commit -m "feat:新增系统配置服务实现(含Redis缓存)"
Task 7: 后端管理端 Controller — SystemConfigController.java
Files:
-
Create:
server/src/main/java/com/emotion/controller/SystemConfigController.java -
Step 1: 创建 SystemConfigController
此 controller 的路径为 /admin/systemConfig/**,自动被 AdminAuthInterceptor 保护,不需要登录用户 JWT。
package com.emotion.controller;
import com.emotion.common.Result;
import com.emotion.dto.request.SystemConfigUpdateRequest;
import com.emotion.dto.response.SystemConfigResponse;
import com.emotion.service.SystemConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 系统配置管理控制器(管理端)
*
* @author system
* @date 2026-06-27
*/
@RestController
@RequestMapping("/admin/systemConfig")
@Tag(name = "系统配置管理", description = "系统配置的查询和更新功能")
public class SystemConfigController {
@Autowired
private SystemConfigService systemConfigService;
/**
* 获取所有可见配置列表
*/
@GetMapping("/list")
@Operation(summary = "获取配置列表", description = "获取所有可见的系统配置列表")
public Result<List<SystemConfigResponse>> list() {
List<SystemConfigResponse> configs = systemConfigService.getVisibleConfigList();
return Result.success(configs);
}
/**
* 批量更新配置
*/
@PutMapping("/update")
@Operation(summary = "批量更新配置", description = "批量更新系统配置值")
public Result<Void> update(@RequestBody @Validated List<SystemConfigUpdateRequest> requests) {
systemConfigService.batchUpdateConfigs(requests);
return Result.success("更新成功", null);
}
}
- Step 2: Commit
git add server/src/main/java/com/emotion/controller/SystemConfigController.java
git commit -m "feat:新增系统配置管理端Controller"
Task 8: 后端公开接口 — AuthController 新增 loginConfig
Files:
-
Modify:
server/src/main/java/com/emotion/controller/AuthController.java -
Step 1: 在 AuthController 中添加 loginConfig 接口
在 AuthController.java 中添加 SystemService 注入和新接口方法。
添加 import:
import com.emotion.dto.response.LoginConfigResponse;
添加 service 注入(在已有的 @Autowired 字段后面):
@Autowired
private SystemConfigService systemConfigService;
添加新接口方法(放在 wechatLogin 方法后面):
/**
* 获取登录方式配置
*/
@GetMapping("/loginConfig")
@Operation(summary = "获取登录方式配置", description = "返回当前启用的登录方式,供小程序登录页动态渲染")
public Result<LoginConfigResponse> getLoginConfig() {
LoginConfigResponse response = systemConfigService.getLoginConfig();
return Result.success(response);
}
- Step 2: 在 WebMvcConfig 白名单中放行
修改 server/src/main/java/com/emotion/config/WebMvcConfig.java,在 JWT 拦截器的 excludePathPatterns 中添加一行:
"/auth/loginConfig", // 登录方式配置接口(免登录)
添加位置:在 "/auth/wechat/login" 行的上方或下方。
- Step 3: Commit
git add server/src/main/java/com/emotion/controller/AuthController.java
git add server/src/main/java/com/emotion/config/WebMvcConfig.java
git commit -m "feat:新增公开接口 /auth/loginConfig 并放行白名单"
Task 9: 后端编译验证
- Step 1: 编译后端
cd server
mvn clean install -DskipTests
Expected: BUILD SUCCESS
-
Step 2: 如果有编译错误,修复后重新编译直到通过
-
Step 3: Commit(如果有修复)
git add -A
git commit -m "fix:修复系统配置编译错误"
Task 10: 管理后台 — API 和路由配置
Files:
-
Create:
web-admin/src/api/systemConfig.ts -
Modify:
web-admin/src/router/index.ts -
Modify:
web-admin/src/config/menu.ts -
Step 1: 创建 systemConfig API 文件
web-admin/src/api/systemConfig.ts:
import request from '@/utils/request'
// 获取所有可见配置列表
export function getSystemConfigList() {
return request({
url: '/admin/systemConfig/list',
method: 'get'
})
}
// 批量更新配置
export function updateSystemConfig(data: { configKey: string; configValue: string }[]) {
return request({
url: '/admin/systemConfig/update',
method: 'put',
data
})
}
- Step 2: 添加路由配置
在 web-admin/src/router/index.ts 中,在 dictionary 路由块之前添加:
{
path: '/system',
component: Layout,
redirect: '/system/settings',
meta: { title: '系统设置', icon: 'Setting' },
children: [
{
path: 'settings',
name: 'SystemSettings',
component: () => import('@/views/system/SystemSettings.vue'),
meta: { title: '基础设置' }
}
]
},
- Step 3: 添加菜单配置
在 web-admin/src/config/menu.ts 的 menuConfig 数组中,在 AI配置管理 项之前添加:
{
path: '/system',
title: '系统设置',
icon: 'Setting'
},
- Step 4: Commit
git add web-admin/src/api/systemConfig.ts
git add web-admin/src/router/index.ts
git add web-admin/src/config/menu.ts
git commit -m "feat:管理后台新增系统设置路由和菜单"
Task 11: 管理后台 — SystemSettings.vue 页面
Files:
-
Create:
web-admin/src/views/system/SystemSettings.vue -
Step 1: 创建 SystemSettings 页面
<template>
<div class="system-settings">
<div class="page-header">
<h2>基础设置</h2>
<el-button type="primary" :loading="saving" @click="handleSave">保存</el-button>
</div>
<el-card v-if="loading" v-loading="true" class="loading-card" />
<div v-else class="config-groups">
<el-card v-for="(items, group) in groupedConfigs" :key="group" class="config-group">
<template #header>
<span class="group-title">{{ groupLabels[group] || group }}</span>
</template>
<div v-for="item in items" :key="item.configKey" class="config-item">
<div class="config-label">
<span class="config-name">{{ item.configName }}</span>
<span class="config-desc">{{ item.description }}</span>
</div>
<div class="config-control">
<el-switch
v-if="item.valueType === 'boolean'"
v-model="editValues[item.configKey]"
:active-value="'true'"
:inactive-value="'false'"
/>
<el-input
v-else-if="item.valueType === 'string'"
v-model="editValues[item.configKey]"
style="width: 300px"
/>
<el-input-number
v-else-if="item.valueType === 'number'"
v-model="editValues[item.configKey]"
style="width: 200px"
/>
<span v-else>{{ editValues[item.configKey] }}</span>
</div>
</div>
</el-card>
<el-empty v-if="Object.keys(groupedConfigs).length === 0" description="暂无配置项" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { getSystemConfigList, updateSystemConfig } from '@/api/systemConfig'
interface ConfigItem {
configKey: string
configValue: string
valueType: string
configGroup: string
configName: string
description: string
sortOrder: number
}
const loading = ref(false)
const saving = ref(false)
const configs = ref<ConfigItem[]>([])
const editValues = ref<Record<string, string | boolean>>({})
const groupLabels: Record<string, string> = {
system: '系统配置',
login: '登录设置',
notification: '通知设置'
}
const groupedConfigs = computed(() => {
const groups: Record<string, ConfigItem[]> = {}
for (const item of configs.value) {
if (!groups[item.configGroup]) {
groups[item.configGroup] = []
}
groups[item.configGroup].push(item)
}
return groups
})
const fetchConfigs = async () => {
loading.value = true
try {
const res = await getSystemConfigList()
configs.value = res.data || []
// 初始化编辑值
const values: Record<string, string | boolean> = {}
for (const item of configs.value) {
if (item.valueType === 'boolean') {
values[item.configKey] = item.configValue
} else {
values[item.configKey] = item.configValue
}
}
editValues.value = values
} catch (error) {
ElMessage.error('获取配置列表失败')
} finally {
loading.value = false
}
}
const handleSave = async () => {
saving.value = true
try {
const updates = configs.value.map(item => ({
configKey: item.configKey,
configValue: String(editValues.value[item.configKey])
}))
await updateSystemConfig(updates)
ElMessage.success('保存成功')
await fetchConfigs()
} catch (error) {
ElMessage.error('保存失败')
} finally {
saving.value = false
}
}
onMounted(() => {
fetchConfigs()
})
</script>
<style scoped>
.system-settings {
padding: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.page-header h2 {
margin: 0;
font-size: 20px;
}
.loading-card {
height: 200px;
}
.config-group {
margin-bottom: 20px;
}
.group-title {
font-weight: 600;
font-size: 16px;
}
.config-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 0;
border-bottom: 1px solid var(--el-border-color-lighter);
}
.config-item:last-child {
border-bottom: none;
}
.config-label {
display: flex;
flex-direction: column;
gap: 4px;
}
.config-name {
font-size: 14px;
font-weight: 500;
color: var(--el-text-color-primary);
}
.config-desc {
font-size: 12px;
color: var(--el-text-color-secondary);
}
</style>
- Step 2: Commit
git add web-admin/src/views/system/SystemSettings.vue
git commit -m "feat:管理后台新增系统设置页面"
Task 12: 小程序 — 新增 getLoginConfig 接口
Files:
-
Modify:
mini-program/src/services/auth.js -
Step 1: 在 auth.js 中添加 getLoginConfig 方法
在 mini-program/src/services/auth.js 的 getSmsCode 方法之前,添加:
/**
* 获取登录方式配置
* @returns {Promise<Object>} 登录方式配置
*/
export const getLoginConfig = async () => {
const response = await get('/auth/loginConfig')
return response
}
- Step 2: 在底部的 default 导出对象中添加
getLoginConfig
export default {
getLoginConfig,
getSmsCode,
login,
wechatLogin,
logout,
refreshToken,
validateToken,
validateTokenStrict,
getCurrentUserInfo,
checkPhone
}
- Step 3: Commit
git add mini-program/src/services/auth.js
git commit -m "feat:小程序新增获取登录方式配置接口"
Task 13: 小程序 — 登录页条件渲染
Files:
-
Modify:
mini-program/src/pages/login/index.vue -
Step 1: 修改 template — 用 v-if 控制 SMS 相关元素
在 mini-program/src/pages/login/index.vue 中:
- 在「微信一键登录」按钮的
</view>之后,「分隔线」的<view class="login-divider">上添加v-if:
将:
<view class="login-divider">
<view></view>
<text>或使用手机号登录</text>
<view></view>
</view>
改为:
<view class="login-divider" v-if="loginConfig.smsLoginEnabled">
<view></view>
<text>或使用手机号登录</text>
<view></view>
</view>
- 在
<view class="form">上添加v-if:
将:
<view class="form">
改为:
<view class="form" v-if="loginConfig.smsLoginEnabled">
- 在「开启旅程」按钮上添加
v-if:
将:
<view
class="btn-primary login-btn"
:class="{ disabled: !canSubmit || loading }"
@click="handleLogin"
>
改为:
<view
v-if="loginConfig.smsLoginEnabled"
class="btn-primary login-btn"
:class="{ disabled: !canSubmit || loading }"
@click="handleLogin"
>
- Step 2: 修改 script — 添加 loginConfig 状态和接口调用
- 添加 import:在现有的
import { getSmsCode } from '../../services/auth.js'行,改为:
import { getSmsCode, getLoginConfig } from '../../services/auth.js'
- 添加
loginConfigref:在const isCountingDown = ref(false)行之后添加:
const loginConfig = ref({ wechatLoginEnabled: true, smsLoginEnabled: false })
- 在
onMounted回调中添加获取登录配置的逻辑,将现有的onMounted改为:
onMounted(async () => {
const windowInfo = uni.getWindowInfo()
statusBarHeight.value = windowInfo.statusBarHeight || 20
safeAreaTop.value = windowInfo.safeAreaInsets?.top || windowInfo.statusBarHeight || 20
safeAreaBottom.value = windowInfo.safeAreaInsets?.bottom || 0
try {
const res = await getLoginConfig()
if (res.data) {
loginConfig.value = res.data
}
} catch (e) {
// 接口失败时默认只显示微信登录
loginConfig.value = { wechatLoginEnabled: true, smsLoginEnabled: false }
}
})
- Step 3: Commit
git add mini-program/src/pages/login/index.vue
git commit -m "feat:小程序登录页根据配置动态显示短信登录入口"
Task 14: 部署后端到服务器
- Step 1: 部署后端
cd G:\IdeaProjects\emotion-museun
python deploy.py backend
Expected: 部署成功,无报错
- Step 2: 验证后端接口
用 curl 或浏览器验证:
curl https://lifescript.happylifeos.com/api/auth/loginConfig
Expected 响应(短信默认关闭):
{"code":200,"message":"操作成功","data":{"wechatLoginEnabled":true,"smsLoginEnabled":false},"timestamp":...}
Task 15: 管理后台验收
- Step 1: 打开管理后台
访问 https://lifescript.happylifeos.com/emotion-museum-admin/
-
Step 2: 验证侧边栏出现「系统设置」菜单
-
Step 3: 点击进入「基础设置」页面,确认显示「短信登录」开关
-
Step 4: 确认开关默认为关闭状态
-
Step 5: 打开开关并点击「保存」,确认保存成功
-
Step 6: 再次调用 loginConfig 接口验证值已更新
curl https://lifescript.happylifeos.com/api/auth/loginConfig
Expected: smsLoginEnabled 变为 true
- Step 7: 将开关改回关闭状态(为小程序验收做准备)
Task 16: 小程序 H5 验收
- Step 1: 启动小程序 H5 开发服务器
cd mini-program
npm run dev:h5
-
Step 2: 浏览器打开 http://localhost:5284
-
Step 3: 验证登录页只显示「微信一键登录」按钮,无手机号表单
-
Step 4: 在管理后台打开短信登录开关
-
Step 5: 刷新小程序 H5 登录页,验证出现手机号+验证码表单
-
Step 6: 关闭开关,刷新登录页,验证手机号表单消失
-
Step 7: 确认浏览器 Console 无任何报错
-
Step 8: 将开关恢复为关闭状态
Task 17: 最终提交
- Step 1: 确认所有代码已提交
git status
Expected: working tree clean
- Step 2: 如果没有全部提交,补充提交
git add -A
git commit -m "feat:短信登录开关与通用系统配置功能完成"