沈明 1 year ago
parent
commit
5e3200bb78
  1. 4
      .env.development
  2. 98
      src/api/common.ts
  3. 23
      src/api/login.ts
  4. 9
      src/api/user.ts
  5. 2
      src/config/index.ts
  6. 128
      src/hooks/paginationLoader.ts
  7. 125
      src/hooks/useListLoadClass.ts
  8. 127
      src/hooks/usePagingLoad.ts
  9. 8
      src/manifest.json
  10. 8
      src/pages.json
  11. 105
      src/pages/login/login.vue
  12. 25
      src/pages/mine/index.vue
  13. BIN
      src/static/bg1.png
  14. BIN
      src/static/card.png
  15. BIN
      src/static/invite.jpg
  16. BIN
      src/static/logo-bg.png
  17. BIN
      src/static/logo-tran.png
  18. BIN
      src/static/password.png
  19. BIN
      src/static/redpacket.png
  20. BIN
      src/static/user.png
  21. 67
      src/store/user.ts
  22. 19
      src/utils/http.ts

4
.env.development

@ -1,5 +1,5 @@
ENV='development' ENV='development'
# base api # base api
VITE_APP_BASE_URL = 'http://pos-api.lingji.vip' VITE_APP_BASE_URL = 'https://evote.truescloud.com'
VITE_APP_BASE_PRE = '/shenname' VITE_APP_BASE_PRE = '/dev-api'
VITE_APP_BASE_NAME = 'POS' VITE_APP_BASE_NAME = 'POS'

98
src/api/common.ts

@ -1,98 +0,0 @@
import { request } from '@/utils/http'
export interface sendCodeType {
phone: number | string
}
export function sendCode(data: sendCodeType) {
return request.http({
url: '/api/t.sms/sendCode',
data
})
}
// 上传图片
export function uploadFile(file: any) {
let url = ''
// #ifdef APP-PLUS
url = import.meta.env.VITE_APP_BASE_URL
// #endif
// #ifdef H5
url = import.meta.env.VITE_APP_BASE_PRE
// #endif
return new Promise((resolve, reject) => {
uni.uploadFile({
url: `${url}/api/resources/upload`,
filePath: file,
name: 'file',
success: (res) => {
resolve(JSON.parse(res.data))
},
fail: (res) => {
reject(res)
}
})
})
}
// 根据 KEY 获取对应数据
export function getKeyData(key: string) {
return request.http({
url: '/api/t.systemData/getCategoryByKey',
data: { key: key }
})
}
// 根据 KEY 获取对应系统设置
export function getKeySetting(key: string) {
return request.http({
url: '/api/t.systemData/getSettingByKey',
data: { key: key }
})
}
// 获取无限极分类数据
export function getCategoryData() {
return request.http({
url: '/api/t.systemData/categoryTree'
})
}
// 意见反馈
export interface feedbackType {
type: useType
category_id: number
content: string
image?: string
contact_information: string
}
export function feedback(data: feedbackType) {
return request.http({
url: '/api/t.feedback/add',
data
})
}
// 购买支付接口
export function pay(data: { policy_id: number; uid: number; pay_amount: number; version: string }) {
return request.http({
url: '/api/t.notify/buyAgentPolicyNotify',
data
})
}
// 直采列表
export function voucherQuery() {
return request.http({
url: '/api/d.pickGiftBag/query'
})
}
// 直采支付
export function voucherPay(data: { id: number }) {
return request.http({
url: '/api/d.pickGiftBag/buy',
data
})
}

23
src/api/login.ts

@ -0,0 +1,23 @@
import { request } from '@/utils/http'
export function getOpenid(data: { code: string }) {
return request.http({
url: '/api/openid',
data
})
}
export function getMobile(data: { code: string; openid: string }) {
return request.http({
url: '/api/mobile',
data
})
}
export function getUserInfo(data: { mobile: string }) {
return request.http({
url: '/api/member_mobile',
method: 'GET',
data
})
}

9
src/api/user.ts

@ -0,0 +1,9 @@
import { request } from '@/utils/http'
export function getUserList(data: { name: string }) {
return request.http({
url: '/api/openid',
method: 'GET',
data
})
}

2
src/config/index.ts

@ -1,4 +1,4 @@
export const Prefix = 'POS_' export const Prefix = 'Election_'
export const getPrefixName = (name: string) => { export const getPrefixName = (name: string) => {
return Prefix + name return Prefix + name

128
src/hooks/paginationLoader.ts

@ -1,128 +0,0 @@
/*
* @description:
* @fileName: useListLoadClass.ts
* @author: lxx
* @date: 2023-07-08 08:55:52
* @version: V1.0.0
*/
import { ref } from 'vue'
class PaginationLoader<T> {
// 分页数据
private data = ref<T[]>([])
// 分页基础参数
private requestParams = {
page: 1,
limit: 10
}
// 是否正在加载
private isLoading = ref(false)
// 是否还有更多数据
private hasMore = ref(true)
// 请求函数(由外部传入)
private fetchFn: (formData: Object, onSuccess: Function) => Function
constructor(fetchFn: (formData: Object, onSuccess: Function) => Function) {
this.fetchFn = fetchFn
}
// 获取当前数据
get dataList() {
return this.data.value
}
// 获取是否正在加载
get loading() {
return this.isLoading.value
}
// 获取是否还有更多数据
get more() {
return this.hasMore.value
}
// 加载下一页数据
async loadNextPage() {
// if (this.isLoading.value || !this.hasMore.value) return
// uni.showLoading({ title: '加载中...' })
// this.isLoading.value = true
// try {
// const result = await this.fetchFn(this.currentPage.value, this.pageSize)
// if (result.length > 0) {
// this.data.value = [...this.data.value, ...result]
// this.currentPage.value += 1
// } else {
// this.hasMore.value = false // 没有更多数据
// }
// } catch (error) {
// console.error('加载数据失败:', error)
// } finally {
// this.isLoading.value = false
// }
uni.showLoading({ title: '数据加载中' })
this.isLoading.value = false
const params = { ...this.requestParams } as { [n: string]: string | number | boolean }
setTimeout(() => {
this.fetchFn(params, ({ data }: { data: { data: any; total: number } }) => {
this.isLoading.value = true
this.requestParams.page = params.page as number
this.requestParams.limit = params.limit as number
this.data.value = [...this.data.value, ...data.data]
uni.hideLoading()
})
}, 100)
}
// 重置分页状态
reset() {
this.data.value = []
this.requestParams.page = 1
this.hasMore.value = true
this.isLoading.value = false
}
}
// export default PaginationLoader
const fetchData = async (formData: any, onSuccess: Function) => {
const submitData = { ...formData }
point_order_list(submitData).then((res) => {
const { data } = res as { data: { data: any; total: number } }
onSuccess({ data })
})
// // 模拟接口请求
// return new Promise<{ id: number; name: string }[]>((resolve) => {
// setTimeout(() => {
// const data = Array.from({ length: pageSize }, (_, index) => ({
// id: (page - 1) * pageSize + index,
// name: `Item ${(page - 1) * pageSize + index}`
// }))
// resolve(data)
// }, 1000)
// })
}
// 创建分页加载实例
const paginationLoader = new PaginationLoader(fetchData)
// 加载下一页数据
paginationLoader.loadNextPage(formDate.value)
// 初始化加载第一页数据
paginationLoader.loadNextPage()
return {
dataList: paginationLoader.dataList,
loading: paginationLoader.loading,
more: paginationLoader.more,
loadNextPage
}

125
src/hooks/useListLoadClass.ts

@ -1,125 +0,0 @@
/*
* @description:
* @fileName: useListLoadClass.ts
* @author: lxx
* @date: 2023-07-08 08:55:52
* @version: V1.0.0
*/
import { ref, computed } from 'vue'
import { onReachBottom } from '@dcloudio/uni-app'
class LoadDataClass {
// 请求参数
static queryParams = {
page: 1,
limit: 10
}
// 列表数据
list = ref<any[]>([])
total = ref(0)
// 前置处理方法
afterLoadData: Function | undefined
// 请求方法
Query: Function
// 加载状态参数
isLoading = ref(false)
// 无更多数据了
isNoData = computed(() => {
if (LoadDataClass.queryParams.page * LoadDataClass.queryParams.limit >= this.total.value) {
return true
} else {
return false
}
})
// 显示暂无数据
isEmpty = computed(() => {
if (this.total.value === 0) {
return true
} else {
return false
}
})
constructor(apiFunctions: Function, afterLoadData?: Function, options?: any) {
this.Query = apiFunctions
this.afterLoadData = afterLoadData
// 存在额外参数拼接
if (options) {
LoadDataClass.queryParams = { ...LoadDataClass.queryParams, ...options }
}
// 加载数据
this.LoadData()
}
// 加载数据
LoadData = async () => {
uni.showLoading({
title: '加载中...'
})
this.isLoading.value = true
const res = await this.Query(LoadDataClass.queryParams)
this.afterLoadData && this.afterLoadData(res)
this.total.value = res.data.total
this.list.value = this.list.value.concat(res.data.data)
uni.hideLoading()
uni.stopPullDownRefresh()
this.isLoading.value = false
}
// 加载更多
LoadMore = () => {
if (this.isNoData.value || this.isLoading.value) return // 无数据或者加载中不进行加载
LoadDataClass.queryParams.page += 1
this.LoadData()
}
// 重置参数
queryParamsReset = () => {
LoadDataClass.queryParams = {
page: 1,
limit: 10
}
}
/**
*
* @param isClear: 是否清空数据
*/
ReLoad = (isClear: boolean = true) => {
this.isLoading.value = false
this.list.value = []
if (isClear) {
this.queryParamsReset()
} else {
LoadDataClass.queryParams.page = 1
}
this.LoadData()
}
}
/**
*
* @param api: ListAPI
* @param afterLoadData: res数据前置处理方法
* @returns
*/
interface LoadDataInt {
api: Function
afterLoadData?: Function
options?: any
}
export function LoadData({ api, afterLoadData, options }: LoadDataInt) {
const data = new LoadDataClass(api, afterLoadData, options)
// 下拉加载
onReachBottom(() => {
console.log('onReachBottom')
data.LoadMore()
})
return {
list: data.list,
isLoading: data.isLoading,
isNoData: data.isNoData,
isEmpty: data.isEmpty,
ReLoad: data.ReLoad
}
}

127
src/hooks/usePagingLoad.ts

@ -1,127 +0,0 @@
import { ref, reactive, computed } from 'vue'
import { onReachBottom } from '@dcloudio/uni-app'
/*
* @description:
* @fileName: useListLoadClass.ts
* @author: lxx
* @date: 2023-07-08 08:55:52
* @version: V1.0.0
* loading状态
* @param {*} Query
* @param {*} LoadData
* @param {*} ReLoad (isClear?: boolean) isClear true时将请求参数queryParams
*/
export function usePagingLoad(Query: any) {
// 下拉加载
onReachBottom(() => {
console.log('onReachBottom')
loadMore()
})
const isLoading = ref(false)
let queryParams = reactive({} as any)
queryParams = {
page: 1,
limit: 10
}
const total = ref(0)
const list = ref([])
// 无更多数据了
const isNoData = computed(() => {
if (queryParams.page * queryParams.limit >= total.value) {
return true
} else {
return false
}
})
// 显示暂无数据
const isEmpty = computed(() => {
if (total.value === 0) {
return true
} else {
return false
}
})
interface optionInt {
key: string
val: any
}
const LoadData = async (afterLoadData?: any, option?: optionInt[]) => {
const obj: any = {}
console.log('option', option)
if (option && option?.length > 0) {
option?.map((item) => {
obj[item?.key] = item.val
})
}
// , ...rest: any[]
// if (rest.length > 0) {
// rest.map((item) => {
// obj[item?.key] = item?.val;
// });
// }
queryParams = reactive({ ...queryParams, ...obj })
uni.showLoading({
title: '加载中...'
})
isLoading.value = true
const res = await Query(queryParams)
total.value = res?.data?.total
// 数据加载完成后 设置 after 钩子
afterLoadData && afterLoadData(res.data)
console.log('res.data', res.data)
list.value = list.value.concat(res?.data?.data)
uni.hideLoading()
uni.stopPullDownRefresh()
isLoading.value = false
}
// const afterLoadData = (data: any) => {
// console.log(data);
// };
const ReLoad = (option?: optionInt[], isClear?: boolean) => {
isLoading.value = false
list.value = []
if (isClear) {
queryParams = reactive({
page: 1,
limit: 10
})
} else {
queryParams.page = 1
}
const obj: any = {}
if (option && option?.length > 0) {
option?.map((item) => {
obj[item?.key] = item.val
})
}
queryParams = reactive({ ...queryParams, ...obj })
LoadData()
}
const loadMore = () => {
if (isNoData.value || isLoading.value) return // 无数据或者加载中不进行加载
queryParams.page += 1
LoadData()
}
return {
list,
LoadData,
ReLoad,
isNoData,
isEmpty,
isLoading
}
}

8
src/manifest.json

@ -95,9 +95,9 @@
"iosStyle": "common", "iosStyle": "common",
"androidStyle": "default", "androidStyle": "default",
"android": { "android": {
"hdpi": "C:/Users/Lenovo/Desktop/480x762.9/res/drawable-xhdpi/480x762.9.png", "hdpi": "",
"xhdpi": "C:/Users/Lenovo/Desktop/720x1242.9/res/drawable-xhdpi/720x1242.9.png", "xhdpi": "",
"xxhdpi": "C:/Users/Lenovo/Desktop/1080x1882.9/res/drawable-xhdpi/1080x1882.9.png" "xxhdpi": ""
}, },
"ios": { "ios": {
"storyboard": "C:/Users/Lenovo/Desktop/CustomStoryboard.zip" "storyboard": "C:/Users/Lenovo/Desktop/CustomStoryboard.zip"
@ -109,7 +109,7 @@
"quickapp": {}, "quickapp": {},
/* */ /* */
"mp-weixin": { "mp-weixin": {
"appid": "", "appid": "wx5d1a07b75bd48225",
"setting": { "setting": {
"urlCheck": false, "urlCheck": false,
"es6": true, "es6": true,

8
src/pages.json

@ -7,6 +7,14 @@
} }
}, },
"pages": [ "pages": [
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "登录",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
},
{ {
"path": "pages/votingElection/index", "path": "pages/votingElection/index",
"style": { "style": {

105
src/pages/login/login.vue

@ -0,0 +1,105 @@
<script setup lang="ts">
import { debounce } from 'feng-uniapp-exploit/utils/index'
import { getMobile } from '@/api/login'
import useUserStore from '@/store/user'
const userStore = useUserStore()
const loginCode = ref('')
// code
const getLoginCode = async (): Promise<string> => {
try {
const res = await uni.login({ provider: 'weixin' })
loginCode.value = res.code
return res.code
} catch (error) {
console.error('获取登录code失败:', error)
throw error
}
}
//
interface phoneEvent {
detail: { errMsg: string; iv: string; encryptedData: string; code: string }
}
const onGetPhoneNumber = debounce(async (e: phoneEvent) => {
if (e.detail.errMsg.includes('fail')) {
uni.showToast({ title: '用户拒绝授权', icon: 'none' })
return
}
try {
if (!loginCode.value) {
await getLoginCode()
}
if (!userStore.openId) {
await userStore.getopenid({ code: loginCode.value })
}
const { data: phone } = (await getMobile({ openid: userStore.openId, code: e.detail.code })) as { data: string }
userStore.userInfo.mobile = phone
userStore.getUserInfo({ mobile: phone })
onLoginSuccess()
} catch (error) {
uni.showToast({ title: '登录失败', icon: 'none' })
}
})
const onLoginSuccess = () => {
uni.showToast({
title: '登录成功',
icon: 'none',
success: () => {
setTimeout(() => {
uni.reLaunch({ url: '/pages/votingElection/index' })
}, 1000)
}
})
}
onShow(() => {
if (userStore.userInfo.mobile) {
onLoginSuccess()
}
})
</script>
<template>
<view class="login">
<image class="logo-img" src="@/static/logo.png" mode="widthFix" />
<view class="btn_box">
<u-button
@getphonenumber="onGetPhoneNumber"
text="微信一键登录"
icon="weixin-fill"
icon-color="#fff"
open-type="getPhoneNumber"
color="linear-gradient(270deg, rgba(232, 123, 7, 1) 0%, rgba(247, 205, 77, 1) 100%)"
shape="circle"
/>
</view>
</view>
</template>
<style scoped lang="scss">
.login {
width: 100%;
height: 100vh;
overflow: hidden;
position: relative;
text-align: center;
box-sizing: border-box;
padding: 500rpx 30rpx 0;
background-color: #fff;
.logo-img {
width: 220rpx;
margin-bottom: 60rpx;
}
}
</style>

25
src/pages/mine/index.vue

@ -1,17 +1,30 @@
<script setup lang="ts"> <script setup lang="ts">
// import useUserStore from "@/store/user"; import useUserStore from '@/store/user'
import { getUserList } from '@/api/user'
const userStore = useUserStore()
const isHandleUserInfo = computed(() => userStore.userInfo.name === '')
const keyword = ref('')
const list = ref<any[]>([])
const getList = () => {
getUserList({ name: keyword.value }).then((res) => {
const { data } = res as { data: any[] }
list.value = data || []
})
}
// const userStore = useUserStore();
// const navto = (url: string, params = {}) => uni.$util.goToPage({ url, params }); // const navto = (url: string, params = {}) => uni.$util.goToPage({ url, params });
</script> </script>
<template> <template>
<view class="userview"> <view class="userview">
<!-- 搜索信息 --> <!-- 搜索信息 -->
<block v-if="false"> <block v-if="!isHandleUserInfo">
<view class="userview-search box flex"> <view class="userview-search box flex">
<view class="userview-search-label">姓名</view> <view class="userview-search-label">姓名</view>
<input class="flex1" type="text" placeholder="请输入您的姓名" placeholder-class="placeholder" /> <input class="flex1" type="text" placeholder="请输入您的姓名" v-model="keyword" placeholder-class="placeholder" />
<view class="userview-search-bts">搜索</view> <view class="userview-search-bts" @click="getList">搜索</view>
</view> </view>
<view class="userview-info box" :class="{ border: index === 0 }" v-for="(row, index) in 2" :key="index"> <view class="userview-info box" :class="{ border: index === 0 }" v-for="(row, index) in 2" :key="index">
@ -41,7 +54,7 @@
</block> </block>
<!-- 详细信息 --> <!-- 详细信息 -->
<block v-if="true"> <block v-else>
<view class="userview-info box"> <view class="userview-info box">
<view class="flex-center-between userview-info-item"> <view class="flex-center-between userview-info-item">
<view class="userview-info-item-label">姓名</view> <view class="userview-info-item-label">姓名</view>

BIN
src/static/bg1.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

BIN
src/static/card.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

BIN
src/static/invite.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

BIN
src/static/logo-bg.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 893 KiB

BIN
src/static/logo-tran.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

BIN
src/static/password.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 521 B

BIN
src/static/redpacket.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

BIN
src/static/user.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 541 B

67
src/store/user.ts

@ -1,6 +1,7 @@
// 定义组合式API仓库 // 定义组合式API仓库
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { getPrefixName } from '@/config' import { getPrefixName } from '@/config'
import { getOpenid, getUserInfo as getUserInfoApi } from '@/api/login'
interface userInfoStoreInt { interface userInfoStoreInt {
[n: string]: any [n: string]: any
@ -8,37 +9,47 @@ interface userInfoStoreInt {
export default defineStore( export default defineStore(
getPrefixName('user'), getPrefixName('user'),
() => { () => {
const token = ref('') const openId = ref('')
const isShowPrize = ref(false) const userInfo = ref<userInfoStoreInt>({
const useType = ref<useType>('client') mobile: ''
const userInfo = ref<userInfoStoreInt>({})
const accountInfo = reactive({
can_switch: false,
client: {
account: '',
password: ''
},
agent: {
account: '',
password: ''
}
}) })
const bankCard = ref<userInfoStoreInt>({})
const checkLogin = computed(() => token.value !== '') const checkLogin = computed(() => openId.value !== '')
function logOut() { function getopenid(params: { code: string }) {
token.value = '' return new Promise<any>((resolve, reject) => {
getOpenid(params)
.then((res) => {
const { data } = res as { data: string }
openId.value = data
if (useType.value === 'client') { resolve({ code: 1, message: '登录成功~' })
accountInfo.client = { account: '', password: '' } })
} else { .catch((err) => {
accountInfo.agent = { account: '', password: '' } reject(err)
})
})
} }
useType.value = 'client'
function getUserInfo(params: { mobile: string }) {
return new Promise<any>((resolve, reject) => {
getUserInfoApi(params)
.then((res) => {
const { data } = res as { data: userInfoStoreInt }
userInfo.value = Object.assign(userInfo.value, data)
resolve({ code: 1, data, message: 'SUCCESS' })
})
.catch((err) => {
reject(err)
})
})
}
function logOut() {
userInfo.value = {} userInfo.value = {}
bankCard.value = {}
uni.clearStorageSync() uni.clearStorageSync()
uni.showToast({ uni.showToast({
@ -52,13 +63,11 @@ export default defineStore(
} }
return { return {
token, openId,
accountInfo,
isShowPrize,
useType,
userInfo, userInfo,
bankCard,
checkLogin, checkLogin,
getopenid,
getUserInfo,
logOut logOut
} }
}, },

19
src/utils/http.ts

@ -1,5 +1,3 @@
import useUserStore from '../store/user'
interface ResponseOptions { interface ResponseOptions {
url: string url: string
headers?: { [key: string]: string } headers?: { [key: string]: string }
@ -8,8 +6,6 @@ interface ResponseOptions {
isSinglePost?: boolean isSinglePost?: boolean
} }
const userStore = useUserStore()
export const request = { export const request = {
isLock: false, isLock: false,
http({ url = '', headers = {}, data = {}, method = 'POST', isSinglePost = false }: ResponseOptions) { http({ url = '', headers = {}, data = {}, method = 'POST', isSinglePost = false }: ResponseOptions) {
@ -28,11 +24,13 @@ export const request = {
url = import.meta.env.VITE_APP_BASE_PRE + url url = import.meta.env.VITE_APP_BASE_PRE + url
// #endif // #endif
// #ifdef MP-WEIXIN
url = import.meta.env.VITE_APP_BASE_URL + url
// #endif
const header = Object.assign({ 'content-type': 'application/json', Authorization: '' }, headers) const header = Object.assign({ 'content-type': 'application/json', Authorization: '' }, headers)
if (userStore.token) { console.log('request Url:', url)
header['Authorization'] = 'Bearer' + userStore.token
}
uni.request({ uni.request({
url, url,
@ -42,11 +40,7 @@ export const request = {
success(res) { success(res) {
const data = res.data as { code: number; data: object; msg: string } const data = res.data as { code: number; data: object; msg: string }
switch (data.code) { switch (data.code) {
case 1005: case 1:
uni.showToast({ title: '登录状态已失效,请重新登录!', icon: 'none' })
setTimeout(() => uni.navigateTo({ url: 'pages/login/login' }), 1000)
break
case 200:
resolve(res.data) resolve(res.data)
break break
default: default:
@ -60,7 +54,6 @@ export const request = {
reject(err) reject(err)
}, },
complete() { complete() {
_this.controller = null
_this.isLock = false _this.isLock = false
} }
}) })

Loading…
Cancel
Save