feat(passport): 优化扫码登录兼容性与确认交互

- 兼容接口中 code 返回 0 和 200 两种成功状态
- 在扫码登录确认接口添加日志输出,调试响应数据
- 重构扫码登录确认页面逻辑,支持主动扫码和URL扫码两种场景
- 兼容多种token参数名,支持URL编码和旧参数解析
- URL扫码场景自动确认登录,未登录用户自动跳转登录页
- 新增主动扫码功能,支持二维码内容多格式解析(URL/JSON/纯token)
- 优化确认登录后页面交互,支持自动返回或提示用户回PC端刷新
- 增加状态视觉反馈,包括加载、成功、失败及初始状态
- 优化UI细节,使用圆角样式及布局调整提升视觉体验
- 新增页面底部帮助提示文字,提升用户指引
- 新增多页面配置,设置导航栏标题及样式统一管理
- 新增应用密钥凭证、应用操作动态、应用成员、应用版本发布等增删改查功能模块及接口定义
- 新增对应页面表单组件,实现应用相关实体的新增和编辑功能
This commit is contained in:
2026-04-07 16:43:41 +08:00
parent dd9c1708fa
commit 8da0108f56
30 changed files with 1724 additions and 66 deletions

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '新增应用密钥凭证',
navigationBarTextStyle: 'black'
})

View 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>

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '应用密钥凭证管理',
navigationBarTextStyle: 'black'
})

View 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.

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '新增应用操作动态',
navigationBarTextStyle: 'black'
})

98
src/app/appEvent/add.tsx Normal file
View 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>

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '应用操作动态管理',
navigationBarTextStyle: 'black'
})

View 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.

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '新增应用成员',
navigationBarTextStyle: 'black'
})

98
src/app/appUser/add.tsx Normal file
View 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>

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '应用成员管理',
navigationBarTextStyle: 'black'
})

64
src/app/appUser/index.tsx Normal file
View 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.

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '新增应用版本发布记录',
navigationBarTextStyle: 'black'
})

View 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>

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '应用版本发布记录管理',
navigationBarTextStyle: 'black'
})

View 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.