diff --git a/src/api/app/appCredential/index.ts b/src/api/app/appCredential/index.ts new file mode 100644 index 0000000..920a1c8 --- /dev/null +++ b/src/api/app/appCredential/index.ts @@ -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>>( + '/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>( + '/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>( + '/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>( + '/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>( + '/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>( + '/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>( + '/app/app-credential/' + id + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} diff --git a/src/api/app/appCredential/model/index.ts b/src/api/app/appCredential/model/index.ts new file mode 100644 index 0000000..c70c424 --- /dev/null +++ b/src/api/app/appCredential/model/index.ts @@ -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; +} diff --git a/src/api/app/appEvent/index.ts b/src/api/app/appEvent/index.ts new file mode 100644 index 0000000..34e6e58 --- /dev/null +++ b/src/api/app/appEvent/index.ts @@ -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>>( + '/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>( + '/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>( + '/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>( + '/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>( + '/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>( + '/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>( + '/app/app-event/' + id + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} diff --git a/src/api/app/appEvent/model/index.ts b/src/api/app/appEvent/model/index.ts new file mode 100644 index 0000000..7b53319 --- /dev/null +++ b/src/api/app/appEvent/model/index.ts @@ -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; +} diff --git a/src/api/app/appUser/index.ts b/src/api/app/appUser/index.ts new file mode 100644 index 0000000..1d2c72f --- /dev/null +++ b/src/api/app/appUser/index.ts @@ -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>>( + '/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>( + '/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>( + '/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>( + '/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>( + '/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>( + '/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>( + '/app/app-user/' + id + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} diff --git a/src/api/app/appUser/model/index.ts b/src/api/app/appUser/model/index.ts new file mode 100644 index 0000000..8f73f5e --- /dev/null +++ b/src/api/app/appUser/model/index.ts @@ -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; +} diff --git a/src/api/app/appVersion/index.ts b/src/api/app/appVersion/index.ts new file mode 100644 index 0000000..352cb1f --- /dev/null +++ b/src/api/app/appVersion/index.ts @@ -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>>( + '/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>( + '/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>( + '/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>( + '/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>( + '/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>( + '/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>( + '/app/app-version/' + id + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} diff --git a/src/api/app/appVersion/model/index.ts b/src/api/app/appVersion/model/index.ts new file mode 100644 index 0000000..18a1875 --- /dev/null +++ b/src/api/app/appVersion/model/index.ts @@ -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; +} diff --git a/src/api/passport/qr-login/index.ts b/src/api/passport/qr-login/index.ts index f76c47f..1a52c9f 100644 --- a/src/api/passport/qr-login/index.ts +++ b/src/api/passport/qr-login/index.ts @@ -105,7 +105,8 @@ export async function generateQRToken(data?: GenerateQRTokenParam) { ...data } ); - if (res.code === 0 && res.data) { + // 兼容 code === 0 和 code === 200 + if ((res.code === 0 || res.code === 200) && res.data) { return res.data; } return Promise.reject(new Error(res.message)); @@ -118,7 +119,8 @@ export async function checkQRLoginStatus(token: string) { const res = await request.get>( 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 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) { const res = await request.post>( SERVER_API_URL + '/qr-login/confirm', 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 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', 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 Promise.reject(new Error(res.message)); + return Promise.reject(new Error(res.message || '确认登录失败')); } catch (error: any) { return Promise.reject(new Error(error.message || '确认登录失败')); } diff --git a/src/app/appCredential/add.config.ts b/src/app/appCredential/add.config.ts new file mode 100644 index 0000000..7132df7 --- /dev/null +++ b/src/app/appCredential/add.config.ts @@ -0,0 +1,4 @@ +export default definePageConfig({ + navigationBarTitleText: '新增应用密钥凭证', + navigationBarTextStyle: 'black' +}) diff --git a/src/app/appCredential/add.tsx b/src/app/appCredential/add.tsx new file mode 100644 index 0000000..1326736 --- /dev/null +++ b/src/app/appCredential/add.tsx @@ -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(true) + const [FormData, setFormData] = useState({}) + const formRef = useRef(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 加载中 + } + + return ( + <> +
submitSucceed(values)} + onFinishFailed={(errors) => submitFailed(errors)} + footer={ +
+ +
+ } + > + + diff --git a/src/app/appCredential/index.config.ts b/src/app/appCredential/index.config.ts new file mode 100644 index 0000000..915b81a --- /dev/null +++ b/src/app/appCredential/index.config.ts @@ -0,0 +1,4 @@ +export default definePageConfig({ + navigationBarTitleText: '应用密钥凭证管理', + navigationBarTextStyle: 'black' +}) diff --git a/src/app/appCredential/index.tsx b/src/app/appCredential/index.tsx new file mode 100644 index 0000000..d96b6b9 --- /dev/null +++ b/src/app/appCredential/index.tsx @@ -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([]) + + 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 ( + +
+ + + + +
+
+ ) + } + + return ( + <> + {list.map((item, _) => ( + { + const {params} = useRouter(); + const [loading, setLoading] = useState(true) + const [FormData, setFormData] = useState({}) + const formRef = useRef(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 加载中 + } + + return ( + <> + submitSucceed(values)} + onFinishFailed={(errors) => submitFailed(errors)} + footer={ +
+ +
+ } + > + + diff --git a/src/app/appEvent/index.config.ts b/src/app/appEvent/index.config.ts new file mode 100644 index 0000000..d4b8fd3 --- /dev/null +++ b/src/app/appEvent/index.config.ts @@ -0,0 +1,4 @@ +export default definePageConfig({ + navigationBarTitleText: '应用操作动态管理', + navigationBarTextStyle: 'black' +}) diff --git a/src/app/appEvent/index.tsx b/src/app/appEvent/index.tsx new file mode 100644 index 0000000..5a0886b --- /dev/null +++ b/src/app/appEvent/index.tsx @@ -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([]) + + 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 ( + +
+ + + + +
+
+ ) + } + + return ( + <> + {list.map((item, _) => ( + { + const {params} = useRouter(); + const [loading, setLoading] = useState(true) + const [FormData, setFormData] = useState({}) + const formRef = useRef(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 加载中 + } + + return ( + <> + submitSucceed(values)} + onFinishFailed={(errors) => submitFailed(errors)} + footer={ +
+ +
+ } + > + + diff --git a/src/app/appUser/index.config.ts b/src/app/appUser/index.config.ts new file mode 100644 index 0000000..53812f4 --- /dev/null +++ b/src/app/appUser/index.config.ts @@ -0,0 +1,4 @@ +export default definePageConfig({ + navigationBarTitleText: '应用成员管理', + navigationBarTextStyle: 'black' +}) diff --git a/src/app/appUser/index.tsx b/src/app/appUser/index.tsx new file mode 100644 index 0000000..792d37c --- /dev/null +++ b/src/app/appUser/index.tsx @@ -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([]) + + 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 ( + +
+ + + + +
+
+ ) + } + + return ( + <> + {list.map((item, _) => ( + { + const {params} = useRouter(); + const [loading, setLoading] = useState(true) + const [FormData, setFormData] = useState({}) + const formRef = useRef(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 加载中 + } + + return ( + <> + submitSucceed(values)} + onFinishFailed={(errors) => submitFailed(errors)} + footer={ +
+ +
+ } + > + + diff --git a/src/app/appVersion/index.config.ts b/src/app/appVersion/index.config.ts new file mode 100644 index 0000000..5fa5510 --- /dev/null +++ b/src/app/appVersion/index.config.ts @@ -0,0 +1,4 @@ +export default definePageConfig({ + navigationBarTitleText: '应用版本发布记录管理', + navigationBarTextStyle: 'black' +}) diff --git a/src/app/appVersion/index.tsx b/src/app/appVersion/index.tsx new file mode 100644 index 0000000..9d9fd3d --- /dev/null +++ b/src/app/appVersion/index.tsx @@ -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([]) + + 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 ( + +
+ + + + +
+
+ ) + } + + return ( + <> + {list.map((item, _) => ( + { const router = useRouter(); - const { user, getDisplayName } = useUser(); + const { user, getDisplayName, isLoggedIn } = useUser(); const [loading, setLoading] = useState(false); const [confirmed, setConfirmed] = useState(false); const [error, setError] = useState(''); const [token, setToken] = useState(''); + const [loginMethod, setLoginMethod] = useState<'scan' | 'url'>('url'); useEffect(() => { - // 从URL参数中获取token - const { qrCodeKey, token: urlToken } = router.params; - const loginToken = qrCodeKey || urlToken; + // 从 URL 参数中获取 token + const params = router.params; + + // 兼容多种参数名 + // 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) { setToken(loginToken); + console.log('[QRConfirm] 获取到 token:', loginToken); + + // URL 扫码场景:自动确认登录 + if (!params.qrCodeKey && !params.token) { + // 如果不是直接参数,说明是 URL 扫码,自动确认 + setTimeout(() => { + handleAutoConfirm(loginToken); + }, 500); + } } else { setError('无效的登录链接'); } }, [router.params]); - // 确认登录 - const handleConfirmLogin = async () => { - if (!token) { + /** + * 自动确认登录(URL 扫码场景) + */ + 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'); return; } if (!user?.userId) { setError('请先登录小程序'); + Taro.showToast({ + title: '请先登录小程序', + icon: 'none' + }); + + setTimeout(() => { + Taro.redirectTo({ + url: '/passport/login' + }); + }, 1500); return; } @@ -47,7 +131,7 @@ const QRConfirmPage: React.FC = () => { setError(''); const result = await confirmQRLogin({ - token, + token: confirmToken, userId: user.userId, platform: 'wechat', wechatInfo: { @@ -64,81 +148,212 @@ const QRConfirmPage: React.FC = () => { duration: 2000 }); - // 3秒后自动返回 + // 3秒后自动关闭或返回 setTimeout(() => { - Taro.navigateBack(); + // 尝试返回上一页,如果没有则关闭 + const pages = Taro.getCurrentPages(); + if (pages.length > 1) { + Taro.navigateBack(); + } else { + // 小程序场景下,提示用户回到 PC 端 + Taro.showModal({ + title: '登录成功', + content: '请回到电脑端刷新页面', + showCancel: false, + confirmText: '我知道了' + }); + } }, 3000); } else { setError(result.message || '登录确认失败'); } } catch (err: any) { + console.error('[QRConfirm] 确认登录失败:', err); setError(err.message || '登录确认失败'); } finally { 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 = () => { setError(''); setConfirmed(false); 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 = () => ( + + + + + + ); + + // 渲染状态:成功 + const renderSuccess = () => ( + + + + + + ); + + // 渲染状态:错误 + const renderError = () => ( + + + + + + ); + + // 渲染状态:初始(用户未扫码) + const renderInitial = () => ( + + + + + + ); + + // 获取标题 + 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 ( - + + {/* Logo/品牌区域 */} + + + 🔐 + + WebSoft Platform + + {/* 主要内容卡片 */} - + - {/* 图标 */} - - {loading ? ( - - - - ) : confirmed ? ( - - - - ) : error ? ( - - - - ) : ( - - - - )} - + {/* 状态图标 */} + {loading ? renderLoading() : confirmed ? renderSuccess() : error ? renderError() : renderInitial()} {/* 标题 */} - {loading ? '正在确认登录...' : - confirmed ? '登录确认成功' : - error ? '登录确认失败' : '确认登录'} + {getTitle()} {/* 描述 */} - - {loading ? '请稍候,正在为您确认登录' : - confirmed ? '您已成功确认登录,网页端将自动登录' : - error ? error : - `确认使用 ${getDisplayName()} 登录网页端?`} + + {getDescription()} {/* 用户信息 */} {!loading && !confirmed && !error && user && ( - + - - - + {user.avatar ? ( + + ) : ( + + + + )} {user.nickname || user.username || '用户'} @@ -151,6 +366,15 @@ const QRConfirmPage: React.FC = () => { )} + {/* Token 信息 */} + {token && !loading && !confirmed && ( + + + 登录令牌:{token.substring(0, 20)}...{token.substring(token.length - 10)} + + + )} + {/* 操作按钮 */} {loading ? ( @@ -158,7 +382,7 @@ const QRConfirmPage: React.FC = () => { type="default" size="large" disabled - className="w-full" + className="w-full rounded-xl" > 确认中... @@ -167,7 +391,7 @@ const QRConfirmPage: React.FC = () => { type="success" size="large" onClick={handleCancel} - className="w-full" + className="w-full rounded-xl" > 完成 @@ -177,27 +401,36 @@ const QRConfirmPage: React.FC = () => { type="primary" size="large" onClick={handleRetry} - className="w-full" + className="w-full rounded-xl" > 重试 + - ) : ( - + ) : loginMethod === 'scan' ? ( + @@ -205,18 +438,29 @@ const QRConfirmPage: React.FC = () => { type="default" size="large" onClick={handleCancel} - className="w-full" + className="w-full rounded-xl" + fill="none" > 取消 + ) : ( + // URL 扫码场景:自动确认中 + )} {/* 安全提示 */} - + @@ -231,6 +475,13 @@ const QRConfirmPage: React.FC = () => { + + {/* 底部说明 */} + + + 如有问题,请联系管理员 + + ); diff --git a/src/rider/index.config.ts b/src/rider/index.config.ts new file mode 100644 index 0000000..7472e5e --- /dev/null +++ b/src/rider/index.config.ts @@ -0,0 +1,4 @@ +export default definePageConfig({ + navigationBarTitleText: '骑手中心' +}) + diff --git a/src/rider/index.tsx b/src/rider/index.tsx new file mode 100644 index 0000000..42ad0f7 --- /dev/null +++ b/src/rider/index.tsx @@ -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 ( + + + 请先登录后再进入骑手中心 + + + + + + ) + } + + return ( + + + + } + className="mr-4" + style={{border: '2px solid rgba(255, 255, 255, 0.3)'}} + /> + + {getDisplayName()} + + 骑手中心 + + + + + + + + 常用功能 + + + + + + + ) +} + +export default RiderIndex + diff --git a/src/rider/orders/index.config.ts b/src/rider/orders/index.config.ts new file mode 100644 index 0000000..c4ea1b0 --- /dev/null +++ b/src/rider/orders/index.config.ts @@ -0,0 +1,5 @@ +export default definePageConfig({ + navigationBarTitleText: '骑手订单', + navigationBarTextStyle: 'black' +}) + diff --git a/src/rider/orders/index.tsx b/src/rider/orders/index.tsx new file mode 100644 index 0000000..70f62e2 --- /dev/null +++ b/src/rider/orders/index.tsx @@ -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 ( + + + 请先登录 + + + + + + ) + } + + if (!riderId) { + return ( + + + 未获取到骑手信息(UserId) + + + ) + } + + return ( + + + + + + ) +} +