diff --git a/.hbuilderx/launch.json b/.hbuilderx/launch.json index 15db785..a5db135 100644 --- a/.hbuilderx/launch.json +++ b/.hbuilderx/launch.json @@ -2,8 +2,13 @@ "version" : "1.0", "configurations" : [ { + "customPlaygroundType" : "device", "playground" : "standard", "type" : "uni-app:app-ios" + }, + { + "playground" : "standard", + "type" : "uni-app:app-android" } ] } diff --git a/.prettierrc.js b/.prettierrc.cjs similarity index 97% rename from .prettierrc.js rename to .prettierrc.cjs index 8ecac9d..def7884 100644 --- a/.prettierrc.js +++ b/.prettierrc.cjs @@ -1,4 +1,3 @@ -// @ts-check module.exports = { printWidth: 74, diff --git a/api/index.js b/api/index.js index fa765f9..a4a8777 100644 --- a/api/index.js +++ b/api/index.js @@ -1 +1,27 @@ -// 各模块api \ No newline at end of file +import http from '@/utils/request' + +/** 注册 */ +export const userRegister = data => { + return http({ + url: '/api/register', + method: 'post', + data + }) +} + +/** 登录 */ +export const userLogin = data => { + return http({ + url: '/api/login', + method: 'post', + data + }) +} + +/** 获取用户信息 */ +export const getUserData = () => { + return http({ + url: '/api/userInfo', + method: 'get' + }) +} diff --git a/components/cb-search/cb-search.vue b/components/cb-search/cb-search.vue new file mode 100644 index 0000000..d226799 --- /dev/null +++ b/components/cb-search/cb-search.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/components/register-app/register-app.vue b/components/register-app/register-app.vue index 6344a8a..c4947bb 100644 --- a/components/register-app/register-app.vue +++ b/components/register-app/register-app.vue @@ -1,6 +1,16 @@ + + + + diff --git a/pages/discover/discover.vue b/pages/discover/discover.vue new file mode 100644 index 0000000..b1475ba --- /dev/null +++ b/pages/discover/discover.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/pages/index/index.vue b/pages/index/index.vue index b536876..bffa7c5 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -1,59 +1,67 @@ - - + + diff --git a/pages/login/forgot-password/forgot-password.vue b/pages/login/forgot-password/forgot-password.vue index ea9358d..40d481f 100644 --- a/pages/login/forgot-password/forgot-password.vue +++ b/pages/login/forgot-password/forgot-password.vue @@ -1,5 +1,8 @@ @@ -38,14 +51,14 @@ v-model="formData.username" placeholder="请输入手机号/邮箱" > - + > --> { - showToast('登录成功') - console.log('登录:', formData) + const onLogin = async () => { + if (!formData.agreement) { + showToast('请同意协议') + return + } + const res = await userLogin({ + account: formData.username, + password: formData.password + }) + setToken(res.token) + await fetchUserInfo() + reLaunch('/pages/news-list/news-list') } const onRegister = () => { @@ -26,7 +40,7 @@ } onLoad(e => { - console.log('接收==:', e.id) + console.log('接收参数,返回对应页面的时候使用', e) }) @@ -50,9 +64,7 @@ 登录 diff --git a/pages/mall/confirm-order.vue b/pages/mall/confirm-order.vue new file mode 100644 index 0000000..017fa36 --- /dev/null +++ b/pages/mall/confirm-order.vue @@ -0,0 +1,7 @@ + + + + + diff --git a/pages/mall/detail.vue b/pages/mall/detail.vue new file mode 100644 index 0000000..46c7b74 --- /dev/null +++ b/pages/mall/detail.vue @@ -0,0 +1,7 @@ + + + + + diff --git a/pages/mall/list.vue b/pages/mall/list.vue new file mode 100644 index 0000000..714f0ed --- /dev/null +++ b/pages/mall/list.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/pages/my-index/my-index.vue b/pages/my-index/my-index.vue new file mode 100644 index 0000000..3175cb3 --- /dev/null +++ b/pages/my-index/my-index.vue @@ -0,0 +1,221 @@ + + + + + diff --git a/pages/news-list/news-list.vue b/pages/news-list/news-list.vue new file mode 100644 index 0000000..ca6dbf3 --- /dev/null +++ b/pages/news-list/news-list.vue @@ -0,0 +1,12 @@ + + + + + diff --git a/static/images/discover/circle.png b/static/images/discover/circle.png new file mode 100644 index 0000000..6b57de8 Binary files /dev/null and b/static/images/discover/circle.png differ diff --git a/static/images/discover/company.png b/static/images/discover/company.png new file mode 100644 index 0000000..eb09a97 Binary files /dev/null and b/static/images/discover/company.png differ diff --git a/static/images/discover/grade.png b/static/images/discover/grade.png new file mode 100644 index 0000000..9cf4a45 Binary files /dev/null and b/static/images/discover/grade.png differ diff --git a/static/images/discover/mall.png b/static/images/discover/mall.png new file mode 100644 index 0000000..efae5d2 Binary files /dev/null and b/static/images/discover/mall.png differ diff --git a/static/images/discover/project.png b/static/images/discover/project.png new file mode 100644 index 0000000..2523676 Binary files /dev/null and b/static/images/discover/project.png differ diff --git a/static/images/discover/sign.png b/static/images/discover/sign.png new file mode 100644 index 0000000..82ccdcc Binary files /dev/null and b/static/images/discover/sign.png differ diff --git a/static/images/discover/team.png b/static/images/discover/team.png new file mode 100644 index 0000000..09c748e Binary files /dev/null and b/static/images/discover/team.png differ diff --git a/static/images/my-index/circle.png b/static/images/my-index/circle.png new file mode 100644 index 0000000..77e2987 Binary files /dev/null and b/static/images/my-index/circle.png differ diff --git a/static/images/my-index/collection.png b/static/images/my-index/collection.png new file mode 100644 index 0000000..4c8a6b2 Binary files /dev/null and b/static/images/my-index/collection.png differ diff --git a/static/images/my-index/customer.png b/static/images/my-index/customer.png new file mode 100644 index 0000000..5f85168 Binary files /dev/null and b/static/images/my-index/customer.png differ diff --git a/static/images/my-index/meeting.png b/static/images/my-index/meeting.png new file mode 100644 index 0000000..410266b Binary files /dev/null and b/static/images/my-index/meeting.png differ diff --git a/static/images/my-index/my-card-bg.png b/static/images/my-index/my-card-bg.png new file mode 100644 index 0000000..0bb677f Binary files /dev/null and b/static/images/my-index/my-card-bg.png differ diff --git a/static/images/my-index/system.png b/static/images/my-index/system.png new file mode 100644 index 0000000..9e31ba0 Binary files /dev/null and b/static/images/my-index/system.png differ diff --git a/static/images/my-index/team.png b/static/images/my-index/team.png new file mode 100644 index 0000000..6073754 Binary files /dev/null and b/static/images/my-index/team.png differ diff --git a/static/images/my-index/wallet.png b/static/images/my-index/wallet.png new file mode 100644 index 0000000..1965571 Binary files /dev/null and b/static/images/my-index/wallet.png differ diff --git a/static/images/public/right-arrow.png b/static/images/public/right-arrow.png new file mode 100644 index 0000000..e79cde8 Binary files /dev/null and b/static/images/public/right-arrow.png differ diff --git a/static/images/public/search.png b/static/images/public/search.png new file mode 100644 index 0000000..09bbacf Binary files /dev/null and b/static/images/public/search.png differ diff --git a/static/images/tabBar/contacts.png b/static/images/tabBar/contacts.png new file mode 100644 index 0000000..3639cef Binary files /dev/null and b/static/images/tabBar/contacts.png differ diff --git a/static/images/tabBar/contactsHL.png b/static/images/tabBar/contactsHL.png new file mode 100644 index 0000000..1bbf5a2 Binary files /dev/null and b/static/images/tabBar/contactsHL.png differ diff --git a/static/images/tabBar/discover.png b/static/images/tabBar/discover.png new file mode 100644 index 0000000..3ec9939 Binary files /dev/null and b/static/images/tabBar/discover.png differ diff --git a/static/images/tabBar/discoverHL.png b/static/images/tabBar/discoverHL.png new file mode 100644 index 0000000..ad5ed4b Binary files /dev/null and b/static/images/tabBar/discoverHL.png differ diff --git a/static/images/tabBar/my.png b/static/images/tabBar/my.png new file mode 100644 index 0000000..b980a76 Binary files /dev/null and b/static/images/tabBar/my.png differ diff --git a/static/images/tabBar/myHL.png b/static/images/tabBar/myHL.png new file mode 100644 index 0000000..4868631 Binary files /dev/null and b/static/images/tabBar/myHL.png differ diff --git a/static/images/tabBar/news.png b/static/images/tabBar/news.png new file mode 100644 index 0000000..b6526a1 Binary files /dev/null and b/static/images/tabBar/news.png differ diff --git a/static/images/tabBar/newsHL.png b/static/images/tabBar/newsHL.png new file mode 100644 index 0000000..907915a Binary files /dev/null and b/static/images/tabBar/newsHL.png differ diff --git a/stores/token.js b/stores/token.js index fa4d009..8c99915 100644 --- a/stores/token.js +++ b/stores/token.js @@ -1,42 +1,48 @@ -import { defineStore } from 'pinia'; -import { ref } from 'vue'; -import { STORAGE_KEYS } from '@/constants/storage-keys'; -import { getToken, removeToken } from '@/utils/storage'; +import { defineStore } from 'pinia' +import { ref } from 'vue' +import { STORAGE_KEYS } from '@/constants/storage-keys' +import { setTokenData, getToken, removeToken } from '@/utils/storage' /** 登录状态 */ export const useTokenStore = defineStore(STORAGE_KEYS.TOKEN, () => { + // 从本地存储获取token + const token = ref(getToken() || null) + const isLogin = ref(!!token.value) - // 从本地存储获取token - const token = ref(getToken() || null); - const isLogin = ref(!!token.value); + /** 设置token并保存到本地 */ + const setToken = newToken => { + token.value = newToken + isLogin.value = true + setTokenData(newToken) + } - /** 设置token并保存到本地 */ - const setToken = (newToken) => { - token.value = newToken; - isLogin.value = true; - setToken(newToken); - }; + /** 清除token */ + const clearToken = () => { + token.value = null + isLogin.value = false + removeToken() + } - /** 清除token */ - const clearToken = () => { - token.value = null; - isLogin.value = false; - removeToken() - }; + /** 检查token是否有效(可扩展过期时间判断) */ + const checkToken = () => { + // 简单判断:token存在且不为空 + return !!token.value + } - /** 检查token是否有效(可扩展过期时间判断) */ - const checkToken = () => { - // 简单判断:token存在且不为空 - return !!token.value; - }; + /** 验证token是否过期(实际项目中可添加过期时间判断) */ + const isTokenExpired = () => { + // 示例:如果token中包含过期时间,这里可以判断 + // 例如:const expireTime = parseInt(token.value.split('.')[1]); + // return Date.now() > expireTime * 1000; + return false // 默认不过期 + } - /** 验证token是否过期(实际项目中可添加过期时间判断) */ - const isTokenExpired = () => { - // 示例:如果token中包含过期时间,这里可以判断 - // 例如:const expireTime = parseInt(token.value.split('.')[1]); - // return Date.now() > expireTime * 1000; - return false; // 默认不过期 - }; - - return { token, isLogin, setToken, clearToken, checkToken, isTokenExpired }; -}) \ No newline at end of file + return { + token, + isLogin, + setToken, + clearToken, + checkToken, + isTokenExpired + } +}) diff --git a/stores/user.js b/stores/user.js new file mode 100644 index 0000000..b6dc35a --- /dev/null +++ b/stores/user.js @@ -0,0 +1,70 @@ +import { defineStore } from 'pinia' +import { + getToken, + getUserInfoData, + setUserInfoData, + removeUserInfoData +} from '@/utils/storage' +import { useTokenStore } from './token' +import { getUserData } from '@/api' +import { ref } from 'vue' + +export const useUserStore = defineStore('user', () => { + const { clearToken } = useTokenStore() + + /** 用户信息对象 */ + const userInfo = ref(JSON.parse(getUserInfoData()) || null) + + /** + * 获取用户信息(可从缓存或接口) + */ + const fetchUserInfo = async () => { + // 示例:先尝试从本地缓存读取 + const cachedToken = getToken() + const cachedUserInfo = getUserInfoData() + + if (cachedToken && cachedUserInfo) { + userInfo.value = JSON.parse(cachedUserInfo) + return + } + const res = await getUserData() + await setUserInfo(res.data) + return + } + + /** + * 设置用户信息 + */ + const setUserInfo = async data => { + console.log('存储数据到userInfo==', data) + userInfo.value = data + // 同步到本地存储 + setUserInfoData(data) + } + + /** + * 清除用户信息(退出登录) + */ + const clearUserInfo = () => { + userInfo.value = null + clearToken() + removeUserInfoData() + } + + /** + * 更新部分用户信息(例如昵称、头像) + */ + const updateUserInfo = partialData => { + if (!userInfo.value) return + userInfo.value = { ...userInfo.value, ...partialData } + setUserInfoData(userInfo.value) + } + + return { + userInfo: userInfo.value, + fetchUserInfo, + setUserInfo, + clearUserInfo, + updateUserInfo + } +}) diff --git a/utils/request.js b/utils/request.js index 1cd03e7..490582d 100644 --- a/utils/request.js +++ b/utils/request.js @@ -1,75 +1,81 @@ -import { getToken, removeToken } from './storage'; +import { getToken, removeToken } from './storage' -const BASE_URL = 'xxxxx' +const BASE_URL = 'http://c36bd4b4.natappfree.cc' /** * 网络请求封装 * @param {Object} options 请求参数 * @returns {Promise} */ -const request = (options) => { - // 默认配置 - const defaultOptions = { - url: '', - method: 'GET', - data: {}, - header: { - 'Content-Type': 'application/json' // 默认请求内容类型 - } - } +const request = options => { + // 默认配置 + const defaultOptions = { + url: '', + method: 'GET', + data: {}, + header: { + 'Content-Type': 'application/json' // 默认请求内容类型 + } + } - // 合并配置 - const config = { ...defaultOptions, ...options } + // 合并配置 + const config = { ...defaultOptions, ...options } - // 请求拦截:添加token等通用header - if (getToken()) { - config.header['Authorization'] = 'Bearer ' + getToken() - } + // 请求拦截:添加token等通用header + if (getToken()) { + config.header['Authorization'] = 'Bearer ' + getToken() + } - // 显示加载状态(可选) - if (options.loading !== false) { - uni.showLoading({ - title: '加载中...', - mask: true - }) - }; + // 显示加载状态(可选) + if (options.loading !== false) { + uni.showLoading({ + title: '加载中...', + mask: true + }) + } - return new Promise((resolve, reject) => { - uni.request({ - url: BASE_URL + config.url, - method: config.method, - data: config.data, - timeout: 10000, // 请求超时时间 - header: config.header, - success: (response) => { - // 响应拦截:根据状态码处理 - if (response.statusCode === 200) { - // 这里可以根据后端数据格式调整 - // 例如:if (response.data.code === 0) {...} - resolve(response.data) - } else { - // 状态码错误处理 - handleError(response.statusCode, response.data) - reject(response) - } - }, - fail: (error) => { - // 网络错误处理 - uni.showToast({ - title: '网络异常,请检查网络连接', - icon: 'none', - duration: 2000 - }) - reject(error) - }, - complete: () => { - // 隐藏加载状态 - if (options.loading !== false) { - uni.hideLoading() - } - } - }) - }) + return new Promise((resolve, reject) => { + uni.request({ + url: BASE_URL + config.url, + method: config.method, + data: config.data, + timeout: 10000, // 请求超时时间 + header: config.header, + success: response => { + console.log(response) + // 响应拦截:根据状态码处理 + if (response.statusCode === 200) { + // 这里可以根据后端数据格式调整 + // 例如:if (response.data.code === 0) {...} + if (response.data.code === 200) { + resolve(response.data) + } else { + handleError(response.data.code, response.data) + } + } else { + // 状态码错误处理 + handleError(response.statusCode, response.data) + reject(response) + } + }, + fail: error => { + // 网络错误处理 + uni.showToast({ + title: '网络异常,请检查网络连接', + icon: 'none', + duration: 2000, + mask: true + }) + reject(error) + }, + complete: () => { + // 隐藏加载状态 + if (options.loading !== false) { + uni.hideLoading() + } + } + }) + }) } /** @@ -78,49 +84,53 @@ const request = (options) => { * @param {Object} data 响应数据 */ const handleError = (statusCode, data) => { - switch (statusCode) { - case 401: - uni.showModal({ - title: '提示', - content: '登录已过期,请重新登录', - showCancel: false, - success: () => { - // 清除本地存储的token并跳转到登录页 - removeToken() - uni.navigateTo({ - url: '/pages/login/index' - }) - } - }) - break - case 403: - uni.showToast({ - title: '没有权限访问', - icon: 'none', - duration: 2000 - }) - break - case 404: - uni.showToast({ - title: '请求资源不存在', - icon: 'none', - duration: 2000 - }) - break - case 500: - uni.showToast({ - title: '服务器内部错误', - icon: 'none', - duration: 2000 - }) - break - default: - uni.showToast({ - title: data.message || '请求失败,请重试', - icon: 'none', - duration: 2000 - }) - } + switch (statusCode) { + case 401: + uni.showModal({ + title: '提示', + content: '登录已过期,请重新登录', + showCancel: false, + success: () => { + // 清除本地存储的token并跳转到登录页 + removeToken() + uni.navigateTo({ + url: '/pages/login/index' + }) + } + }) + break + case 403: + uni.showToast({ + title: '没有权限访问', + icon: 'none', + duration: 2000, + mask: true + }) + break + case 404: + uni.showToast({ + title: '请求资源不存在', + icon: 'none', + duration: 2000, + mask: true + }) + break + case 500: + uni.showToast({ + title: data.msg || '服务器内部错误', + icon: 'none', + duration: 2000, + mask: true + }) + break + default: + uni.showToast({ + title: data.msg || '请求失败,请重试', + icon: 'none', + duration: 2000, + mask: true + }) + } } -export default request \ No newline at end of file +export default request diff --git a/utils/storage.js b/utils/storage.js index e7155af..9716994 100644 --- a/utils/storage.js +++ b/utils/storage.js @@ -1,16 +1,31 @@ import { STORAGE_KEYS } from '@/constants/storage-keys' /** 保存 token */ -export const setToken = (v) => { - return uni.setStorageSync(STORAGE_KEYS.TOKEN, v) +export const setTokenData = v => { + return uni.setStorageSync(STORAGE_KEYS.TOKEN, v) } /** 获取 token */ export const getToken = () => { - return uni.getStorageSync(STORAGE_KEYS.TOKEN) || '' + return uni.getStorageSync(STORAGE_KEYS.TOKEN) || '' } /** 清楚 token */ export const removeToken = () => { - return uni.removeStorageSync(STORAGE_KEYS.TOKEN) -} \ No newline at end of file + return uni.removeStorageSync(STORAGE_KEYS.TOKEN) +} + +/** 保存用户信息 */ +export const setUserInfoData = v => { + return uni.setStorageSync(STORAGE_KEYS.USER, JSON.stringify(v)) +} + +/** 获取用户信息 */ +export const getUserInfoData = () => { + return uni.getStorageSync(STORAGE_KEYS.USER) || '' +} + +/** 删除用户信息 */ +export const removeUserInfoData = () => { + return uni.removeStorageSync(STORAGE_KEYS.USER) +} diff --git a/utils/use-ui.js b/utils/use-ui.js index 738464c..16edc7a 100644 --- a/utils/use-ui.js +++ b/utils/use-ui.js @@ -37,11 +37,14 @@ const showToast = (message, type = 'none', duration = 2000) => { if (type === 'error') icon = 'error' if (type === 'warning') icon = 'none' - uni.showToast({ - title: message, - icon, - duration, - mask: true + return new Promise(resolve => { + uni.showToast({ + title: message, + icon, + duration, + mask: true + }) + setTimeout(() => resolve(), duration) }) } diff --git a/utils/validate.js b/utils/validate.js new file mode 100644 index 0000000..d5b91d6 --- /dev/null +++ b/utils/validate.js @@ -0,0 +1,84 @@ +// 正则 + +/** 手机号正则(中国大陆) */ +const PHONE_REGEX = /^1[3-9]\d{9}$/ + +/** 邮箱正则 */ +const EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ + +/** 身份证号(简化版) */ +const ID_CARD_REGEX = /(^\d{15}$)|(^\d{18}$)|(^\d{17}[\dXx]$)/ + +/** 密码强度(至少8位,包含数字和字母) */ +const PASSWORD_REGEX = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/ + +/** + * 校验手机号 + * @param {string} phone - 待校验的手机号 + * @returns {{ valid: boolean, message: string }} + */ +export const validatePhone = phone => { + if (!phone) return { valid: false, message: '手机号不能为空' } + if (!PHONE_REGEX.test(phone)) { + return { valid: false, message: '手机号格式不正确' } + } + return { valid: true, message: '' } +} + +/** + * 校验邮箱 + * @param {string} email - 待校验的邮箱 + * @returns {{ valid: boolean, message: string }} + */ +export const validateEmail = email => { + if (!email) return { valid: false, message: '邮箱不能为空' } + if (!EMAIL_REGEX.test(email)) { + return { valid: false, message: '邮箱格式不正确' } + } + return { valid: true, message: '' } +} + +/** + * 校验密码强度 + * @param {string} password - 待校验的密码 + * @returns {{ valid: boolean, message: string }} + */ +export const validatePassword = password => { + if (!password) return { valid: false, message: '密码不能为空' } + if (password.length < 8) { + return { valid: false, message: '密码长度不能少于8位' } + } + if (!PASSWORD_REGEX.test(password)) { + return { valid: false, message: '密码需包含字母和数字' } + } + return { valid: true, message: '' } +} + +/** + * 校验确认密码是否与原密码一致 + * @param {string} password - 原始密码 + * @param {string} confirmPassword - 确认密码 + * @returns {{ valid: boolean, message: string }} + */ +export function validateConfirmPassword(password, confirmPassword) { + if (!confirmPassword) { + return { valid: false, message: '请再次输入密码' } + } + if (password !== confirmPassword) { + return { valid: false, message: '两次输入的密码不一致' } + } + return { valid: true, message: '' } +} + +/** + * 校验身份证号(仅格式校验,不验证真实性) + * @param {string} idCard - 待校验的身份证号 + * @returns {{ valid: boolean, message: string }} + */ +export const validateIdCard = idCard => { + if (!idCard) return { valid: false, message: '身份证号不能为空' } + if (!ID_CARD_REGEX.test(idCard)) { + return { valid: false, message: '身份证号格式不正确' } + } + return { valid: true, message: '' } +}