feat(passport): 优化扫码登录兼容性与确认交互
- 兼容接口中 code 返回 0 和 200 两种成功状态 - 在扫码登录确认接口添加日志输出,调试响应数据 - 重构扫码登录确认页面逻辑,支持主动扫码和URL扫码两种场景 - 兼容多种token参数名,支持URL编码和旧参数解析 - URL扫码场景自动确认登录,未登录用户自动跳转登录页 - 新增主动扫码功能,支持二维码内容多格式解析(URL/JSON/纯token) - 优化确认登录后页面交互,支持自动返回或提示用户回PC端刷新 - 增加状态视觉反馈,包括加载、成功、失败及初始状态 - 优化UI细节,使用圆角样式及布局调整提升视觉体验 - 新增页面底部帮助提示文字,提升用户指引 - 新增多页面配置,设置导航栏标题及样式统一管理 - 新增应用密钥凭证、应用操作动态、应用成员、应用版本发布等增删改查功能模块及接口定义 - 新增对应页面表单组件,实现应用相关实体的新增和编辑功能
This commit is contained in:
101
src/api/app/appCredential/index.ts
Normal file
101
src/api/app/appCredential/index.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import request from '@/utils/request';
|
||||||
|
import type { ApiResult, PageResult } from '@/api/index';
|
||||||
|
import type { AppCredential, AppCredentialParam } from './model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询应用密钥凭证
|
||||||
|
*/
|
||||||
|
export async function pageAppCredential(params: AppCredentialParam) {
|
||||||
|
const res = await request.get<ApiResult<PageResult<AppCredential>>>(
|
||||||
|
'/app/app-credential/page',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询应用密钥凭证列表
|
||||||
|
*/
|
||||||
|
export async function listAppCredential(params?: AppCredentialParam) {
|
||||||
|
const res = await request.get<ApiResult<AppCredential[]>>(
|
||||||
|
'/app/app-credential',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加应用密钥凭证
|
||||||
|
*/
|
||||||
|
export async function addAppCredential(data: AppCredential) {
|
||||||
|
const res = await request.post<ApiResult<unknown>>(
|
||||||
|
'/app/app-credential',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改应用密钥凭证
|
||||||
|
*/
|
||||||
|
export async function updateAppCredential(data: AppCredential) {
|
||||||
|
const res = await request.put<ApiResult<unknown>>(
|
||||||
|
'/app/app-credential',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除应用密钥凭证
|
||||||
|
*/
|
||||||
|
export async function removeAppCredential(id?: number) {
|
||||||
|
const res = await request.del<ApiResult<unknown>>(
|
||||||
|
'/app/app-credential/' + id
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除应用密钥凭证
|
||||||
|
*/
|
||||||
|
export async function removeBatchAppCredential(data: (number | undefined)[]) {
|
||||||
|
const res = await request.del<ApiResult<unknown>>(
|
||||||
|
'/app/app-credential/batch',
|
||||||
|
{
|
||||||
|
data
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id查询应用密钥凭证
|
||||||
|
*/
|
||||||
|
export async function getAppCredential(id: number) {
|
||||||
|
const res = await request.get<ApiResult<AppCredential>>(
|
||||||
|
'/app/app-credential/' + id
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
49
src/api/app/appCredential/model/index.ts
Normal file
49
src/api/app/appCredential/model/index.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import type { PageParam } from '@/api/index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用密钥凭证
|
||||||
|
*/
|
||||||
|
export interface AppCredential {
|
||||||
|
// 自增ID
|
||||||
|
id?: string;
|
||||||
|
// 关联应用ID
|
||||||
|
websiteId?: string;
|
||||||
|
// 凭证名称,如生产环境密钥
|
||||||
|
name?: string;
|
||||||
|
// App ID(公开)
|
||||||
|
appId?: string;
|
||||||
|
// App Secret(加密存储)
|
||||||
|
appSecret?: string;
|
||||||
|
// 凭证类型: server/client/webhook
|
||||||
|
type?: string;
|
||||||
|
// 权限范围,空格分隔
|
||||||
|
scopes?: string;
|
||||||
|
// 到期时间,NULL=永不过期
|
||||||
|
expireTime?: string;
|
||||||
|
// 最后使用时间
|
||||||
|
lastUsedAt?: string;
|
||||||
|
//
|
||||||
|
remark?: string;
|
||||||
|
// 排序(数字越小越靠前)
|
||||||
|
sortNumber?: number;
|
||||||
|
// 状态, 0正常, 1冻结
|
||||||
|
status?: number;
|
||||||
|
// 是否删除, 0否, 1是
|
||||||
|
deleted?: number;
|
||||||
|
// 用户ID
|
||||||
|
userId?: number;
|
||||||
|
// 租户id
|
||||||
|
tenantId?: number;
|
||||||
|
// 创建时间
|
||||||
|
createTime?: string;
|
||||||
|
// 修改时间
|
||||||
|
updateTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用密钥凭证搜索条件
|
||||||
|
*/
|
||||||
|
export interface AppCredentialParam extends PageParam {
|
||||||
|
id?: number;
|
||||||
|
keywords?: string;
|
||||||
|
}
|
||||||
101
src/api/app/appEvent/index.ts
Normal file
101
src/api/app/appEvent/index.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import request from '@/utils/request';
|
||||||
|
import type { ApiResult, PageResult } from '@/api/index';
|
||||||
|
import type { AppEvent, AppEventParam } from './model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询应用操作动态
|
||||||
|
*/
|
||||||
|
export async function pageAppEvent(params: AppEventParam) {
|
||||||
|
const res = await request.get<ApiResult<PageResult<AppEvent>>>(
|
||||||
|
'/app/app-event/page',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询应用操作动态列表
|
||||||
|
*/
|
||||||
|
export async function listAppEvent(params?: AppEventParam) {
|
||||||
|
const res = await request.get<ApiResult<AppEvent[]>>(
|
||||||
|
'/app/app-event',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加应用操作动态
|
||||||
|
*/
|
||||||
|
export async function addAppEvent(data: AppEvent) {
|
||||||
|
const res = await request.post<ApiResult<unknown>>(
|
||||||
|
'/app/app-event',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改应用操作动态
|
||||||
|
*/
|
||||||
|
export async function updateAppEvent(data: AppEvent) {
|
||||||
|
const res = await request.put<ApiResult<unknown>>(
|
||||||
|
'/app/app-event',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除应用操作动态
|
||||||
|
*/
|
||||||
|
export async function removeAppEvent(id?: number) {
|
||||||
|
const res = await request.del<ApiResult<unknown>>(
|
||||||
|
'/app/app-event/' + id
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除应用操作动态
|
||||||
|
*/
|
||||||
|
export async function removeBatchAppEvent(data: (number | undefined)[]) {
|
||||||
|
const res = await request.del<ApiResult<unknown>>(
|
||||||
|
'/app/app-event/batch',
|
||||||
|
{
|
||||||
|
data
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id查询应用操作动态
|
||||||
|
*/
|
||||||
|
export async function getAppEvent(id: number) {
|
||||||
|
const res = await request.get<ApiResult<AppEvent>>(
|
||||||
|
'/app/app-event/' + id
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
47
src/api/app/appEvent/model/index.ts
Normal file
47
src/api/app/appEvent/model/index.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import type { PageParam } from '@/api/index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用操作动态
|
||||||
|
*/
|
||||||
|
export interface AppEvent {
|
||||||
|
// 自增ID
|
||||||
|
id?: string;
|
||||||
|
// 关联应用ID
|
||||||
|
websiteId?: string;
|
||||||
|
// 事件类型: created/published/updated/domain_bound/member_added/status_changed
|
||||||
|
eventType?: string;
|
||||||
|
// 事件标题,如已发布
|
||||||
|
title?: string;
|
||||||
|
// 详细描述
|
||||||
|
content?: string;
|
||||||
|
// 操作人用户ID
|
||||||
|
operatorId?: string;
|
||||||
|
// 操作人名称(冗余)
|
||||||
|
operator?: string;
|
||||||
|
// 关联ID,如版本ID
|
||||||
|
refId?: string;
|
||||||
|
// 关联类型
|
||||||
|
refType?: string;
|
||||||
|
// 扩展数据
|
||||||
|
extra?: string;
|
||||||
|
// 排序(数字越小越靠前)
|
||||||
|
sortNumber?: number;
|
||||||
|
// 状态, 0正常, 1冻结
|
||||||
|
status?: number;
|
||||||
|
// 用户ID
|
||||||
|
userId?: number;
|
||||||
|
// 租户id
|
||||||
|
tenantId?: number;
|
||||||
|
// 创建时间
|
||||||
|
createTime?: string;
|
||||||
|
// 修改时间
|
||||||
|
updateTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用操作动态搜索条件
|
||||||
|
*/
|
||||||
|
export interface AppEventParam extends PageParam {
|
||||||
|
id?: number;
|
||||||
|
keywords?: string;
|
||||||
|
}
|
||||||
101
src/api/app/appUser/index.ts
Normal file
101
src/api/app/appUser/index.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import request from '@/utils/request';
|
||||||
|
import type { ApiResult, PageResult } from '@/api/index';
|
||||||
|
import type { AppUser, AppUserParam } from './model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询应用成员
|
||||||
|
*/
|
||||||
|
export async function pageAppUser(params: AppUserParam) {
|
||||||
|
const res = await request.get<ApiResult<PageResult<AppUser>>>(
|
||||||
|
'/app/app-user/page',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询应用成员列表
|
||||||
|
*/
|
||||||
|
export async function listAppUser(params?: AppUserParam) {
|
||||||
|
const res = await request.get<ApiResult<AppUser[]>>(
|
||||||
|
'/app/app-user',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加应用成员
|
||||||
|
*/
|
||||||
|
export async function addAppUser(data: AppUser) {
|
||||||
|
const res = await request.post<ApiResult<unknown>>(
|
||||||
|
'/app/app-user',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改应用成员
|
||||||
|
*/
|
||||||
|
export async function updateAppUser(data: AppUser) {
|
||||||
|
const res = await request.put<ApiResult<unknown>>(
|
||||||
|
'/app/app-user',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除应用成员
|
||||||
|
*/
|
||||||
|
export async function removeAppUser(id?: number) {
|
||||||
|
const res = await request.del<ApiResult<unknown>>(
|
||||||
|
'/app/app-user/' + id
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除应用成员
|
||||||
|
*/
|
||||||
|
export async function removeBatchAppUser(data: (number | undefined)[]) {
|
||||||
|
const res = await request.del<ApiResult<unknown>>(
|
||||||
|
'/app/app-user/batch',
|
||||||
|
{
|
||||||
|
data
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id查询应用成员
|
||||||
|
*/
|
||||||
|
export async function getAppUser(id: number) {
|
||||||
|
const res = await request.get<ApiResult<AppUser>>(
|
||||||
|
'/app/app-user/' + id
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
41
src/api/app/appUser/model/index.ts
Normal file
41
src/api/app/appUser/model/index.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import type { PageParam } from '@/api/index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用成员
|
||||||
|
*/
|
||||||
|
export interface AppUser {
|
||||||
|
// 自增ID
|
||||||
|
id?: string;
|
||||||
|
// 关联应用ID
|
||||||
|
websiteId?: string;
|
||||||
|
// 用户ID
|
||||||
|
userId?: string;
|
||||||
|
// 用户名(冗余)
|
||||||
|
username?: string;
|
||||||
|
// 头像(冗余)
|
||||||
|
avatar?: string;
|
||||||
|
// 角色: owner/admin/developer/viewer
|
||||||
|
role?: string;
|
||||||
|
// 邀请人用户ID
|
||||||
|
inviteBy?: string;
|
||||||
|
// 加入时间
|
||||||
|
inviteTime?: string;
|
||||||
|
// 排序(数字越小越靠前)
|
||||||
|
sortNumber?: number;
|
||||||
|
// 状态, 0正常, 1冻结
|
||||||
|
status?: number;
|
||||||
|
// 租户id
|
||||||
|
tenantId?: number;
|
||||||
|
// 创建时间
|
||||||
|
createTime?: string;
|
||||||
|
// 修改时间
|
||||||
|
updateTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用成员搜索条件
|
||||||
|
*/
|
||||||
|
export interface AppUserParam extends PageParam {
|
||||||
|
id?: number;
|
||||||
|
keywords?: string;
|
||||||
|
}
|
||||||
101
src/api/app/appVersion/index.ts
Normal file
101
src/api/app/appVersion/index.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import request from '@/utils/request';
|
||||||
|
import type { ApiResult, PageResult } from '@/api/index';
|
||||||
|
import type { AppVersion, AppVersionParam } from './model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询应用版本发布记录
|
||||||
|
*/
|
||||||
|
export async function pageAppVersion(params: AppVersionParam) {
|
||||||
|
const res = await request.get<ApiResult<PageResult<AppVersion>>>(
|
||||||
|
'/app/app-version/page',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询应用版本发布记录列表
|
||||||
|
*/
|
||||||
|
export async function listAppVersion(params?: AppVersionParam) {
|
||||||
|
const res = await request.get<ApiResult<AppVersion[]>>(
|
||||||
|
'/app/app-version',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加应用版本发布记录
|
||||||
|
*/
|
||||||
|
export async function addAppVersion(data: AppVersion) {
|
||||||
|
const res = await request.post<ApiResult<unknown>>(
|
||||||
|
'/app/app-version',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改应用版本发布记录
|
||||||
|
*/
|
||||||
|
export async function updateAppVersion(data: AppVersion) {
|
||||||
|
const res = await request.put<ApiResult<unknown>>(
|
||||||
|
'/app/app-version',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除应用版本发布记录
|
||||||
|
*/
|
||||||
|
export async function removeAppVersion(id?: number) {
|
||||||
|
const res = await request.del<ApiResult<unknown>>(
|
||||||
|
'/app/app-version/' + id
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除应用版本发布记录
|
||||||
|
*/
|
||||||
|
export async function removeBatchAppVersion(data: (number | undefined)[]) {
|
||||||
|
const res = await request.del<ApiResult<unknown>>(
|
||||||
|
'/app/app-version/batch',
|
||||||
|
{
|
||||||
|
data
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id查询应用版本发布记录
|
||||||
|
*/
|
||||||
|
export async function getAppVersion(id: number) {
|
||||||
|
const res = await request.get<ApiResult<AppVersion>>(
|
||||||
|
'/app/app-version/' + id
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
53
src/api/app/appVersion/model/index.ts
Normal file
53
src/api/app/appVersion/model/index.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import type { PageParam } from '@/api/index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用版本发布记录
|
||||||
|
*/
|
||||||
|
export interface AppVersion {
|
||||||
|
// 自增ID
|
||||||
|
id?: string;
|
||||||
|
// 关联应用ID
|
||||||
|
websiteId?: string;
|
||||||
|
// 版本号,如 1.0.0
|
||||||
|
versionNo?: string;
|
||||||
|
// 版本名称
|
||||||
|
versionName?: string;
|
||||||
|
// 版本更新说明
|
||||||
|
changelog?: string;
|
||||||
|
// 安装包地址
|
||||||
|
packageUrl?: string;
|
||||||
|
// 包大小(字节)
|
||||||
|
packageSize?: string;
|
||||||
|
// 包MD5/SHA256
|
||||||
|
packageHash?: string;
|
||||||
|
// 环境: development/staging/production
|
||||||
|
env?: string;
|
||||||
|
// 状态 0=构建中 1=已发布 2=已回滚 3=构建失败
|
||||||
|
status?: number;
|
||||||
|
// 是否为当前版本
|
||||||
|
isCurrent?: string;
|
||||||
|
// 发布人用户ID
|
||||||
|
publishBy?: string;
|
||||||
|
// 发布时间
|
||||||
|
publishTime?: string;
|
||||||
|
// 备注
|
||||||
|
remark?: string;
|
||||||
|
// 排序(数字越小越靠前)
|
||||||
|
sortNumber?: number;
|
||||||
|
// 用户ID
|
||||||
|
userId?: number;
|
||||||
|
// 租户id
|
||||||
|
tenantId?: number;
|
||||||
|
// 创建时间
|
||||||
|
createTime?: string;
|
||||||
|
// 修改时间
|
||||||
|
updateTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用版本发布记录搜索条件
|
||||||
|
*/
|
||||||
|
export interface AppVersionParam extends PageParam {
|
||||||
|
id?: number;
|
||||||
|
keywords?: string;
|
||||||
|
}
|
||||||
@@ -105,7 +105,8 @@ export async function generateQRToken(data?: GenerateQRTokenParam) {
|
|||||||
...data
|
...data
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (res.code === 0 && res.data) {
|
// 兼容 code === 0 和 code === 200
|
||||||
|
if ((res.code === 0 || res.code === 200) && res.data) {
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
return Promise.reject(new Error(res.message));
|
return Promise.reject(new Error(res.message));
|
||||||
@@ -118,7 +119,8 @@ export async function checkQRLoginStatus(token: string) {
|
|||||||
const res = await request.get<ApiResult<QRLoginStatusResult>>(
|
const res = await request.get<ApiResult<QRLoginStatusResult>>(
|
||||||
SERVER_API_URL + `/qr-login/status/${token}`
|
SERVER_API_URL + `/qr-login/status/${token}`
|
||||||
);
|
);
|
||||||
if (res.code === 0 && res.data) {
|
// 兼容 code === 0 和 code === 200
|
||||||
|
if ((res.code === 0 || res.code === 200) && res.data) {
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
return Promise.reject(new Error(res.message));
|
return Promise.reject(new Error(res.message));
|
||||||
@@ -126,16 +128,22 @@ export async function checkQRLoginStatus(token: string) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 确认扫码登录(通用接口)
|
* 确认扫码登录(通用接口)
|
||||||
|
*
|
||||||
|
* @description 小程序端确认扫码登录
|
||||||
|
* @param data - 包含 token 和用户信息
|
||||||
*/
|
*/
|
||||||
export async function confirmQRLogin(data: ConfirmLoginParam) {
|
export async function confirmQRLogin(data: ConfirmLoginParam) {
|
||||||
const res = await request.post<ApiResult<ConfirmLoginResult>>(
|
const res = await request.post<ApiResult<ConfirmLoginResult>>(
|
||||||
SERVER_API_URL + '/qr-login/confirm',
|
SERVER_API_URL + '/qr-login/confirm',
|
||||||
data
|
data
|
||||||
);
|
);
|
||||||
if (res.code === 0 && res.data) {
|
console.log('[QRLogin API] confirmQRLogin 响应:', res);
|
||||||
|
|
||||||
|
// 兼容 code === 0 和 code === 200
|
||||||
|
if ((res.code === 0 || res.code === 200) && res.data) {
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
return Promise.reject(new Error(res.message));
|
return Promise.reject(new Error(res.message || '登录确认失败'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -162,10 +170,13 @@ export async function confirmWechatQRLogin(token: string, userId: number) {
|
|||||||
SERVER_API_URL + '/qr-login/confirm',
|
SERVER_API_URL + '/qr-login/confirm',
|
||||||
data
|
data
|
||||||
);
|
);
|
||||||
if (res.code === 0 && res.data) {
|
console.log('[QRLogin API] confirmWechatQRLogin 响应:', res);
|
||||||
|
|
||||||
|
// 兼容 code === 0 和 code === 200
|
||||||
|
if ((res.code === 0 || res.code === 200) && res.data) {
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
return Promise.reject(new Error(res.message));
|
return Promise.reject(new Error(res.message || '确认登录失败'));
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return Promise.reject(new Error(error.message || '确认登录失败'));
|
return Promise.reject(new Error(error.message || '确认登录失败'));
|
||||||
}
|
}
|
||||||
|
|||||||
4
src/app/appCredential/add.config.ts
Normal file
4
src/app/appCredential/add.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '新增应用密钥凭证',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
})
|
||||||
98
src/app/appCredential/add.tsx
Normal file
98
src/app/appCredential/add.tsx
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import {useEffect, useState, useRef} from "react";
|
||||||
|
import {useRouter} from '@tarojs/taro'
|
||||||
|
import {Button, Loading, CellGroup, Input, TextArea, Form} from '@nutui/nutui-react-taro'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import {View} from '@tarojs/components'
|
||||||
|
import {AppCredential} from "@/api/app/appCredential/model";
|
||||||
|
import {getAppCredential, listAppCredential, updateAppCredential, addAppCredential} from "@/api/app/appCredential";
|
||||||
|
|
||||||
|
const AddAppCredential = () => {
|
||||||
|
const {params} = useRouter();
|
||||||
|
const [loading, setLoading] = useState<boolean>(true)
|
||||||
|
const [FormData, setFormData] = useState<AppCredential>({})
|
||||||
|
const formRef = useRef<any>(null)
|
||||||
|
|
||||||
|
const reload = async () => {
|
||||||
|
if (params.id) {
|
||||||
|
const data = await getAppCredential(Number(params.id))
|
||||||
|
setFormData(data)
|
||||||
|
} else {
|
||||||
|
setFormData({})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const submitSucceed = async (values: any) => {
|
||||||
|
try {
|
||||||
|
if (params.id) {
|
||||||
|
// 编辑模式
|
||||||
|
await updateAppCredential({
|
||||||
|
...values,
|
||||||
|
id: Number(params.id)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 新增模式
|
||||||
|
await addAppCredential(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
Taro.showToast({
|
||||||
|
title: `操作成功`,
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
return Taro.navigateBack()
|
||||||
|
}, 1000)
|
||||||
|
} catch (error) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: `操作失败`,
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitFailed = (error: any) => {
|
||||||
|
console.log(error, 'err...')
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
reload().then(() => {
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <Loading className={'px-2'}>加载中</Loading>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form
|
||||||
|
ref={formRef}
|
||||||
|
divider
|
||||||
|
initialValues={FormData}
|
||||||
|
labelPosition="left"
|
||||||
|
onFinish={(values) => submitSucceed(values)}
|
||||||
|
onFinishFailed={(errors) => submitFailed(errors)}
|
||||||
|
footer={
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
nativeType="submit"
|
||||||
|
type="success"
|
||||||
|
size="large"
|
||||||
|
className={'w-full'}
|
||||||
|
block
|
||||||
|
>
|
||||||
|
{params.id ? '更新' : '保存'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<CellGroup style={{padding: '4px 0'}}>
|
||||||
|
<Form.Item name="websiteId" label="关联应用ID" initialValue={FormData.websiteId} required>
|
||||||
4
src/app/appCredential/index.config.ts
Normal file
4
src/app/appCredential/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '应用密钥凭证管理',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
})
|
||||||
64
src/app/appCredential/index.tsx
Normal file
64
src/app/appCredential/index.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import {useState} from "react";
|
||||||
|
import Taro, {useDidShow} from '@tarojs/taro'
|
||||||
|
import {Button, Cell, CellGroup, Space, Empty, ConfigProvider, Divider} from '@nutui/nutui-react-taro'
|
||||||
|
import {Dongdong, ArrowRight, CheckNormal, Checked} from '@nutui/icons-react-taro'
|
||||||
|
import {View} from '@tarojs/components'
|
||||||
|
import {AppCredential} from "@/api/app/appCredential/model";
|
||||||
|
import {listAppCredential, removeAppCredential, updateAppCredential} from "@/api/app/appCredential";
|
||||||
|
|
||||||
|
const AppCredentialList = () => {
|
||||||
|
const [list, setList] = useState<AppCredential[]>([])
|
||||||
|
|
||||||
|
const reload = () => {
|
||||||
|
listAppCredential({
|
||||||
|
// 添加查询条件
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
setList(data || [])
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '获取数据失败',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const onDel = async (id?: number) => {
|
||||||
|
await removeAppCredential(id)
|
||||||
|
Taro.showToast({
|
||||||
|
title: '删除成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
useDidShow(() => {
|
||||||
|
reload()
|
||||||
|
});
|
||||||
|
|
||||||
|
if (list.length == 0) {
|
||||||
|
return (
|
||||||
|
<ConfigProvider>
|
||||||
|
<div className={'h-full flex flex-col justify-center items-center'} style={{
|
||||||
|
height: 'calc(100vh - 300px)',
|
||||||
|
}}>
|
||||||
|
<Empty
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}}
|
||||||
|
description="暂无数据"
|
||||||
|
/>
|
||||||
|
<Space>
|
||||||
|
<Button onClick={() => Taro.navigateTo({url: '/app/appCredential/add'})}>新增应用密钥凭证</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</ConfigProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{list.map((item, _) => (
|
||||||
|
<Cell.Group key={item.
|
||||||
4
src/app/appEvent/add.config.ts
Normal file
4
src/app/appEvent/add.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '新增应用操作动态',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
})
|
||||||
98
src/app/appEvent/add.tsx
Normal file
98
src/app/appEvent/add.tsx
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import {useEffect, useState, useRef} from "react";
|
||||||
|
import {useRouter} from '@tarojs/taro'
|
||||||
|
import {Button, Loading, CellGroup, Input, TextArea, Form} from '@nutui/nutui-react-taro'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import {View} from '@tarojs/components'
|
||||||
|
import {AppEvent} from "@/api/app/appEvent/model";
|
||||||
|
import {getAppEvent, listAppEvent, updateAppEvent, addAppEvent} from "@/api/app/appEvent";
|
||||||
|
|
||||||
|
const AddAppEvent = () => {
|
||||||
|
const {params} = useRouter();
|
||||||
|
const [loading, setLoading] = useState<boolean>(true)
|
||||||
|
const [FormData, setFormData] = useState<AppEvent>({})
|
||||||
|
const formRef = useRef<any>(null)
|
||||||
|
|
||||||
|
const reload = async () => {
|
||||||
|
if (params.id) {
|
||||||
|
const data = await getAppEvent(Number(params.id))
|
||||||
|
setFormData(data)
|
||||||
|
} else {
|
||||||
|
setFormData({})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const submitSucceed = async (values: any) => {
|
||||||
|
try {
|
||||||
|
if (params.id) {
|
||||||
|
// 编辑模式
|
||||||
|
await updateAppEvent({
|
||||||
|
...values,
|
||||||
|
id: Number(params.id)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 新增模式
|
||||||
|
await addAppEvent(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
Taro.showToast({
|
||||||
|
title: `操作成功`,
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
return Taro.navigateBack()
|
||||||
|
}, 1000)
|
||||||
|
} catch (error) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: `操作失败`,
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitFailed = (error: any) => {
|
||||||
|
console.log(error, 'err...')
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
reload().then(() => {
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <Loading className={'px-2'}>加载中</Loading>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form
|
||||||
|
ref={formRef}
|
||||||
|
divider
|
||||||
|
initialValues={FormData}
|
||||||
|
labelPosition="left"
|
||||||
|
onFinish={(values) => submitSucceed(values)}
|
||||||
|
onFinishFailed={(errors) => submitFailed(errors)}
|
||||||
|
footer={
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
nativeType="submit"
|
||||||
|
type="success"
|
||||||
|
size="large"
|
||||||
|
className={'w-full'}
|
||||||
|
block
|
||||||
|
>
|
||||||
|
{params.id ? '更新' : '保存'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<CellGroup style={{padding: '4px 0'}}>
|
||||||
|
<Form.Item name="websiteId" label="关联应用ID" initialValue={FormData.websiteId} required>
|
||||||
4
src/app/appEvent/index.config.ts
Normal file
4
src/app/appEvent/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '应用操作动态管理',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
})
|
||||||
64
src/app/appEvent/index.tsx
Normal file
64
src/app/appEvent/index.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import {useState} from "react";
|
||||||
|
import Taro, {useDidShow} from '@tarojs/taro'
|
||||||
|
import {Button, Cell, CellGroup, Space, Empty, ConfigProvider, Divider} from '@nutui/nutui-react-taro'
|
||||||
|
import {Dongdong, ArrowRight, CheckNormal, Checked} from '@nutui/icons-react-taro'
|
||||||
|
import {View} from '@tarojs/components'
|
||||||
|
import {AppEvent} from "@/api/app/appEvent/model";
|
||||||
|
import {listAppEvent, removeAppEvent, updateAppEvent} from "@/api/app/appEvent";
|
||||||
|
|
||||||
|
const AppEventList = () => {
|
||||||
|
const [list, setList] = useState<AppEvent[]>([])
|
||||||
|
|
||||||
|
const reload = () => {
|
||||||
|
listAppEvent({
|
||||||
|
// 添加查询条件
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
setList(data || [])
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '获取数据失败',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const onDel = async (id?: number) => {
|
||||||
|
await removeAppEvent(id)
|
||||||
|
Taro.showToast({
|
||||||
|
title: '删除成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
useDidShow(() => {
|
||||||
|
reload()
|
||||||
|
});
|
||||||
|
|
||||||
|
if (list.length == 0) {
|
||||||
|
return (
|
||||||
|
<ConfigProvider>
|
||||||
|
<div className={'h-full flex flex-col justify-center items-center'} style={{
|
||||||
|
height: 'calc(100vh - 300px)',
|
||||||
|
}}>
|
||||||
|
<Empty
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}}
|
||||||
|
description="暂无数据"
|
||||||
|
/>
|
||||||
|
<Space>
|
||||||
|
<Button onClick={() => Taro.navigateTo({url: '/app/appEvent/add'})}>新增应用操作动态</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</ConfigProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{list.map((item, _) => (
|
||||||
|
<Cell.Group key={item.
|
||||||
4
src/app/appUser/add.config.ts
Normal file
4
src/app/appUser/add.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '新增应用成员',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
})
|
||||||
98
src/app/appUser/add.tsx
Normal file
98
src/app/appUser/add.tsx
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import {useEffect, useState, useRef} from "react";
|
||||||
|
import {useRouter} from '@tarojs/taro'
|
||||||
|
import {Button, Loading, CellGroup, Input, TextArea, Form} from '@nutui/nutui-react-taro'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import {View} from '@tarojs/components'
|
||||||
|
import {AppUser} from "@/api/app/appUser/model";
|
||||||
|
import {getAppUser, listAppUser, updateAppUser, addAppUser} from "@/api/app/appUser";
|
||||||
|
|
||||||
|
const AddAppUser = () => {
|
||||||
|
const {params} = useRouter();
|
||||||
|
const [loading, setLoading] = useState<boolean>(true)
|
||||||
|
const [FormData, setFormData] = useState<AppUser>({})
|
||||||
|
const formRef = useRef<any>(null)
|
||||||
|
|
||||||
|
const reload = async () => {
|
||||||
|
if (params.id) {
|
||||||
|
const data = await getAppUser(Number(params.id))
|
||||||
|
setFormData(data)
|
||||||
|
} else {
|
||||||
|
setFormData({})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const submitSucceed = async (values: any) => {
|
||||||
|
try {
|
||||||
|
if (params.id) {
|
||||||
|
// 编辑模式
|
||||||
|
await updateAppUser({
|
||||||
|
...values,
|
||||||
|
id: Number(params.id)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 新增模式
|
||||||
|
await addAppUser(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
Taro.showToast({
|
||||||
|
title: `操作成功`,
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
return Taro.navigateBack()
|
||||||
|
}, 1000)
|
||||||
|
} catch (error) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: `操作失败`,
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitFailed = (error: any) => {
|
||||||
|
console.log(error, 'err...')
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
reload().then(() => {
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <Loading className={'px-2'}>加载中</Loading>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form
|
||||||
|
ref={formRef}
|
||||||
|
divider
|
||||||
|
initialValues={FormData}
|
||||||
|
labelPosition="left"
|
||||||
|
onFinish={(values) => submitSucceed(values)}
|
||||||
|
onFinishFailed={(errors) => submitFailed(errors)}
|
||||||
|
footer={
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
nativeType="submit"
|
||||||
|
type="success"
|
||||||
|
size="large"
|
||||||
|
className={'w-full'}
|
||||||
|
block
|
||||||
|
>
|
||||||
|
{params.id ? '更新' : '保存'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<CellGroup style={{padding: '4px 0'}}>
|
||||||
|
<Form.Item name="websiteId" label="关联应用ID" initialValue={FormData.websiteId} required>
|
||||||
4
src/app/appUser/index.config.ts
Normal file
4
src/app/appUser/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '应用成员管理',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
})
|
||||||
64
src/app/appUser/index.tsx
Normal file
64
src/app/appUser/index.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import {useState} from "react";
|
||||||
|
import Taro, {useDidShow} from '@tarojs/taro'
|
||||||
|
import {Button, Cell, CellGroup, Space, Empty, ConfigProvider, Divider} from '@nutui/nutui-react-taro'
|
||||||
|
import {Dongdong, ArrowRight, CheckNormal, Checked} from '@nutui/icons-react-taro'
|
||||||
|
import {View} from '@tarojs/components'
|
||||||
|
import {AppUser} from "@/api/app/appUser/model";
|
||||||
|
import {listAppUser, removeAppUser, updateAppUser} from "@/api/app/appUser";
|
||||||
|
|
||||||
|
const AppUserList = () => {
|
||||||
|
const [list, setList] = useState<AppUser[]>([])
|
||||||
|
|
||||||
|
const reload = () => {
|
||||||
|
listAppUser({
|
||||||
|
// 添加查询条件
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
setList(data || [])
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '获取数据失败',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const onDel = async (id?: number) => {
|
||||||
|
await removeAppUser(id)
|
||||||
|
Taro.showToast({
|
||||||
|
title: '删除成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
useDidShow(() => {
|
||||||
|
reload()
|
||||||
|
});
|
||||||
|
|
||||||
|
if (list.length == 0) {
|
||||||
|
return (
|
||||||
|
<ConfigProvider>
|
||||||
|
<div className={'h-full flex flex-col justify-center items-center'} style={{
|
||||||
|
height: 'calc(100vh - 300px)',
|
||||||
|
}}>
|
||||||
|
<Empty
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}}
|
||||||
|
description="暂无数据"
|
||||||
|
/>
|
||||||
|
<Space>
|
||||||
|
<Button onClick={() => Taro.navigateTo({url: '/app/appUser/add'})}>新增应用成员</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</ConfigProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{list.map((item, _) => (
|
||||||
|
<Cell.Group key={item.
|
||||||
4
src/app/appVersion/add.config.ts
Normal file
4
src/app/appVersion/add.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '新增应用版本发布记录',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
})
|
||||||
98
src/app/appVersion/add.tsx
Normal file
98
src/app/appVersion/add.tsx
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import {useEffect, useState, useRef} from "react";
|
||||||
|
import {useRouter} from '@tarojs/taro'
|
||||||
|
import {Button, Loading, CellGroup, Input, TextArea, Form} from '@nutui/nutui-react-taro'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import {View} from '@tarojs/components'
|
||||||
|
import {AppVersion} from "@/api/app/appVersion/model";
|
||||||
|
import {getAppVersion, listAppVersion, updateAppVersion, addAppVersion} from "@/api/app/appVersion";
|
||||||
|
|
||||||
|
const AddAppVersion = () => {
|
||||||
|
const {params} = useRouter();
|
||||||
|
const [loading, setLoading] = useState<boolean>(true)
|
||||||
|
const [FormData, setFormData] = useState<AppVersion>({})
|
||||||
|
const formRef = useRef<any>(null)
|
||||||
|
|
||||||
|
const reload = async () => {
|
||||||
|
if (params.id) {
|
||||||
|
const data = await getAppVersion(Number(params.id))
|
||||||
|
setFormData(data)
|
||||||
|
} else {
|
||||||
|
setFormData({})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const submitSucceed = async (values: any) => {
|
||||||
|
try {
|
||||||
|
if (params.id) {
|
||||||
|
// 编辑模式
|
||||||
|
await updateAppVersion({
|
||||||
|
...values,
|
||||||
|
id: Number(params.id)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 新增模式
|
||||||
|
await addAppVersion(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
Taro.showToast({
|
||||||
|
title: `操作成功`,
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
return Taro.navigateBack()
|
||||||
|
}, 1000)
|
||||||
|
} catch (error) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: `操作失败`,
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitFailed = (error: any) => {
|
||||||
|
console.log(error, 'err...')
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
reload().then(() => {
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <Loading className={'px-2'}>加载中</Loading>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form
|
||||||
|
ref={formRef}
|
||||||
|
divider
|
||||||
|
initialValues={FormData}
|
||||||
|
labelPosition="left"
|
||||||
|
onFinish={(values) => submitSucceed(values)}
|
||||||
|
onFinishFailed={(errors) => submitFailed(errors)}
|
||||||
|
footer={
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
nativeType="submit"
|
||||||
|
type="success"
|
||||||
|
size="large"
|
||||||
|
className={'w-full'}
|
||||||
|
block
|
||||||
|
>
|
||||||
|
{params.id ? '更新' : '保存'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<CellGroup style={{padding: '4px 0'}}>
|
||||||
|
<Form.Item name="websiteId" label="关联应用ID" initialValue={FormData.websiteId} required>
|
||||||
4
src/app/appVersion/index.config.ts
Normal file
4
src/app/appVersion/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '应用版本发布记录管理',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
})
|
||||||
64
src/app/appVersion/index.tsx
Normal file
64
src/app/appVersion/index.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import {useState} from "react";
|
||||||
|
import Taro, {useDidShow} from '@tarojs/taro'
|
||||||
|
import {Button, Cell, CellGroup, Space, Empty, ConfigProvider, Divider} from '@nutui/nutui-react-taro'
|
||||||
|
import {Dongdong, ArrowRight, CheckNormal, Checked} from '@nutui/icons-react-taro'
|
||||||
|
import {View} from '@tarojs/components'
|
||||||
|
import {AppVersion} from "@/api/app/appVersion/model";
|
||||||
|
import {listAppVersion, removeAppVersion, updateAppVersion} from "@/api/app/appVersion";
|
||||||
|
|
||||||
|
const AppVersionList = () => {
|
||||||
|
const [list, setList] = useState<AppVersion[]>([])
|
||||||
|
|
||||||
|
const reload = () => {
|
||||||
|
listAppVersion({
|
||||||
|
// 添加查询条件
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
setList(data || [])
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '获取数据失败',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const onDel = async (id?: number) => {
|
||||||
|
await removeAppVersion(id)
|
||||||
|
Taro.showToast({
|
||||||
|
title: '删除成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
useDidShow(() => {
|
||||||
|
reload()
|
||||||
|
});
|
||||||
|
|
||||||
|
if (list.length == 0) {
|
||||||
|
return (
|
||||||
|
<ConfigProvider>
|
||||||
|
<div className={'h-full flex flex-col justify-center items-center'} style={{
|
||||||
|
height: 'calc(100vh - 300px)',
|
||||||
|
}}>
|
||||||
|
<Empty
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}}
|
||||||
|
description="暂无数据"
|
||||||
|
/>
|
||||||
|
<Space>
|
||||||
|
<Button onClick={() => Taro.navigateTo({url: '/app/appVersion/add'})}>新增应用版本发布记录</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</ConfigProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{list.map((item, _) => (
|
||||||
|
<Cell.Group key={item.
|
||||||
@@ -8,37 +8,121 @@ import { useUser } from '@/hooks/useUser';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 扫码登录确认页面
|
* 扫码登录确认页面
|
||||||
* 用于处理从二维码跳转过来的登录确认
|
*
|
||||||
|
* 支持两种场景:
|
||||||
|
* 1. 主动扫码:用户点击按钮调用微信扫码,扫描 PC 端二维码
|
||||||
|
* 2. URL 扫码(小程序码):用户扫描小程序码后,微信自动打开此页面
|
||||||
|
*
|
||||||
|
* URL 扫码场景:
|
||||||
|
* - 微信「扫普通链接二维码打开小程序」配置的二维码规则:`https://websopy.websoft.top/wx-scan/`
|
||||||
|
* - 扫码后 URL:`https://websopy.websoft.top/wx-scan?token=xxx`
|
||||||
|
* - 小程序接收到参数后自动确认登录
|
||||||
*/
|
*/
|
||||||
const QRConfirmPage: React.FC = () => {
|
const QRConfirmPage: React.FC = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { user, getDisplayName } = useUser();
|
const { user, getDisplayName, isLoggedIn } = useUser();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [confirmed, setConfirmed] = useState(false);
|
const [confirmed, setConfirmed] = useState(false);
|
||||||
const [error, setError] = useState<string>('');
|
const [error, setError] = useState<string>('');
|
||||||
const [token, setToken] = useState<string>('');
|
const [token, setToken] = useState<string>('');
|
||||||
|
const [loginMethod, setLoginMethod] = useState<'scan' | 'url'>('url');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 从URL参数中获取token
|
// 从 URL 参数中获取 token
|
||||||
const { qrCodeKey, token: urlToken } = router.params;
|
const params = router.params;
|
||||||
const loginToken = qrCodeKey || urlToken;
|
|
||||||
|
// 兼容多种参数名
|
||||||
|
// 1. 直接参数:?token=xxx
|
||||||
|
// 2. URL 编码参数:?q=xxx(扫普通链接二维码场景)
|
||||||
|
// 3. 旧版参数:?qrCodeKey=xxx
|
||||||
|
let loginToken = params.token || params.qrCodeKey || '';
|
||||||
|
|
||||||
|
// 如果是 q 参数(URL 编码的完整 URL),需要解析
|
||||||
|
if (params.q) {
|
||||||
|
try {
|
||||||
|
const decodedUrl = decodeURIComponent(params.q);
|
||||||
|
console.log('[QRConfirm] 解码后的 URL:', decodedUrl);
|
||||||
|
|
||||||
|
// 解析 token
|
||||||
|
const url = new URL(decodedUrl);
|
||||||
|
loginToken = url.searchParams.get('token') ||
|
||||||
|
url.searchParams.get('qrCodeKey') ||
|
||||||
|
'';
|
||||||
|
|
||||||
|
setLoginMethod('url');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[QRConfirm] 解析 q 参数失败:', e);
|
||||||
|
// 尝试直接使用 q 作为 token
|
||||||
|
loginToken = decodeURIComponent(params.q);
|
||||||
|
setLoginMethod('url');
|
||||||
|
}
|
||||||
|
} else if (loginToken) {
|
||||||
|
setLoginMethod('url');
|
||||||
|
}
|
||||||
|
|
||||||
if (loginToken) {
|
if (loginToken) {
|
||||||
setToken(loginToken);
|
setToken(loginToken);
|
||||||
|
console.log('[QRConfirm] 获取到 token:', loginToken);
|
||||||
|
|
||||||
|
// URL 扫码场景:自动确认登录
|
||||||
|
if (!params.qrCodeKey && !params.token) {
|
||||||
|
// 如果不是直接参数,说明是 URL 扫码,自动确认
|
||||||
|
setTimeout(() => {
|
||||||
|
handleAutoConfirm(loginToken);
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setError('无效的登录链接');
|
setError('无效的登录链接');
|
||||||
}
|
}
|
||||||
}, [router.params]);
|
}, [router.params]);
|
||||||
|
|
||||||
// 确认登录
|
/**
|
||||||
const handleConfirmLogin = async () => {
|
* 自动确认登录(URL 扫码场景)
|
||||||
if (!token) {
|
*/
|
||||||
|
const handleAutoConfirm = async (loginToken: string) => {
|
||||||
|
if (!isLoggedIn || !user?.userId) {
|
||||||
|
// 用户未登录,跳转到登录页面
|
||||||
|
console.log('[QRConfirm] 用户未登录,跳转到登录页');
|
||||||
|
Taro.showToast({
|
||||||
|
title: '请先登录小程序',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
Taro.redirectTo({
|
||||||
|
url: '/passport/login'
|
||||||
|
});
|
||||||
|
}, 2000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await handleConfirmLogin(loginToken);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确认登录
|
||||||
|
*/
|
||||||
|
const handleConfirmLogin = async (loginToken?: string) => {
|
||||||
|
const confirmToken = loginToken || token;
|
||||||
|
|
||||||
|
if (!confirmToken) {
|
||||||
setError('缺少登录token');
|
setError('缺少登录token');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user?.userId) {
|
if (!user?.userId) {
|
||||||
setError('请先登录小程序');
|
setError('请先登录小程序');
|
||||||
|
Taro.showToast({
|
||||||
|
title: '请先登录小程序',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
Taro.redirectTo({
|
||||||
|
url: '/passport/login'
|
||||||
|
});
|
||||||
|
}, 1500);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +131,7 @@ const QRConfirmPage: React.FC = () => {
|
|||||||
setError('');
|
setError('');
|
||||||
|
|
||||||
const result = await confirmQRLogin({
|
const result = await confirmQRLogin({
|
||||||
token,
|
token: confirmToken,
|
||||||
userId: user.userId,
|
userId: user.userId,
|
||||||
platform: 'wechat',
|
platform: 'wechat',
|
||||||
wechatInfo: {
|
wechatInfo: {
|
||||||
@@ -64,81 +148,212 @@ const QRConfirmPage: React.FC = () => {
|
|||||||
duration: 2000
|
duration: 2000
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3秒后自动返回
|
// 3秒后自动关闭或返回
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
Taro.navigateBack();
|
// 尝试返回上一页,如果没有则关闭
|
||||||
|
const pages = Taro.getCurrentPages();
|
||||||
|
if (pages.length > 1) {
|
||||||
|
Taro.navigateBack();
|
||||||
|
} else {
|
||||||
|
// 小程序场景下,提示用户回到 PC 端
|
||||||
|
Taro.showModal({
|
||||||
|
title: '登录成功',
|
||||||
|
content: '请回到电脑端刷新页面',
|
||||||
|
showCancel: false,
|
||||||
|
confirmText: '我知道了'
|
||||||
|
});
|
||||||
|
}
|
||||||
}, 3000);
|
}, 3000);
|
||||||
} else {
|
} else {
|
||||||
setError(result.message || '登录确认失败');
|
setError(result.message || '登录确认失败');
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
console.error('[QRConfirm] 确认登录失败:', err);
|
||||||
setError(err.message || '登录确认失败');
|
setError(err.message || '登录确认失败');
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 取消登录
|
/**
|
||||||
const handleCancel = () => {
|
* 手动确认登录(主动扫码场景)
|
||||||
Taro.navigateBack();
|
*/
|
||||||
|
const handleManualConfirm = () => {
|
||||||
|
handleConfirmLogin();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 重试
|
/**
|
||||||
|
* 取消登录
|
||||||
|
*/
|
||||||
|
const handleCancel = () => {
|
||||||
|
const pages = Taro.getCurrentPages();
|
||||||
|
if (pages.length > 1) {
|
||||||
|
Taro.navigateBack();
|
||||||
|
} else {
|
||||||
|
Taro.switchTab({
|
||||||
|
url: '/pages/user/user'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重试
|
||||||
|
*/
|
||||||
const handleRetry = () => {
|
const handleRetry = () => {
|
||||||
setError('');
|
setError('');
|
||||||
setConfirmed(false);
|
setConfirmed(false);
|
||||||
handleConfirmLogin();
|
handleConfirmLogin();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开微信扫码
|
||||||
|
*/
|
||||||
|
const handleScan = () => {
|
||||||
|
Taro.scanCode({
|
||||||
|
success: async (res) => {
|
||||||
|
console.log('[QRConfirm] 扫码成功:', res);
|
||||||
|
|
||||||
|
// 解析二维码内容
|
||||||
|
let scanToken = '';
|
||||||
|
const qrContent = res.result;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 尝试解析 URL
|
||||||
|
if (qrContent.includes('http')) {
|
||||||
|
const url = new URL(qrContent);
|
||||||
|
scanToken = url.searchParams.get('token') ||
|
||||||
|
url.searchParams.get('qrCodeKey') ||
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试解析 JSON
|
||||||
|
if (!scanToken && qrContent.startsWith('{')) {
|
||||||
|
const parsed = JSON.parse(qrContent);
|
||||||
|
scanToken = parsed.token || parsed.qrCodeKey || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接作为 token
|
||||||
|
if (!scanToken && qrContent.length >= 32) {
|
||||||
|
scanToken = qrContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scanToken) {
|
||||||
|
setToken(scanToken);
|
||||||
|
setLoginMethod('scan');
|
||||||
|
handleConfirmLogin(scanToken);
|
||||||
|
} else {
|
||||||
|
setError('无效的二维码内容');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[QRConfirm] 解析二维码失败:', e);
|
||||||
|
setError('二维码解析失败');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('[QRConfirm] 扫码失败:', err);
|
||||||
|
if (err.errMsg !== 'scanCode:fail cancel') {
|
||||||
|
setError('扫码失败,请重试');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 渲染状态:加载中
|
||||||
|
const renderLoading = () => (
|
||||||
|
<View className="mb-6">
|
||||||
|
<View className="w-16 h-16 mx-auto flex items-center justify-center">
|
||||||
|
<Loading className="text-blue-500" />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
// 渲染状态:成功
|
||||||
|
const renderSuccess = () => (
|
||||||
|
<View className="mb-6">
|
||||||
|
<View className="w-16 h-16 mx-auto bg-green-100 rounded-full flex items-center justify-center">
|
||||||
|
<Success className="text-green-500" size="32" />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
// 渲染状态:错误
|
||||||
|
const renderError = () => (
|
||||||
|
<View className="mb-6">
|
||||||
|
<View className="w-16 h-16 mx-auto bg-red-100 rounded-full flex items-center justify-center">
|
||||||
|
<Failure className="text-red-500" size="32" />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
// 渲染状态:初始(用户未扫码)
|
||||||
|
const renderInitial = () => (
|
||||||
|
<View className="mb-6">
|
||||||
|
<View className="w-16 h-16 mx-auto bg-blue-100 rounded-full flex items-center justify-center">
|
||||||
|
<User size="32" className="text-blue-500" />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
// 获取标题
|
||||||
|
const getTitle = () => {
|
||||||
|
if (loading) return '正在确认登录...';
|
||||||
|
if (confirmed) return '登录确认成功';
|
||||||
|
if (error) return '登录确认失败';
|
||||||
|
return loginMethod === 'url' ? '扫码登录确认' : '确认登录';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取描述
|
||||||
|
const getDescription = () => {
|
||||||
|
if (loading) return '请稍候,正在为您确认登录';
|
||||||
|
if (confirmed) return '您已成功确认登录,网页端将自动登录';
|
||||||
|
if (error) return error;
|
||||||
|
if (loginMethod === 'url') {
|
||||||
|
return '检测到登录请求,是否确认登录?';
|
||||||
|
}
|
||||||
|
return `确认使用 ${getDisplayName()} 登录网页端?`;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="qr-confirm-page min-h-screen bg-gray-50">
|
<View className="qr-confirm-page min-h-screen bg-gradient-to-b from-blue-50 to-white">
|
||||||
<View className="p-4">
|
<View className="p-4">
|
||||||
|
{/* Logo/品牌区域 */}
|
||||||
|
<View className="text-center pt-8 pb-6">
|
||||||
|
<View className="w-20 h-20 mx-auto bg-white rounded-2xl shadow-lg flex items-center justify-center mb-4">
|
||||||
|
<Text className="text-3xl">🔐</Text>
|
||||||
|
</View>
|
||||||
|
<Text className="text-gray-400 text-sm">WebSoft Platform</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
{/* 主要内容卡片 */}
|
{/* 主要内容卡片 */}
|
||||||
<Card className="bg-white rounded-lg shadow-sm">
|
<Card className="bg-white rounded-2xl shadow-xl -mt-4">
|
||||||
<View className="p-6 text-center">
|
<View className="p-6 text-center">
|
||||||
{/* 图标 */}
|
{/* 状态图标 */}
|
||||||
<View className="mb-6">
|
{loading ? renderLoading() : confirmed ? renderSuccess() : error ? renderError() : renderInitial()}
|
||||||
{loading ? (
|
|
||||||
<View className="w-16 h-16 mx-auto flex items-center justify-center">
|
|
||||||
<Loading className="text-blue-500" />
|
|
||||||
</View>
|
|
||||||
) : confirmed ? (
|
|
||||||
<View className="w-16 h-16 mx-auto bg-green-100 rounded-full flex items-center justify-center">
|
|
||||||
<Success className="text-green-500" />
|
|
||||||
</View>
|
|
||||||
) : error ? (
|
|
||||||
<View className="w-16 h-16 mx-auto bg-red-100 rounded-full flex items-center justify-center">
|
|
||||||
<Failure className="text-red-500" />
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<View className="w-16 h-16 mx-auto bg-blue-100 rounded-full flex items-center justify-center">
|
|
||||||
<User size="32" className="text-blue-500" />
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* 标题 */}
|
{/* 标题 */}
|
||||||
<Text className="text-xl font-bold text-gray-800 mb-2 block">
|
<Text className="text-xl font-bold text-gray-800 mb-2 block">
|
||||||
{loading ? '正在确认登录...' :
|
{getTitle()}
|
||||||
confirmed ? '登录确认成功' :
|
|
||||||
error ? '登录确认失败' : '确认登录'}
|
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{/* 描述 */}
|
{/* 描述 */}
|
||||||
<Text className="text-gray-600 mb-6 block">
|
<Text className="text-gray-600 mb-6 block text-sm">
|
||||||
{loading ? '请稍候,正在为您确认登录' :
|
{getDescription()}
|
||||||
confirmed ? '您已成功确认登录,网页端将自动登录' :
|
|
||||||
error ? error :
|
|
||||||
`确认使用 ${getDisplayName()} 登录网页端?`}
|
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{/* 用户信息 */}
|
{/* 用户信息 */}
|
||||||
{!loading && !confirmed && !error && user && (
|
{!loading && !confirmed && !error && user && (
|
||||||
<View className="bg-gray-50 rounded-lg p-4 mb-6">
|
<View className="bg-gray-50 rounded-xl p-4 mb-6">
|
||||||
<View className="flex items-center justify-center">
|
<View className="flex items-center justify-center">
|
||||||
<View className="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center mr-3">
|
{user.avatar ? (
|
||||||
<User className="text-blue-500" size="20" />
|
<View
|
||||||
</View>
|
className="w-12 h-12 rounded-full bg-blue-100 mr-3 overflow-hidden"
|
||||||
|
style={{ backgroundImage: `url(${user.avatar})`, backgroundSize: 'cover' }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<View className="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center mr-3">
|
||||||
|
<User className="text-blue-500" size="20" />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
<View className="text-left">
|
<View className="text-left">
|
||||||
<Text className="text-sm font-medium text-gray-800 block">
|
<Text className="text-sm font-medium text-gray-800 block">
|
||||||
{user.nickname || user.username || '用户'}
|
{user.nickname || user.username || '用户'}
|
||||||
@@ -151,6 +366,15 @@ const QRConfirmPage: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Token 信息 */}
|
||||||
|
{token && !loading && !confirmed && (
|
||||||
|
<View className="bg-blue-50 rounded-lg p-3 mb-4">
|
||||||
|
<Text className="text-xs text-blue-600">
|
||||||
|
登录令牌:{token.substring(0, 20)}...{token.substring(token.length - 10)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 操作按钮 */}
|
{/* 操作按钮 */}
|
||||||
<View className="space-y-3">
|
<View className="space-y-3">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
@@ -158,7 +382,7 @@ const QRConfirmPage: React.FC = () => {
|
|||||||
type="default"
|
type="default"
|
||||||
size="large"
|
size="large"
|
||||||
disabled
|
disabled
|
||||||
className="w-full"
|
className="w-full rounded-xl"
|
||||||
>
|
>
|
||||||
确认中...
|
确认中...
|
||||||
</Button>
|
</Button>
|
||||||
@@ -167,7 +391,7 @@ const QRConfirmPage: React.FC = () => {
|
|||||||
type="success"
|
type="success"
|
||||||
size="large"
|
size="large"
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
className="w-full"
|
className="w-full rounded-xl"
|
||||||
>
|
>
|
||||||
完成
|
完成
|
||||||
</Button>
|
</Button>
|
||||||
@@ -177,27 +401,36 @@ const QRConfirmPage: React.FC = () => {
|
|||||||
type="primary"
|
type="primary"
|
||||||
size="large"
|
size="large"
|
||||||
onClick={handleRetry}
|
onClick={handleRetry}
|
||||||
className="w-full"
|
className="w-full rounded-xl"
|
||||||
>
|
>
|
||||||
重试
|
重试
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="default"
|
type="default"
|
||||||
size="large"
|
size="large"
|
||||||
|
onClick={handleScan}
|
||||||
|
className="w-full rounded-xl"
|
||||||
|
>
|
||||||
|
扫码其他二维码
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="default"
|
||||||
|
size="small"
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
className="w-full"
|
className="w-full rounded-xl"
|
||||||
|
fill="none"
|
||||||
>
|
>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : loginMethod === 'scan' ? (
|
||||||
<View className="mt-3">
|
<View>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="large"
|
size="large"
|
||||||
onClick={handleConfirmLogin}
|
onClick={handleManualConfirm}
|
||||||
className="w-full mb-2"
|
className="w-full mb-2 rounded-xl"
|
||||||
disabled={!token || !user?.userId}
|
disabled={!token}
|
||||||
>
|
>
|
||||||
确认登录
|
确认登录
|
||||||
</Button>
|
</Button>
|
||||||
@@ -205,18 +438,29 @@ const QRConfirmPage: React.FC = () => {
|
|||||||
type="default"
|
type="default"
|
||||||
size="large"
|
size="large"
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
className="w-full"
|
className="w-full rounded-xl"
|
||||||
|
fill="none"
|
||||||
>
|
>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
) : (
|
||||||
|
// URL 扫码场景:自动确认中
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
onClick={handleManualConfirm}
|
||||||
|
className="w-full rounded-xl"
|
||||||
|
>
|
||||||
|
确认登录
|
||||||
|
</Button>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* 安全提示 */}
|
{/* 安全提示 */}
|
||||||
<Card className="bg-yellow-50 border border-yellow-200 rounded-lg mt-4">
|
<Card className="bg-yellow-50 border border-yellow-200 rounded-xl mt-4">
|
||||||
<View className="p-4">
|
<View className="p-4">
|
||||||
<View className="flex items-start">
|
<View className="flex items-start">
|
||||||
<Tips className="text-yellow-600 mr-2 mt-1" size="16" />
|
<Tips className="text-yellow-600 mr-2 mt-1" size="16" />
|
||||||
@@ -231,6 +475,13 @@ const QRConfirmPage: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{/* 底部说明 */}
|
||||||
|
<View className="text-center mt-6 pb-8">
|
||||||
|
<Text className="text-xs text-gray-400">
|
||||||
|
如有问题,请联系管理员
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
4
src/rider/index.config.ts
Normal file
4
src/rider/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '骑手中心'
|
||||||
|
})
|
||||||
|
|
||||||
63
src/rider/index.tsx
Normal file
63
src/rider/index.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import {View, Text} from '@tarojs/components'
|
||||||
|
import {Avatar, Button} from '@nutui/nutui-react-taro'
|
||||||
|
import {User} from '@nutui/icons-react-taro'
|
||||||
|
import {useThemeStyles} from '@/hooks/useTheme'
|
||||||
|
import {useUser} from '@/hooks/useUser'
|
||||||
|
|
||||||
|
const RiderIndex: React.FC = () => {
|
||||||
|
const themeStyles = useThemeStyles()
|
||||||
|
const {isLoggedIn, loading: userLoading, getAvatarUrl, getDisplayName} = useUser()
|
||||||
|
|
||||||
|
if (!isLoggedIn && !userLoading) {
|
||||||
|
return (
|
||||||
|
<View className="bg-gray-100 min-h-screen p-4">
|
||||||
|
<View className="bg-white rounded-xl p-4">
|
||||||
|
<Text className="text-gray-700">请先登录后再进入骑手中心</Text>
|
||||||
|
<View className="mt-3">
|
||||||
|
<Button type="primary" onClick={() => Taro.navigateTo({url: '/passport/login'})}>
|
||||||
|
去登录
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="bg-gray-100 min-h-screen">
|
||||||
|
<View className="px-4 py-6 relative overflow-hidden" style={themeStyles.primaryBackground}>
|
||||||
|
<View className="flex items-center justify-between relative z-10">
|
||||||
|
<Avatar
|
||||||
|
size="50"
|
||||||
|
src={getAvatarUrl()}
|
||||||
|
icon={<User />}
|
||||||
|
className="mr-4"
|
||||||
|
style={{border: '2px solid rgba(255, 255, 255, 0.3)'}}
|
||||||
|
/>
|
||||||
|
<View className="flex-1 flex-col">
|
||||||
|
<View className="text-white text-lg font-bold mb-1">{getDisplayName()}</View>
|
||||||
|
<View className="text-sm" style={{color: 'rgba(255, 255, 255, 0.8)'}}>
|
||||||
|
骑手中心
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="p-4">
|
||||||
|
<View className="bg-white rounded-xl p-4">
|
||||||
|
<Text className="text-gray-800 font-semibold">常用功能</Text>
|
||||||
|
<View className="mt-3">
|
||||||
|
<Button type="primary" block onClick={() => Taro.navigateTo({url: '/rider/orders/index'})}>
|
||||||
|
我的配送订单
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RiderIndex
|
||||||
|
|
||||||
5
src/rider/orders/index.config.ts
Normal file
5
src/rider/orders/index.config.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '骑手订单',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
})
|
||||||
|
|
||||||
50
src/rider/orders/index.tsx
Normal file
50
src/rider/orders/index.tsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import {useMemo} from 'react'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import {Button} from '@nutui/nutui-react-taro'
|
||||||
|
import {View, Text} from '@tarojs/components'
|
||||||
|
import OrderList from '@/user/order/components/OrderList'
|
||||||
|
|
||||||
|
export default function RiderOrders() {
|
||||||
|
const isLoggedIn = useMemo(() => {
|
||||||
|
return !!Taro.getStorageSync('access_token') && !!Taro.getStorageSync('UserId')
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const riderId = useMemo(() => {
|
||||||
|
const raw = Number(Taro.getStorageSync('UserId'))
|
||||||
|
return Number.isFinite(raw) && raw > 0 ? raw : undefined
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
return (
|
||||||
|
<View className="bg-gray-50 min-h-screen p-4">
|
||||||
|
<View className="bg-white rounded-lg p-4">
|
||||||
|
<Text className="text-sm text-gray-700">请先登录</Text>
|
||||||
|
<View className="mt-3">
|
||||||
|
<Button type="primary" size="small" onClick={() => Taro.navigateTo({url: '/passport/login'})}>
|
||||||
|
去登录
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!riderId) {
|
||||||
|
return (
|
||||||
|
<View className="bg-gray-50 min-h-screen p-4">
|
||||||
|
<View className="bg-white rounded-lg p-4">
|
||||||
|
<Text className="text-sm text-gray-700">未获取到骑手信息(UserId)</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="bg-gray-50 min-h-screen">
|
||||||
|
<View className="px-3">
|
||||||
|
<OrderList mode="rider" baseParams={{riderId}} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user