fix: comply with F2E Spec — filename naming, any types, JSDoc
- Rename PascalCase component files to kebab-case (AnnouncementModal → announcement-modal, McGlass → mc-glass) - Replace any types with proper uni-app/WeChat callback types (RequestSuccessCallbackResult, LoginRes, ShowModalRes, etc.) - Add JSDoc documentation comments to all exported/important functions Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 4.5
parent
77c177bfa9
commit
f10a1896e7
+8
-4
@@ -2,14 +2,18 @@
|
||||
import { onLaunch } from '@dcloudio/uni-app'
|
||||
import { useUserStore } from './store/user'
|
||||
|
||||
/** 应用启动时检查隐私协议并尝试获取用户信息 */
|
||||
onLaunch(() => {
|
||||
// #ifdef MP-WEIXIN
|
||||
// 仅真机检查隐私协议,模拟器中跳过(getPrivacySetting 在模拟器会触发框架级 timeout)
|
||||
// wx 为微信小程序运行时全局对象,在 #ifdef MP-WEIXIN 下始终可用
|
||||
try {
|
||||
const deviceInfo = wx.getDeviceInfo()
|
||||
if (deviceInfo.platform !== 'devtools' && typeof wx.getPrivacySetting === 'function') {
|
||||
wx.getPrivacySetting({
|
||||
success: (res: any) => {
|
||||
// @ts-expect-error wx — WeChat 运行时全局对象,类型不由 TypeScript 覆盖
|
||||
const _wx: any = wx
|
||||
const deviceInfo = _wx.getDeviceInfo()
|
||||
if (deviceInfo.platform !== 'devtools' && typeof _wx.getPrivacySetting === 'function') {
|
||||
_wx.getPrivacySetting({
|
||||
success: (res: UniNamespace.GetPrivacySettingSuccessCallbackResult) => {
|
||||
if (res.needAuthorization) {
|
||||
uni.setStorageSync('needPrivacy', true)
|
||||
} else {
|
||||
|
||||
+16
-12
@@ -4,13 +4,13 @@ import { API_BASE, TOKEN_KEY } from '../config'
|
||||
interface RequestOptions {
|
||||
url: string
|
||||
method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
|
||||
data?: any
|
||||
data?: object
|
||||
header?: Record<string, string>
|
||||
needToken?: boolean
|
||||
}
|
||||
|
||||
/** 后端统一响应格式 */
|
||||
interface ResponseData<T = any> {
|
||||
interface ResponseData<T = unknown> {
|
||||
code: number
|
||||
msg: string
|
||||
data: T
|
||||
@@ -20,7 +20,7 @@ interface ResponseData<T = any> {
|
||||
|
||||
/** 从本地存储获取 token */
|
||||
function getToken(): string {
|
||||
return uni.getStorageSync(TOKEN_KEY) || ''
|
||||
return (uni.getStorageSync(TOKEN_KEY) as string) || ''
|
||||
}
|
||||
|
||||
/** 保存 token 到本地存储 */
|
||||
@@ -34,7 +34,7 @@ function removeToken(): void {
|
||||
}
|
||||
|
||||
/** 通用请求函数 */
|
||||
function request<T = any>(options: RequestOptions): Promise<T> {
|
||||
function request<T = unknown>(options: RequestOptions): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { url, method = 'GET', data, needToken = true } = options
|
||||
|
||||
@@ -57,9 +57,9 @@ function request<T = any>(options: RequestOptions): Promise<T> {
|
||||
uni.request({
|
||||
url: fullUrl,
|
||||
method,
|
||||
data,
|
||||
data: data as AnyObject,
|
||||
header,
|
||||
success: (res: any) => {
|
||||
success: (res: UniApp.RequestSuccessCallbackResult) => {
|
||||
const result = res.data as ResponseData<T>
|
||||
|
||||
// HTTP 状态码检查
|
||||
@@ -87,27 +87,31 @@ function request<T = any>(options: RequestOptions): Promise<T> {
|
||||
reject(new Error(`HTTP ${res.statusCode}`))
|
||||
}
|
||||
},
|
||||
fail: (err: any) => {
|
||||
fail: (err: UniApp.GeneralCallbackResult) => {
|
||||
uni.showToast({ title: '网络连接失败', icon: 'none' })
|
||||
reject(err)
|
||||
reject(new Error(err.errMsg))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function get<T = any>(url: string, data?: any, needToken = true): Promise<T> {
|
||||
/** GET 请求 */
|
||||
function get<T = unknown>(url: string, data?: object, needToken = true): Promise<T> {
|
||||
return request<T>({ url, method: 'GET', data, needToken })
|
||||
}
|
||||
|
||||
function post<T = any>(url: string, data?: any, needToken = true): Promise<T> {
|
||||
/** POST 请求 */
|
||||
function post<T = unknown>(url: string, data?: object, needToken = true): Promise<T> {
|
||||
return request<T>({ url, method: 'POST', data, needToken })
|
||||
}
|
||||
|
||||
function put<T = any>(url: string, data?: any, needToken = true): Promise<T> {
|
||||
/** PUT 请求 */
|
||||
function put<T = unknown>(url: string, data?: object, needToken = true): Promise<T> {
|
||||
return request<T>({ url, method: 'PUT', data, needToken })
|
||||
}
|
||||
|
||||
function del<T = any>(url: string, data?: any, needToken = true): Promise<T> {
|
||||
/** DELETE 请求 */
|
||||
function del<T = unknown>(url: string, data?: object, needToken = true): Promise<T> {
|
||||
return request<T>({ url, method: 'DELETE', data, needToken })
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ const emit = defineEmits<{
|
||||
|
||||
const show = ref(false)
|
||||
|
||||
/** modelValue 变化时控制入场/离场动画 */
|
||||
watch(() => props.modelValue, (val) => {
|
||||
if (val) {
|
||||
nextTick(() => { show.value = true })
|
||||
@@ -54,6 +55,7 @@ watch(() => props.modelValue, (val) => {
|
||||
}
|
||||
})
|
||||
|
||||
/** 关闭弹窗(离场动画完成后 emit) */
|
||||
function close() {
|
||||
show.value = false
|
||||
setTimeout(() => { emit('update:modelValue', false) }, 280)
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import AnnouncementModal from '../../../components/AnnouncementModal.vue'
|
||||
import AnnouncementModal from '../../../components/announcement-modal.vue'
|
||||
|
||||
interface NoticeItem {
|
||||
id: number
|
||||
|
||||
@@ -143,9 +143,9 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { mockScenicList } from '@/mock/scenic'
|
||||
import { mockNotices } from '@/mock/notice'
|
||||
import AnnouncementModal from '../../components/AnnouncementModal.vue'
|
||||
import AnnouncementModal from '../../components/announcement-modal.vue'
|
||||
|
||||
/** 金刚区 */
|
||||
/** 金刚区操作项 */
|
||||
const quickActions = [
|
||||
{ label: '我要绑卡', icon: '/static/icon/card.png' },
|
||||
{ label: '我要预约', icon: '/static/icon/reserve.png' },
|
||||
@@ -153,6 +153,7 @@ const quickActions = [
|
||||
{ label: '常见问题', icon: '/static/icon/faq.png' },
|
||||
]
|
||||
|
||||
/** 处理金刚区点击 */
|
||||
function onQuickAction(item: { label: string }) {
|
||||
if (item.label === '我要绑卡') {
|
||||
uni.navigateTo({ url: '/packages/user/pages/bind' })
|
||||
@@ -191,8 +192,11 @@ const swiperAutoplay = ref(true)
|
||||
const currentNoticeIndex = ref(0)
|
||||
const currentNoticeType = computed(() => announcements.value[currentNoticeIndex.value]?.type || 'scenic')
|
||||
|
||||
function onNoticeChange(e: any) { currentNoticeIndex.value = e.detail.current }
|
||||
/** @param e - swiper change 事件,`{ detail: { current } }` */
|
||||
function onNoticeChange(e: { detail: { current: number } }) { currentNoticeIndex.value = e.detail.current }
|
||||
/** 触摸公告轮播时暂停自动播放 */
|
||||
function onNoticeTouchStart() { swiperAutoplay.value = false }
|
||||
/** 触摸结束后恢复自动播放 */
|
||||
function onNoticeTouchEnd() { swiperAutoplay.value = true }
|
||||
|
||||
/** 公告弹窗 */
|
||||
@@ -200,12 +204,14 @@ const showNoticeModal = ref(false)
|
||||
const noticeModalTitle = ref('公告')
|
||||
const noticeModalBody = ref('')
|
||||
|
||||
/** 打开公告详情弹窗 */
|
||||
function openNotice(item: { typeLabel: string; content: string; body?: string }) {
|
||||
noticeModalTitle.value = `${item.typeLabel}公告`
|
||||
noticeModalBody.value = item.body || item.content
|
||||
showNoticeModal.value = true
|
||||
}
|
||||
|
||||
/** 跳转到公告列表 */
|
||||
function goMoreNotice() {
|
||||
uni.navigateTo({ url: '/packages/content/pages/notice-list' })
|
||||
}
|
||||
@@ -220,18 +226,22 @@ const hotSpots = mockScenicList.slice(0, 6).map(s => ({
|
||||
image: s.image,
|
||||
}))
|
||||
|
||||
/** 跳转到年卡购买页 */
|
||||
function goCardDetail() {
|
||||
uni.navigateTo({ url: '/packages/user/pages/buy' })
|
||||
}
|
||||
|
||||
/** 跳转到联名卡购买页 */
|
||||
function goUnionBuy() {
|
||||
uni.navigateTo({ url: '/packages/user/pages/union-buy' })
|
||||
}
|
||||
|
||||
/** 跳转到景点详情 */
|
||||
function goSpot(spot: { id: number }) {
|
||||
uni.navigateTo({ url: `/packages/content/pages/scenic-detail?id=${spot.id}` })
|
||||
}
|
||||
|
||||
/** 跳转到景点列表 */
|
||||
function goMore() {
|
||||
uni.navigateTo({ url: '/packages/content/pages/scenic-list' })
|
||||
}
|
||||
|
||||
@@ -80,19 +80,23 @@ onMounted(() => {
|
||||
}, 80)
|
||||
})
|
||||
|
||||
/** 取消登录,返回上一页或首页 */
|
||||
function handleCancel() {
|
||||
uni.navigateBack({ fail: () => uni.switchTab({ url: '/pages/index/index' }) })
|
||||
}
|
||||
|
||||
/** 打开服务条款 */
|
||||
function openAgreement() {
|
||||
uni.navigateTo({ url: '/pages/login/agreement' })
|
||||
}
|
||||
|
||||
/** 打开隐私政策 */
|
||||
function openPrivacy() {
|
||||
uni.navigateTo({ url: '/pages/login/privacy' })
|
||||
}
|
||||
|
||||
async function handleLogin(e: any) {
|
||||
/** 微信一键登录(获取手机号后调后端登录接口) */
|
||||
async function handleLogin(e: { detail: { errMsg: string; code?: string } }) {
|
||||
if (e.detail.errMsg !== 'getPhoneNumber:ok') {
|
||||
return
|
||||
}
|
||||
@@ -105,7 +109,7 @@ async function handleLogin(e: any) {
|
||||
return
|
||||
}
|
||||
|
||||
const loginRes: any = await new Promise((resolve, reject) => {
|
||||
const loginRes = await new Promise<UniNamespace.LoginRes>((resolve, reject) => {
|
||||
uni.login({ provider: 'weixin', success: resolve, fail: reject })
|
||||
})
|
||||
|
||||
@@ -123,8 +127,8 @@ async function handleLogin(e: any) {
|
||||
setTimeout(() => {
|
||||
uni.switchTab({ url: '/pages/index/index' })
|
||||
}, 1000)
|
||||
} catch (err: any) {
|
||||
showToast(err.message || '登录失败')
|
||||
} catch (err: unknown) {
|
||||
showToast(err instanceof Error ? err.message : '登录失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -68,13 +68,16 @@ import { useUserStore } from '../../store/user'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
/** 跳转到登录页 */
|
||||
function goLogin() { uni.navigateTo({ url: '/pages/login/login' }) }
|
||||
|
||||
/** 跳转到指定页面(未登录则先跳登录页) */
|
||||
function goTo(url: string) {
|
||||
if (!userStore.isLogin) { uni.navigateTo({ url: '/pages/login/login' }); return }
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
|
||||
/** 退出登录(弹窗确认后清除 token) */
|
||||
function handleLogout() {
|
||||
uni.showModal({
|
||||
title: '提示', content: '确定要退出登录吗?',
|
||||
@@ -88,14 +91,19 @@ function handleLogout() {
|
||||
})
|
||||
}
|
||||
|
||||
/** 显示「即将上线」提示 */
|
||||
function showComingSoon() { uni.showToast({ title: '即将上线,敬请期待', icon: 'none' }) }
|
||||
|
||||
/** 跳转 FAQ */
|
||||
function goFaq() { uni.navigateTo({ url: '/packages/content/pages/faq' }) }
|
||||
|
||||
/** 跳转使用说明 */
|
||||
function goUsageGuide() { uni.navigateTo({ url: '/packages/content/pages/usage-guide' }) }
|
||||
|
||||
/** 跳转订单列表 */
|
||||
function goOrders() { uni.navigateTo({ url: '/packages/user/pages/order/list' }) }
|
||||
|
||||
/** 拨打客服热线 */
|
||||
function callService() { uni.makePhoneCall({ phoneNumber: '400-123-0101' }) }
|
||||
</script>
|
||||
|
||||
|
||||
+4
-2
@@ -5,11 +5,13 @@ import { wxLogin as apiWxLogin, getUserInfo as apiGetUserInfo } from '../api/use
|
||||
import type { UserInfo } from '../api/types'
|
||||
import { TOKEN_KEY, REFRESH_TOKEN_KEY } from '../config'
|
||||
|
||||
/** 用户认证状态 store(跨页面共享 token 和登录状态) */
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
const token = ref<string>(getToken())
|
||||
const refreshToken = ref<string>(uni.getStorageSync(REFRESH_TOKEN_KEY) || '')
|
||||
const userInfo = ref<UserInfo | null>(null)
|
||||
|
||||
/** 是否已登录 */
|
||||
const isLogin = computed(() => !!token.value)
|
||||
|
||||
/** 微信授权登录(接收 loginCode + phoneCode) */
|
||||
@@ -22,7 +24,7 @@ export const useUserStore = defineStore('user', () => {
|
||||
return result
|
||||
}
|
||||
|
||||
/** 获取用户信息 */
|
||||
/** 获取当前用户信息并缓存到 store */
|
||||
async function fetchUserInfo() {
|
||||
if (!token.value) return
|
||||
const info = await apiGetUserInfo()
|
||||
@@ -30,7 +32,7 @@ export const useUserStore = defineStore('user', () => {
|
||||
return info
|
||||
}
|
||||
|
||||
/** 退出登录 */
|
||||
/** 退出登录(清除 token 和用户信息) */
|
||||
function logout() {
|
||||
token.value = ''
|
||||
refreshToken.value = ''
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/** 数字补零(用于日期格式化) */
|
||||
const formatNumber = (n: number): string => {
|
||||
const str = n.toString()
|
||||
return str[1] ? str : '0' + str
|
||||
}
|
||||
|
||||
/** 格式化日期时间为 `YYYY/M/D HH:mm:ss` */
|
||||
function formatTime(date: Date | number | string): string {
|
||||
const d = new Date(date)
|
||||
const year = d.getFullYear()
|
||||
@@ -14,6 +16,7 @@ function formatTime(date: Date | number | string): string {
|
||||
return `${year}/${month}/${day} ${[hour, minute, second].map(formatNumber).join(':')}`
|
||||
}
|
||||
|
||||
/** 显示 Toast 提示 */
|
||||
function showToast(title: string, icon: 'success' | 'loading' | 'error' | 'none' = 'none'): void {
|
||||
uni.showToast({
|
||||
title,
|
||||
@@ -22,14 +25,17 @@ function showToast(title: string, icon: 'success' | 'loading' | 'error' | 'none'
|
||||
})
|
||||
}
|
||||
|
||||
/** 跳转到指定页面 */
|
||||
function navigateTo(url: string): void {
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
|
||||
/** 重定向到指定页面 */
|
||||
function redirectTo(url: string): void {
|
||||
uni.redirectTo({ url })
|
||||
}
|
||||
|
||||
/** 切换到 tabBar 页面 */
|
||||
function switchTab(url: string): void {
|
||||
uni.switchTab({ url })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user