first commit

This commit is contained in:
Your Name
2026-01-19 14:02:50 +08:00
commit 05cacc1481
413 changed files with 65454 additions and 0 deletions

3
.env.development Normal file
View File

@@ -0,0 +1,3 @@
# 请求域名
VITE_APP_BASE_URL='http://server.pronhub.sbs'

3
.env.development.example Normal file
View File

@@ -0,0 +1,3 @@
# 请求域名
VITE_APP_BASE_URL=''

3
.env.production Normal file
View File

@@ -0,0 +1,3 @@
# 请求域名
VITE_APP_BASE_URL='http://server.pronhub.sbs'

3
.env.production.example Normal file
View File

@@ -0,0 +1,3 @@
# 请求域名
VITE_APP_BASE_URL=''

39
.eslintrc.js Normal file
View File

@@ -0,0 +1,39 @@
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
ignorePatterns: ['src/uni_modules/'],
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript/recommended',
'@vue/eslint-config-prettier'
],
rules: {
'prettier/prettier': [
'warn',
{
semi: false,
singleQuote: true,
printWidth: 100,
proseWrap: 'preserve',
bracketSameLine: false,
endOfLine: 'auto',
tabWidth: 4,
useTabs: false,
trailingComma: 'none'
}
],
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'no-undef': 'off',
'vue/prefer-import-from-vue': 'off',
'no-prototype-builtins': 'off',
'prefer-spread': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off'
},
globals: {}
}

29
.gitignore vendored Normal file
View File

@@ -0,0 +1,29 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.hbuilderx

20
index.html Normal file
View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app" style="max-width: 750px;"><!--app-html--></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

6191
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

82
package.json Normal file
View File

@@ -0,0 +1,82 @@
{
"name": "uni-preset-vue",
"version": "0.0.0",
"scripts": {
"dev:app": "uni -p app",
"dev:custom": "uni -p",
"dev:h5": "uni",
"dev:h5:ssr": "uni --ssr",
"dev:mp-alipay": "uni -p mp-alipay",
"dev:mp-baidu": "uni -p mp-baidu",
"dev:mp-kuaishou": "uni -p mp-kuaishou",
"dev:mp-lark": "uni -p mp-lark",
"dev:mp-qq": "uni -p mp-qq",
"dev:mp-toutiao": "uni -p mp-toutiao",
"dev:mp-weixin": "uni -p mp-weixin",
"dev:quickapp-webview": "uni -p quickapp-webview",
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
"build:app": "uni build -p app",
"build:custom": "uni build -p",
"build:h5": "uni build && node scripts/release.mjs",
"build:h5:ssr": "uni build --ssr",
"build:mp-alipay": "uni build -p mp-alipay",
"build:mp-baidu": "uni build -p mp-baidu",
"build:mp-kuaishou": "uni build -p mp-kuaishou",
"build:mp-lark": "uni build -p mp-lark",
"build:mp-qq": "uni build -p mp-qq",
"build:mp-toutiao": "uni build -p mp-toutiao",
"build:mp-weixin": "uni build -p mp-weixin",
"build:quickapp-webview": "uni build -p quickapp-webview",
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
},
"dependencies": {
"@dcloudio/uni-app": "^3.0.0-alpha-3050520220824001",
"@dcloudio/uni-app-plus": "^3.0.0-alpha-3050520220824001",
"@dcloudio/uni-components": "^3.0.0-alpha-3050520220824001",
"@dcloudio/uni-h5": "^3.0.0-alpha-3050520220824001",
"@dcloudio/uni-mp-alipay": "^3.0.0-alpha-3050520220824001",
"@dcloudio/uni-mp-baidu": "^3.0.0-alpha-3050520220824001",
"@dcloudio/uni-mp-kuaishou": "^3.0.0-alpha-3050520220824001",
"@dcloudio/uni-mp-lark": "^3.0.0-alpha-3050520220824001",
"@dcloudio/uni-mp-qq": "^3.0.0-alpha-3050520220824001",
"@dcloudio/uni-mp-toutiao": "^3.0.0-alpha-3050520220824001",
"@dcloudio/uni-mp-weixin": "^3.0.0-alpha-3050520220824001",
"@dcloudio/uni-quickapp-webview": "^3.0.0-alpha-3050520220824001",
"lodash-es": "^4.17.21",
"moment": "^2.30.1",
"pinia": "^2.0.20",
"vconsole": "^3.14.6",
"vue": "^3.2.37",
"vue-i18n": "^9.2.2",
"weixin-js-sdk": "^1.6.0",
"z-paging": "^2.3.8"
},
"devDependencies": {
"@dcloudio/types": "^3.0.13",
"@dcloudio/uni-automator": "^3.0.0-alpha-3050520220824001",
"@dcloudio/uni-cli-shared": "^3.0.0-alpha-3050520220824001",
"@dcloudio/uni-stacktracey": "^3.0.0-alpha-3050520220824001",
"@dcloudio/vite-plugin-uni": "^3.0.0-alpha-3050520220824001",
"@rushstack/eslint-patch": "^1.1.4",
"@types/lodash-es": "^4.17.6",
"@types/node": "^18.7.16",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.0",
"autoprefixer": "^10.4.8",
"eslint": "^8.22.0",
"eslint-plugin-vue": "^9.4.0",
"execa": "^6.1.0",
"fs-extra": "^10.1.0",
"postcss": "^8.4.16",
"postcss-rem-to-responsive-pixel": "^5.1.3",
"prettier": "^2.7.1",
"sass": "^1.54.5",
"tailwindcss": "^3.1.8",
"typescript": "^4.7.4",
"vite": "^2.9.14",
"weapp-tailwindcss-webpack-plugin": "^1.7.0"
}
}

35
scripts/release.mjs Normal file
View File

@@ -0,0 +1,35 @@
import path from 'path'
import fsExtra from 'fs-extra'
const { existsSync, remove, copy } = fsExtra
const cwd = process.cwd()
//打包发布路径,谨慎改动
const releaseRelativePath = '../server/public/mobile'
const distPath = path.resolve(cwd, 'dist/build/h5')
const releasePath = path.resolve(cwd, releaseRelativePath)
async function build() {
if (existsSync(releasePath)) {
await remove(releasePath)
}
console.log(`文件正在复制 ==> ${releaseRelativePath}`)
try {
await copyFile(distPath, releasePath)
} catch (error) {
console.log(`\n ${error}`)
}
console.log(`文件已复制 ==> ${releaseRelativePath}`)
}
function copyFile(sourceDir, targetDir) {
return new Promise((resolve, reject) => {
copy(sourceDir, targetDir, (err) => {
if (err) {
reject(err)
} else {
resolve()
}
})
})
}
build()

45
src/App.vue Normal file
View File

@@ -0,0 +1,45 @@
<script setup lang="ts">
import { onLaunch } from '@dcloudio/uni-app'
import { useAppStore } from './stores/app'
import { getLangPag } from '@/api/lang'
const appStore = useAppStore()
onLaunch(async () => {
await appStore.getConfig()
const pag = await getLangPag({ 'lang': uni.getStorageSync('lang') })
await pag.forEach((item : any) => {
//先销毁再重新赋值
uni.removeStorageSync(item.type + '.' + item.name);
uni.setStorageSync(item.type + '.' + item.name, item.value);
});
if (!uni.getStorageSync('langPag')) {
uni.setStorageSync('langPag', 1);
const current = getCurrentPages();
let pageRoute = "/pages/index/index";
if (current) {
let pageNow = current[current.length - 1]
pageRoute = "/" + pageNow.route;
let params = pageNow.$page.options
let keys = Object.keys(params); //获取对象的key 返回对象key的数组
let query = '';
if (keys.length > 0) {
query = keys.reduce((pre, cur) => {
return pre + cur + '=' + params[cur] + '&';
}, '?').slice(0, -1);
}
pageRoute += query
}
uni.reLaunch({
url: pageRoute
});
}
})
</script>
<style lang="scss">
.uni-simple-toast__text {
word-break: break-word !important;
}
</style>

12
src/api/account.ts Normal file
View File

@@ -0,0 +1,12 @@
import { client } from '@/utils/client'
import request from '@/utils/request'
// 登录
export function login(data: Record<string, any>) {
return request.post({ url: '/login/account', data: { ...data, terminal: client } })
}
//注册
export function register(data: Record<string, any>) {
return request.post({ url: '/login/register', data: { ...data, channel: client } })
}

36
src/api/app.ts Normal file
View File

@@ -0,0 +1,36 @@
import request from '@/utils/request'
//发送短信
export function smsSend(data: any) {
return request.post({ url: '/sms/sendCode', data: data })
}
//发送邮件
export function emailSend(data: any) {
return request.post({ url: '/sms/sendEmail', data: data })
}
//发送邮件
export function sendEmailNoLogin(data: any) {
return request.post({ url: '/sms/sendEmailNoLogin', data: data })
}
export function getConfig() {
return request.get({ url: '/index/config' })
}
export function getCountryCode() {
return request.get({ url: '/index/countryCode' })
}
export function uploadImage(file: any, token?: string) {
return request.uploadFile({
url: '/upload/image',
filePath: file,
name: 'file',
header: {
token
},
fileType: 'image'
})
}

99
src/api/finance.ts Normal file
View File

@@ -0,0 +1,99 @@
import request from '@/utils/request'
//资产数据
export function fundIndex(data : any) {
return request.get({ url: '/finance/index', data }, { isAuth: true })
}
//充值
export function recharge(data : any) {
return request.post({ url: '/recharge/recharge', data }, { isAuth: true })
}
// 充值方式
export function rechargeMethod() {
return request.get({ url: '/recharge/method' }, { isAuth: true })
}
// 充值方式详情
export function rechargeMethodDetail(data : any) {
return request.get({ url: '/recharge/methodDetail', data }, { isAuth: true })
}
// 充值配置
export function rechargeConfig() {
return request.get({ url: '/recharge/config' }, { isAuth: true })
}
// 常用充值金额
export function commonMoney() {
return request.get({ url: '/recharge/commonMoney' }, { isAuth: true })
}
//充值记录
export function rechargeRecordLists(data : any) {
return request.get({ url: '/finance/rechargeRecordLists', data }, { isAuth: true })
}
//充值记录详情
export function rechargeRecordDetail(data : any) {
return request.get({ url: '/finance/rechargeRecordDetail', data }, { isAuth: true })
}
// 充值方式详情
export function rechargeMethodDetailUdun(data : any) {
return request.get({ url: '/recharge/methodDetailUdun', data }, { isAuth: true })
}
// 充值方式详情
export function rechargeMethodDetailTron(data : any) {
return request.get({ url: '/recharge/methodDetailTron', data }, { isAuth: true })
}
// 充值方式详情
export function rechargeMethodDetailAddress(data : any) {
return request.get({ url: '/recharge/methodDetailAddress', data }, { isAuth: true })
}
//获取提现方式
export function getWithdrawMethod() {
return request.get({ url: '/finance/withdrawMethod' }, { isAuth: true })
}
//获取提现钱包
export function getWithdrawWallet() {
return request.get({ url: '/finance/withdrawWallet' }, { isAuth: true })
}
//绑定提现钱包
export function withdrawWalletAdd(data : any) {
return request.post({ url: '/finance/withdrawWalletAdd', data }, { isAuth: true })
}
//获取提现银行
export function getWithdrawBanks() {
return request.get({ url: '/finance/withdrawBanks' }, { isAuth: true })
}
//提现配置
export function withdrawConfig(data : any) {
return request.get({ url: '/finance/withdrawConfig', data }, { isAuth: true })
}
//提现
export function withdraw(data : any) {
return request.post({ url: '/finance/withdraw', data }, { isAuth: true })
}
//提现记录
export function withdrawRecordLists(data : any) {
return request.get({ url: '/finance/withdrawRecordLists', data }, { isAuth: true })
}
//提现记录详情
export function withdrawRecordDetail(data : any) {
return request.get({ url: '/finance/withdrawRecordDetail', data }, { isAuth: true })
}
//资金明细
export function financeLists(data : any) {
return request.get({ url: '/finance/lists', data }, { isAuth: true })
}
//资金明细详情
export function financeDetail(data : any) {
return request.get({ url: '/finance/detail', data }, { isAuth: true })
}
//用户转账详情
export function transferIndex() {
return request.get({ url: '/finance/transferIndex' }, { isAuth: true })
}
//转账
export function transfer(data : any) {
return request.post({ url: '/finance/transfer', data }, { isAuth: true })
}
//转账明细
export function transferRecordLists(data : any) {
return request.get({ url: '/finance/transferRecordLists', data }, { isAuth: true })
}

26
src/api/item.ts Normal file
View File

@@ -0,0 +1,26 @@
import request from '@/utils/request'
// 首页数据
export function getIndex() {
return request.get({ url: '/item/index' }, { isAuth: true })
}
// 详情
export function getDetail(data : any) {
return request.get({ url: '/item/detail', data }, { isAuth: true })
}
// 合同
export function getContract(data : any) {
return request.get({ url: '/item/contract', data }, { isAuth: true })
}
//投资
export function invest(data : any) {
return request.post({ url: '/item/invest', data }, { isAuth: true })
}
//投资记录
export function recordLists(data : any) {
return request.get({ url: '/item/recordLists', data }, { isAuth: true })
}

6
src/api/kefu.ts Normal file
View File

@@ -0,0 +1,6 @@
import request from '@/utils/request'
//客服列表
export function getKefuLists() {
return request.get({ url: '/index/getKefuLists'})
}

19
src/api/lang.ts Normal file
View File

@@ -0,0 +1,19 @@
import request from '@/utils/request'
//获取语言包
export function getLangPag(data : any) {
return request.get({ url: '/language/pagAll', data })
}
//获取所有语言
export function getLangAll() {
return request.get({ url: '/language/all'})
}
/**
* @description 获取语言详情
* @param { string } name
* @return { Promise }
*/
export function getLangDetail(data: { name: string }) {
return request.get({ url: '/language/detail', data: data })
}

31
src/api/mall.ts Normal file
View File

@@ -0,0 +1,31 @@
import request from '@/utils/request'
// 首页数据
export function getIndex() {
return request.get({ url: '/mall/index' }, { isAuth: true })
}
//兑换
export function buy(data : any) {
return request.post({ url: '/mall/buy', data }, { isAuth: true })
}
//兑换记录
export function recordLists(data : any) {
return request.get({ url: '/mall/recordLists', data }, { isAuth: true })
}
// 抽奖数据
export function getDrawIndex() {
return request.get({ url: '/mall/drawIndex' }, { isAuth: true })
}
//抽奖
export function draw(data : any) {
return request.post({ url: '/mall/draw', data }, { isAuth: true })
}
//抽奖记录
export function drawLists(data : any) {
return request.get({ url: '/mall/drawLists', data }, { isAuth: true })
}

35
src/api/news.ts Normal file
View File

@@ -0,0 +1,35 @@
import request from '@/utils/request'
/**
* @description 获取文章分类
* @return { Promise }
*/
export function getArticleCate() {
return request.get({ url: '/article/cate' })
}
/**
* @description 获取文章列表
* @return { Promise }
*/
export function getArticleList(data: Record<string, any>) {
return request.get({ url: '/article/lists', data: data })
}
/**
* @description 获取文章详情
* @param { number } id
* @return { Promise }
*/
export function getArticleDetail(data: { id: number }) {
return request.get({ url: '/article/detail', data: data })
}
/**
* @description 获取提示内容
* @param { number } id
* @return { Promise }
*/
export function getHintDetail(data: { id: number }) {
return request.get({ url: '/article/hintDetail', data: data })
}

29
src/api/robot.ts Normal file
View File

@@ -0,0 +1,29 @@
import request from '@/utils/request'
// 首页数据
export function getIndex(data : any) {
return request.get({ url: '/robot/index', data }, { isAuth: true })
}
//量化买入
export function buy(data : any) {
return request.post({ url: '/robot/buy', data }, { isAuth: true })
}
//量化记录
export function recordLists(data : any) {
return request.get({ url: '/robot/recordLists', data }, { isAuth: true })
}
//抢单
export function grab(data : any) {
return request.post({ url: '/robot/grab', data }, { isAuth: true })
}
//获取进行中订单
export function grabIngRecord() {
return request.get({ url: '/robot/grabIngRecord' }, { isAuth: true })
}
//订单支付
export function grabPay(data : any) {
return request.post({ url: '/robot/grabPay', data }, { isAuth: true })
}

11
src/api/shop.ts Normal file
View File

@@ -0,0 +1,11 @@
import request from '@/utils/request'
//首页数据
export function getIndex() {
return request.get({ url: '/index/index' })
}
//行情数据
export function getMarket() {
return request.get({ url: '/index/market' })
}

10
src/api/team.ts Normal file
View File

@@ -0,0 +1,10 @@
import request from '@/utils/request'
// 团队 首页数据
export function getTeamIndex() {
return request.get({ url: '/team/index'}, { isAuth: true })
}
// 团队列表
export function teamLists(data : any) {
return request.get({ url: '/team/lists', data }, { isAuth: true })
}

129
src/api/user.ts Normal file
View File

@@ -0,0 +1,129 @@
import request from '@/utils/request'
//个人中心数据
export function getUserIndex() {
return request.get({ url: '/user/index' }, { isAuth: true })
}
// 分享 首页数据
export function getShareIndex() {
return request.get({ url: '/user/shareIndex' }, { isAuth: true })
}
// 个人信息
export function getUserInfo() {
return request.get({ url: '/user/info' }, { isAuth: true })
}
// 个人编辑
export function userEdit(data : any) {
return request.post({ url: '/user/setInfo', data }, { isAuth: true })
}
// 绑定手机
export function userBindMobile(data : any, header ?: any) {
return request.post({ url: '/user/bindMobile', data, header }, { isAuth: true })
}
// 更改登录密码
export function userChangePwd(data : any) {
return request.post({ url: '/user/changePassword', data }, { isAuth: true })
}
//忘记密码
export function forgotPassword(data : Record<string, any>) {
return request.post({ url: '/user/resetPassword', data })
}
// 更改支付密码
export function userChangePayPwd(data : any) {
return request.post({ url: '/user/changePayPassword', data }, { isAuth: true })
}
// 设置支付密码
export function setPayPwd(data : any) {
return request.post({ url: '/user/setPayPwd', data }, { isAuth: true })
}
// 用户消息
export function getUserNotice() {
return request.get({ url: '/user/notice' }, { isAuth: true })
}
//用户消息列表
export function getUserNoticeLists(data : any) {
return request.get({ url: '/user/noticeLists', data }, { isAuth: true })
}
//用户消息详情
export function getUserNoticeDetail(data : { id : number }) {
return request.get({ url: '/user/noticeDetail', data: data })
}
//奖励活动数据
export function getActivityIndex() {
return request.get({ url: '/user/activityIndex' }, { isAuth: true })
}
//任务中心数据
export function getMissionIndex() {
return request.get({ url: '/user/missionIndex' }, { isAuth: true })
}
//领取任务奖励
export function missionReward(data : any) {
return request.post({ url: '/user/missionReward', data: data })
}
//意见反馈类型
export function getFeedbackCate() {
return request.get({ url: '/user/feedbackCate' }, { isAuth: true })
}
//意见反馈
export function feedback(data : any) {
return request.post({ url: '/user/feedback', data }, { isAuth: true })
}
//Google Authenticator校验
export function verifyGoogle(data : any) {
return request.post({ url: '/user/verifyGoogle', data }, { isAuth: true })
}
//邮箱校验
export function verifyEmail(data : any) {
return request.post({ url: '/user/verifyEmail', data }, { isAuth: true })
}
//实名认证
export function verifyRealname(data : any) {
return request.post({ url: '/user/verifyRealname', data }, { isAuth: true })
}
//签到配置
export function signinConfig() {
return request.get({ url: '/user/signinConfig' }, { isAuth: true })
}
//签到
export function signin(data : any) {
return request.post({ url: '/user/signin', data }, { isAuth: true })
}
//挖矿 首页数据
export function getMineIndex() {
return request.get({ url: '/user/mineIndex' }, { isAuth: true })
}
//挖矿记录
export function getMineLists(data : any) {
return request.get({ url: '/user/mineLists', data }, { isAuth: true })
}
//矿机 启动
export function mineStart(data : any) {
return request.post({ url: '/user/mineStart', data }, { isAuth: true })
}
//矿机 领取收益
export function mineReceive(data : any) {
return request.post({ url: '/user/mineReceive', data }, { isAuth: true })
}

15
src/api/vip.ts Normal file
View File

@@ -0,0 +1,15 @@
import request from '@/utils/request'
//会员首页数据
export function getIndex() {
return request.get({ url: '/userMember/index' }, { isAuth: true })
}
//会员列表
export function vipAll() {
return request.get({ url: '/userMember/all' }, { isAuth: true })
}
//加入会员
export function vipJoin(data : any) {
return request.post({ url: '/userMember/join', data }, { isAuth: true })
}

View File

@@ -0,0 +1,102 @@
<template>
<button class="avatar-upload p-0 m-0 rounded" :style="styles" hover-class="none" open-type="chooseAvatar"
@click="chooseAvatar" @chooseavatar="chooseAvatar">
<image class="w-full h-full" mode="heightFix" :src="modelValue" v-if="modelValue" />
<slot v-else>
<div :style="styles"
class="border border-dotted border-light flex w-full h-full flex-col items-center justify-center text-muted text-xs box-border rounded">
<u-icon name="plus" :size="36" />
{{t('user.avatarAddImg')}}
</div>
</slot>
</button>
</template>
<script lang="ts" setup>
import { uploadImage } from '@/api/app'
import { useUserStore } from '@/stores/user'
import { addUnit } from '@/utils/util'
import { isBoolean } from 'lodash'
import { computed, CSSProperties, onUnmounted } from 'vue'
import { t } from '@/utils/util'
const props = defineProps({
modelValue: {
type: String
},
fileKey: {
type: String,
default: 'uri'
},
size: {
type: [String, Number],
default: 120
},
round: {
type: [Boolean, String, Number],
default: false
},
border: {
type: Boolean,
default: true
}
})
const emit = defineEmits<{
(event : 'update:modelValue', value : string) : void
}>()
const userStore = useUserStore()
const styles = computed<CSSProperties>(() => {
const size = addUnit(props.size)
return {
width: size,
height: size,
borderRadius: isBoolean(props.round) ? (props.round ? '50%' : '') : addUnit(props.round)
}
})
const chooseAvatar = (e : any) => {
//// #ifndef MP-WEIXIN
uni.navigateTo({
url: '/uni_modules/vk-uview-ui/components/u-avatar-cropper/u-avatar-cropper?destWidth=300&rectWidth=200&fileType=jpg'
})
// // #endif
// // #ifdef MP-WEIXIN
// const path = e.detail?.avatarUrl
// if (path) {
// uploadImageIng(path)
// }
// // #endif
}
const uploadImageIng = async (file : string) => {
uni.showLoading({
title: t('user.avatarUploading')
})
try {
const res : any = await uploadImage(file, userStore.temToken!)
uni.hideLoading()
console.log(res)
emit('update:modelValue', res[props.fileKey])
} catch (error) {
uni.hideLoading()
uni.$u.toast(error)
}
}
// 监听从裁剪页发布的事件,获得裁剪结果
uni.$on('uAvatarCropper', (path) => {
uploadImageIng(path)
})
onUnmounted(() => {
uni.$off('uAvatarCropper')
})
</script>
<style lang="scss" scoped>
.avatar-upload {
background: #fff;
overflow: hidden;
&::after {
border: none;
}
}
</style>

View File

@@ -0,0 +1,88 @@
<template>
<u-popup v-model="show" @close="handleClose" mode="bottom" height="60%" class="max-w-[750px]">
<view class="fixed w-full bg-[#1B1F45] z-10 py-[16rpx]">
<view class="text-center font-bold py-[16rpx]">
{{t('login.countryCodeChoose')}}
</view>
<view class="px-[40rpx]">
<u-search shape="square" :show-action="false" :placeholder="t('login.countryCodeSearch')" v-model="formData.keyword"></u-search>
</view>
</view>
<view class="pt-[200rpx]">
<view v-for="item in keywordFliter" @click="handleChoose(item)" >
<view class="flex justify-start items-center px-[40rpx] py-[10rpx]">
<view class="mt-[16rpx]">
<u-icon width="60rpx" height="40rpx" :name="item.logo"></u-icon>
</view>
<view class="ml-[20rpx] mr-[10rpx]">
{{item.name}}
</view>
<view class="">
( {{item.code}} )
</view>
</view>
</view>
</view>
</u-popup>
</template>
<script lang="ts" setup>
import { reactive, computed } from 'vue'
import { t } from '@/utils/util'
const props = defineProps({
list: {
type: Array
},
show: {
type: Boolean,
}
})
const emit = defineEmits<{
(event : 'update', value : any) : void
}>()
const formData = reactive({
keyword: '',
})
//根据输入框内容塞选
const keywordFliter = computed(() => {
const lists = props.list??[]
if (!formData.keyword) {
return props.list
}
const inputValue = formData.keyword
return lists.filter((item: any) => {
if (item['code'].indexOf(inputValue) !== -1) {
return item
}
})
})
const handleChoose = async (item:any) => {
emit('update', {
code: item['code'],
show: false
})
}
const handleClose = async () => {
emit('update', {
code: '',
show: false
})
}
</script>
<style lang="scss" scoped>
.avatar-upload {
background: #fff;
overflow: hidden;
&::after {
border: none;
}
}
</style>

View File

@@ -0,0 +1,20 @@
<template>
<view class="max-w-[750px]">
<u-navbar :back-text="title" :back-text-style="{color: '#FFFFFF'}" :background="{backgroundColor: backgroundColor}" back-icon-color="#FFFFFF" title-color="#FFFFFF" height="50"></u-navbar>
</view>
</template>
<script lang="ts" setup>
const props = withDefaults(
defineProps < {
title: String,
backgroundColor: String,
} > (), {
title: '',
backgroundColor: '#4E55AF',
}
)
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,97 @@
<template>
<view>
<u-popup v-model="showPopup" mode="bottom" border-radius="14" :mask-close-able="false">
<view class="h-[1000rpx] p-[40rpx]">
<view class="flex items-center">
<image
class="w-[100rpx] h-[100rpx] rounded"
mode="heightFix"
:src="logo"
></image>
<text class="text-3xl ml-5 font-bold">{{ title }}</text>
</view>
<view class="mt-5 text-muted">
建议使用您的微信头像和昵称以便获得更好的体验
</view>
<view class="mt-[30rpx]">
<form @submit="handleSubmit">
<u-form-item required label="头像" :labelWidth="120">
<view class="flex-1">
<avatar-upload v-model="avatar"></avatar-upload>
</view>
</u-form-item>
<u-form-item required label="昵称" :labelWidth="120">
<input
class="flex-1 h-[60rpx]"
name="nickname"
type="nickname"
placeholder="请输入昵称"
/>
</u-form-item>
<view class="mt-[80rpx]">
<button
class="bg-primary rounded-full text-white text-lg h-[80rpx] leading-[80rpx]"
hover-class="none"
form-type="submit"
>
确定
</button>
</view>
<view class="flex justify-center mt-[60rpx]">
<view class="text-muted" @click="showPopup = false">暂不登录</view>
</view>
</form>
</view>
</view>
</u-popup>
</view>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue'
const props = defineProps({
show: {
type: Boolean
},
logo: {
type: String
},
title: {
type: String
}
})
const emit = defineEmits<{
(event: 'update:show', show: boolean): void
(event: 'update', value: any): void
}>()
const showPopup = computed({
get() {
return props.show
},
set(val) {
emit('update:show', val)
}
})
const avatar = ref()
const handleSubmit = (e: any) => {
const { nickname } = e.detail.value
if (!avatar.value)
return uni.$u.toast({
title: '请添加头像'
})
if (!nickname)
return uni.$u.toast({
title: '请输入昵称'
})
emit('update', {
avatar: avatar.value,
nickname
})
}
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,44 @@
<template>
<navigator :url="`/pages/news_detail/news_detail?id=${newsId}`">
<view class="news-card flex px-[20rpx] py-[32rpx]">
<view class="mr-[20rpx]" v-if="item.image">
<u-image :src="item.image" width="240" height="180"></u-image>
</view>
<view class="news-card-content flex flex-col justify-between flex-1">
<view class="news-card-content-title text-lg font-medium">{{ item.title }}</view>
</view>
<u-icon name="arrow-right" class="text-muted" size="28"></u-icon>
</view>
</navigator>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const props = withDefaults(
defineProps<{
item: any
newsId: number
}>(),
{
item: {},
newsId: ''
}
)
</script>
<style lang="scss" scoped>
.news-card {
border-bottom: 1px solid #f8f8f8;
&-content {
&-title {
-webkit-line-clamp: 1;
overflow: hidden;
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
}
}
}
</style>

View File

@@ -0,0 +1,62 @@
<template>
<view
class="page-status"
v-if="status !== PageStatusEnum['NORMAL']"
:class="{ 'page-status--fixed': fixed }"
>
<!-- Loading -->
<template v-if="status === PageStatusEnum['LOADING']">
<slot name="loading">
<u-loading :size="60" mode="flower" />
</slot>
</template>
<!-- Error -->
<template v-if="status === PageStatusEnum['ERROR']">
<slot name="error"></slot>
</template>
<!-- Empty -->
<template v-if="status === PageStatusEnum['EMPTY']">
<slot name="empty"></slot>
</template>
</view>
<template v-else>
<slot> </slot>
</template>
</template>
<script lang="ts" setup>
import { PageStatusEnum } from '@/enums/appEnums'
const props = defineProps({
status: {
type: String,
default: PageStatusEnum['LOADING']
},
fixed: {
type: Boolean,
default: true
}
})
</script>
<style lang="scss" scoped>
.page-status {
height: 100%;
width: 100%;
min-height: 100%;
padding: 0;
flex: 1;
display: flex;
justify-content: center;
align-items: center;
background-color: #ffffff;
&--fixed {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 900;
}
}
</style>

View File

@@ -0,0 +1,322 @@
<template>
<u-popup
v-model="showPay"
mode="bottom"
safe-area-inset-bottom
:mask-close-able="false"
border-radius="14"
closeable
@close="handleClose"
>
<view class="h-[900rpx]">
<page-status :status="popupStatus" :fixed="false">
<template #error>
<u-empty text="订单信息错误,无法查询到订单信息" mode="order"></u-empty>
</template>
<template #default>
<view class="payment h-full w-full flex flex-col">
<view class="header py-[50rpx] flex flex-col items-center">
<price
:content="payData.order_amount"
mainSize="44rpx"
minorSize="40rpx"
fontWeight="500"
color="#333"
></price>
</view>
<view class="main flex-1 mx-[20rpx]">
<view>
<view class="payway-lists">
<u-radio-group v-model="payWay" class="w-full">
<view
class="p-[20rpx] flex items-center w-full payway-item"
v-for="(item, index) in payData.lists"
:key="index"
@click="selectPayWay(item.pay_way)"
>
<u-icon
class="flex-none"
:size="48"
:name="item.icon"
></u-icon>
<view class="mx-[16rpx] flex-1">
<view class="payway-item--name flex-1">
{{ item.name }}
</view>
<view class="text-muted text-xs">{{
item.extra
}}</view>
</view>
<u-radio class="mr-[-20rpx]" :name="item.pay_way">
</u-radio>
</view>
</u-radio-group>
</view>
</view>
</view>
<view class="submit-btn p-[20rpx]">
<u-button
@click="handlePay"
shape="circle"
type="primary"
:loading="isLock"
>
立即支付
</u-button>
</view>
</view>
</template>
</page-status>
</view>
</u-popup>
<u-popup
class="pay-popup"
v-model="showCheckPay"
round
mode="center"
borderRadius="10"
:maskCloseAble="false"
>
<view class="content bg-white w-[560rpx] p-[40rpx]">
<view class="text-2xl font-medium text-center"> 支付确认 </view>
<view class="pt-[30rpx] pb-[40rpx]">
<view> 请在微信内完成支付如果您已支付成功请点击`已完成支付`按钮 </view>
</view>
<view class="flex">
<view class="flex-1 mr-[20rpx]">
<u-button
shape="circle"
type="primary"
plain
size="medium"
hover-class="none"
:customStyle="{ width: '100%' }"
@click="queryPayResult(false)"
>
重新支付
</u-button>
</view>
<view class="flex-1">
<u-button
shape="circle"
type="primary"
size="medium"
hover-class="none"
:customStyle="{ width: '100%' }"
@click="queryPayResult()"
>
已完成支付
</u-button>
</view>
</view>
</view>
</u-popup>
</template>
<script lang="ts" setup>
import { pay, PayWayEnum } from '@/utils/pay'
import { getPayWay, prepay, getPayResult } from '@/api/pay'
import { computed, ref, watch } from 'vue'
import { useLockFn } from '@/hooks/useLockFn'
import { series } from '@/utils/util'
import { ClientEnum, PageStatusEnum, PayStatusEnum } from '@/enums/appEnums'
import { useUserStore } from '@/stores/user'
import { client } from '@/utils/client'
/*
页面参数 orderId订单idfrom订单来源
*/
const props = defineProps({
show: {
type: Boolean,
required: true
},
showCheck: {
type: Boolean
},
// 订单id
orderId: {
type: Number,
required: true
},
//订单来源
from: {
type: String,
required: true
},
//h5微信支付回跳路径一般为拉起支付的页面路径
redirect: {
type: String
}
})
const emit = defineEmits(['update:showCheck', 'update:show', 'close', 'success', 'fail'])
const payWay = ref()
const popupStatus = ref(PageStatusEnum.LOADING)
const payData = ref<any>({
order_amount: '',
lists: []
})
const showCheckPay = computed({
get() {
return props.showCheck
},
set(value) {
emit('update:showCheck', value)
}
})
const showPay = computed({
get() {
return props.show
},
set(value) {
emit('update:show', value)
}
})
const handleClose = () => {
showPay.value = false
emit('close')
}
const getPayData = async () => {
popupStatus.value = PageStatusEnum.LOADING
try {
payData.value = await getPayWay({
order_id: props.orderId,
from: props.from
})
popupStatus.value = PageStatusEnum.NORMAL
const checkPay =
payData.value.lists.find((item: any) => item.is_default) || payData.value.lists[0]
payWay.value = checkPay?.pay_way
} catch (error) {
popupStatus.value = PageStatusEnum.ERROR
}
}
const userStore = useUserStore()
const selectPayWay = (pay: number) => {
payWay.value = pay
}
const payment = (() => {
// 查询是否绑定微信
const checkIsBindWx = async () => {
if (
userStore.userInfo.is_auth == 0 &&
[ClientEnum.OA_WEIXIN, ClientEnum.MP_WEIXIN].includes(client) &&
payWay.value == PayWayEnum.WECHAT
) {
const res: any = await uni.showModal({
title: '温馨提示',
content: '当前账号未绑定微信,无法完成支付',
confirmText: '去绑定'
})
if (res.confirm) {
uni.navigateTo({
url: '/pages/user_set/user_set'
})
}
return Promise.reject()
}
}
// 调用预支付
const prepayTask = async () => {
uni.showLoading({
title: '正在支付中'
})
const data = await prepay({
order_id: props.orderId,
from: props.from,
pay_way: payWay.value,
redirect: props.redirect
})
return data
}
//拉起支付
const payTask = async (data: any) => {
try {
const res = await pay.payment(data.pay_way, data.config)
return res
} catch (error) {
return Promise.reject(error)
}
}
return series(checkIsBindWx, prepayTask, payTask)
})()
const { isLock, lockFn: handlePay } = useLockFn(async () => {
try {
const res: PayStatusEnum = await payment()
handlePayResult(res)
uni.hideLoading()
} catch (error) {
uni.hideLoading()
console.log(error)
}
})
const handlePayResult = (status: PayStatusEnum) => {
switch (status) {
case PayStatusEnum.SUCCESS:
emit('success')
break
case PayStatusEnum.FAIL:
emit('fail')
break
}
}
const queryPayResult = async (confirm = true) => {
const res = await getPayResult({
order_id: props.orderId,
from: props.from
})
if (res.pay_status === 0) {
if (confirm == true) {
uni.$u.toast('您的订单还未支付,请重新支付')
}
showPay.value = true
handlePayResult(PayStatusEnum.FAIL)
} else {
if (confirm == false) {
uni.$u.toast('您的订单已经支付,请勿重新支付')
}
handlePayResult(PayStatusEnum.SUCCESS)
}
showCheckPay.value = false
}
watch(
() => props.show,
(value) => {
if (value) {
if (!props.orderId) {
popupStatus.value = PageStatusEnum.ERROR
return
}
getPayData()
}
},
{
immediate: true
}
)
</script>
<style lang="scss">
.payway-lists {
.payway-item {
border-bottom: 1px solid;
@apply border-page;
}
}
</style>

View File

@@ -0,0 +1,126 @@
<template>
<view class="price-container">
<view
:class="['price-wrap', { 'price-wrap--disabled': lineThrough }]"
:style="{ color: color }"
>
<!-- Prefix -->
<view class="fix-pre" :style="{ fontSize: minorSize }">
<slot name="prefix">{{ prefix }}</slot>
</view>
<!-- Content -->
<view :style="{ 'font-weight': fontWeight }">
<!-- Integer -->
<text :style="{ fontSize: mainSize }">{{ integer }}</text>
<!-- Decimals -->
<text :style="{ fontSize: minorSize }">{{ decimals }}</text>
</view>
<!-- Suffix -->
<view class="fix-suf" :style="{ fontSize: minorSize }">
<slot name="suffix">{{ suffix }}</slot>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
/**
* @description 价格展示,适用于有前后缀,小数样式不一
* @property {String|Number} content 价格 (必填项)
* @property {Number} prec 小数位 (默认: 2)
* @property {Boolean} autoPrec 自动小数位【注以prec为最大小数位】 (默认: true)
* @property {String} color 颜色 (默认: 'unset')
* @property {String} mainSize 主要内容字体大小 (默认: 46rpx)
* @property {String} minorSize 主要内容字体大小 (默认: 32rpx)
* @property {Boolean} lineThrough 贯穿线 (默认: false)
* @property {String|Number} fontWeight 字重 (默认: normal)
* @property {String} prefix 前缀 (默认: ¥)
* @property {String} suffix 后缀
* @example <price content="100" suffix="\/元" />
*/
import { computed } from 'vue'
import { formatPrice } from '@/utils/util'
/** Props Start **/
const props = withDefaults(
defineProps<{
content: string | number // 标题
prec?: number // 小数数量
autoPrec?: boolean // 动态小数
color?: string // 颜色
mainSize?: string // 主要内容字体大小
minorSize?: string // 次要内容字体大小
lineThrough?: boolean // 贯穿线
fontWeight?: string // 字重
prefix?: string // 前缀
suffix?: string // 后缀
}>(),
{
content: '',
prec: 2,
autoPrec: true,
color: '#FA8919',
mainSize: '36rpx',
minorSize: '28rpx',
lineThrough: false,
fontWeight: 'normal',
prefix: '¥',
suffix: ''
}
)
/** Props End **/
/** Computed Start **/
/**
* @description 金额主体部分
*/
const integer = computed(() => {
return formatPrice({
price: props.content,
take: 'int'
})
})
/**
* @description 金额小数部分
*/
const decimals = computed(() => {
let decimals = formatPrice({
price: props.content,
take: 'dec',
prec: props.prec
})
// 小数余十不能是 .10||.20||.30以此类推,
decimals = decimals % 10 == 0 ? decimals.substr(0, decimals.length - 1) : decimals
return props.autoPrec ? (decimals * 1 ? '.' + decimals : '') : props.prec ? '.' + decimals : ''
})
/** Computed End **/
</script>
<style lang="scss" scoped>
.price-container {
display: inline-block;
}
.price-wrap {
display: flex;
align-items: baseline;
&--disabled {
position: relative;
&::before {
position: absolute;
left: 0;
top: 50%;
right: 0;
transform: translateY(-50%);
display: block;
content: '';
height: 0.05em;
background-color: currentColor;
}
}
}
</style>

View File

@@ -0,0 +1,84 @@
<template>
<view
:class="{ active, inactive: !active, tab: true }"
:style="shouldShow ? '' : 'display: none;'"
>
<slot v-if="shouldRender"></slot>
</view>
</template>
<script lang="ts" setup>
import { ref, provide, inject, watch, computed, onMounted, getCurrentInstance } from 'vue'
const props = withDefaults(
defineProps<{
dot?: boolean | string
name?: boolean | string
info?: any
}>(),
{
dot: false,
name: ''
}
)
const active = ref<boolean>(false)
const shouldShow = ref<boolean>(false)
const shouldRender = ref<boolean>(false)
const inited = ref(undefined)
const updateTabs: any = inject('updateTabs')
const handleChange: any = inject('handleChange')
const updateRender = (value) => {
inited.value = inited.value || value
active.value = value
shouldRender.value = inited.value!
shouldShow.value = value
}
const update = () => {
if (updateTabs) {
updateTabs()
}
}
const instance = getCurrentInstance()
console.log(instance)
handleChange(instance?.props, updateRender)
onMounted(() => {
update()
})
const changeData = computed(() => {
const { dot, info } = props
return {
dot,
info
}
})
watch(
() => changeData.value,
() => {
update()
}
)
watch(
() => props.name,
(val) => {
update()
}
)
</script>
<style>
.tab.active {
height: auto;
}
.tab.inactive {
height: 0;
overflow: visible;
}
</style>

View File

@@ -0,0 +1,87 @@
<template>
<u-tabbar v-if="showTabbar" height="120rpx" icon-size="50rpx" v-model="current" v-bind="tabbarStyle" :list="tabbarList"
@change="handleChange" :hide-tab-bar="false" :border-top="false"></u-tabbar>
</template>
<script lang="ts" setup>
import { onShow } from '@dcloudio/uni-app'
import { navigateTo } from '@/utils/util'
import { computed, ref } from 'vue'
import { t } from '@/utils/util'
const current = ref()
const tabbarList = ref([
{
iconPath: "../../static/images/tabs/home_off.png",
selectedIconPath: "../../static/images/tabs/home_on.png",
text: t('tabbar.index'),
pagePath: "/pages/index/index",
link: {
path: "/pages/index/index",
type: "shop"
}
},
{
iconPath: "../../static/images/tabs/item_off.png",
selectedIconPath: "../../static/images/tabs/item_on.png",
text: t('tabbar.mine'),
pagePath: "/pages/item/index",
link: {
path: "/pages/item/index",
type: "shop"
}
},
{
iconPath: "../../static/images/tabs/mine_on.gif",
selectedIconPath: "../../static/images/tabs/mine_on.gif",
text: '',
pagePath: "/pages/mine/index",
link: {
path: "/pages/mine/index",
type: "shop"
}
},
{
iconPath: "../../static/images/tabs/team_off.png",
selectedIconPath: "../../static/images/tabs/team_on.png",
text: t('tabbar.team'),
pagePath: "/pages/team/index",
link: {
path: "/pages/team/index",
type: "shop"
}
},
{
iconPath: "../../static/images/tabs/my_off.png",
selectedIconPath: "../../static/images/tabs/my_on.png",
text: t('tabbar.my'),
pagePath: "/pages/user/user",
link: {
path: "/pages/user/user",
type: "shop"
}
},
])
const showTabbar = computed(() => {
const currentPages = getCurrentPages()
const currentPage = currentPages[currentPages.length - 1]
const current = tabbarList.value.findIndex((item : any) => {
return item.pagePath === '/' + currentPage.route
})
return current >= 0
})
const tabbarStyle = computed(() => ({
activeColor: "#A1DBF5",
inactiveColor: "#FFFFFF"
}))
const handleChange = (index : number) => {
const selectTab = tabbarList.value[index]
navigateTo(selectTab.link, 'switchTab')
}
onShow(() => {
uni.hideTabBar();
})
</script>

View File

@@ -0,0 +1,437 @@
<template>
<view class="tabs">
<u-sticky :enable="isFixed" :bg-color="stickyBgColor" :offset-top="top" :h5-nav-height="0">
<view
:id="id"
:style="{
background: bgColor
}"
>
<scroll-view
:style="{ height: height + 'rpx' }"
scroll-x
class="scroll-view"
:scroll-left="scrollLeft"
scroll-with-animation
>
<view class="scroll-box" :class="{ 'tabs-scorll-flex': !isScroll }">
<view
class="tab-item line1"
:id="'tab-item-' + index"
v-for="(item, index) in list"
:key="index"
@tap="clickTab(index)"
:style="[tabItemStyle(index)]"
>
<u-badge
:count="item[count] || item['dot'] || 0"
:offset="offset"
size="mini"
></u-badge>
{{ item[name] || item['name'] }}
</view>
<view v-if="showBar" class="tab-bar" :style="[tabBarStyle]"></view>
</view>
</scroll-view>
</view>
</u-sticky>
<view
class="tab-content"
@touchstart="onTouchStart"
@touchmove="onTouchMove"
@touchcancel="onTouchEnd"
@touchend="onTouchEnd"
>
<!-- <view class="tab-track" :class="{'tab-animated': animated}" :style="[trackStyle]"> -->
<view>
<slot></slot>
</view>
<!-- </view> -->
</view>
</view>
</template>
<script lang="ts" setup>
import { getRect } from '@/utils/util'
import {
ref,
reactive,
computed,
watch,
provide,
nextTick,
onMounted,
getCurrentInstance
} from 'vue'
import { useTouch } from '@/hooks/useTouch'
// Touch 钩子
const { touch, resetTouchStatus, touchStart, touchMove } = useTouch()
const emit = defineEmits<{
(event: 'change', value: number): void
}>()
const props = withDefaults(
defineProps<{
isScroll?: boolean // 导航菜单是否需要滚动如只有2或者3个的时候就不需要滚动了此时使用flex平分tab的宽度
current?: number | string // 当前活动tab的索引
height?: number | string // 导航栏的高度和行高
fontSize?: number | string // 字体大小
duration?: number | string // 过渡动画时长, 单位ms
activeColor?: number | string // 选中项的主题颜色
inactiveColor?: number | string // 未选中项的颜色
barWidth?: number | string // 菜单底部移动的bar的宽度单位rpx
barHeight?: number // 移动bar的高度
gutter?: number | string // 单个tab的左或有内边距左右相同
bgColor?: number | string // 导航栏的背景颜色
name?: string // 读取传入的数组对象的属性(tab名称)
count?: string // 读取传入的数组对象的属性(徽标数)
offset?: number[] // 徽标数位置偏移
bold?: boolean // 活动tab字体是否加粗
activeItemStyle?: any // 当前活动tab item的样式
showBar?: boolean // 是否显示底部的滑块
barStyle?: any // 底部滑块的自定义样式
itemWidth?: string // 标签的宽度
isFixed?: boolean // 吸顶是否固定
top?: number | string // 吸顶顶部距离
stickyBgColor?: string // 吸顶颜色
swipeable: boolean // 是否允许滑动切换
// animated: boolean // 切换动画
}>(),
{
isScroll: true,
current: 0,
height: 80,
fontSize: 28,
duration: 0.3,
activeColor: '#2073F4',
inactiveColor: '#333',
barWidth: 40,
barHeight: 4,
gutter: 30,
bgColor: '#FFFFFF',
name: 'name',
count: 'count',
offset: [5, 20],
bold: true,
activeItemStyle: {},
showBar: true,
barStyle: {},
itemWidth: 'auto',
isFixed: false,
top: 0,
stickyBgColor: '#FFFFFF',
swipeable: true
// animated: true
}
)
const list = ref<any>([])
const childrens = ref<any>([])
const scrollLeft = ref<number>(0) // 滚动scroll-view的左边滚动距离
const tabQueryInfo = ref<any>([]) // 存放对tab菜单查询后的节点信息
const componentWidth = ref<number>(0) // 屏幕宽度单位为px
const scrollBarLeft = ref<number>(0) // 移动bar需要通过translateX()移动的距离
const parentLeft = ref<number>(0) // 父元素(tabs组件)到屏幕左边的距离
const id = ref<string>('cu-tab') // id值
const currentIndex = ref<any>(props.current)
const barFirstTimeMove = ref<boolean>(true) // 滑块第一次移动时(页面刚生成时),无需动画,否则给人怪异的感觉
const swiping = ref<boolean>(false)
//@ts-ignore
const ctx = getCurrentInstance()
// 监听tab的变化重新计算tab菜单的布局信息因为实际使用中菜单可能是通过
// 后台获取的如新闻app顶部的菜单获取返回需要一定时间所以list变化时重新获取布局信息
watch(
() => list.value,
async (n, o) => {
// list变动时重制内部索引否则可能导致超出数组边界的情况
if (!barFirstTimeMove.value && n.length !== o.length) {
currentIndex.value = 0
}
// 用$nextTick等待视图更新完毕后再计算tab的局部信息否则可能因为tab还没生成就获取就会有问题
await nextTick()
init()
}
)
watch(
() => props.current,
(nVal, oVal) => {
// 视图更新后再执行移动操作、
nextTick(() => {
currentIndex.value = nVal
scrollByIndex()
})
},
{ immediate: true }
)
// 移动bar的样式
const tabBarStyle = computed(() => {
const style = {
width: props.barWidth + 'rpx',
transform: `translate(${scrollBarLeft.value}px, -100%)`,
// 滑块在页面渲染后第一次滑动时,无需动画效果
'transition-duration': `${barFirstTimeMove.value ? 0 : props.duration}s`,
'background-color': props.activeColor,
height: props.barHeight + 'rpx',
opacity: barFirstTimeMove.value ? 0 : 1,
// 设置一个很大的值,它会自动取能用的最大值,不用高度的一半,是因为高度可能是单数,会有小数出现
'border-radius': `${props.barHeight / 2}px`
}
Object.assign(style, props.barStyle)
return style
})
// tab的样式
const tabItemStyle = computed(() => {
return (index) => {
let style: any = {
height: props.height + 'rpx',
'line-height': props.height + 'rpx',
'font-size': props.fontSize + 'rpx',
padding: props.isScroll ? `0 ${props.gutter}rpx` : '',
flex: props.isScroll ? 'auto' : '1',
width: `${props.itemWidth}rpx`
}
// 字体加粗
if (index == currentIndex.value && props.bold) style.fontWeight = 'bold'
if (index == currentIndex.value) {
style.color = props.activeColor
// 给选中的tab item添加外部自定义的样式
style = Object.assign(style, props.activeItemStyle)
} else {
style.color = props.inactiveColor
}
return style
}
})
// const trackStyle = computed(() => {
// if (!props.animated) return ''
// return {
// left: -100 * currentIndex.value + '%',
// 'transition-duration': props.duration + 's',
// '-webkit-transition-duration': props.duration + 's',
// }
// })
const updateTabs = () => {
list.value = childrens.value.map((item) => {
const { name, dot, active, inited } = item.event
const { updateRender } = item
return {
name,
dot,
active,
inited,
updateRender
}
})
// nextTick(() => {
// init()
// })
}
// 设置一个init方法方便多处调用
const init = async () => {
// 获取tabs组件的尺寸信息
const tabRect = await getRect('#' + id.value, false, ctx)
// tabs组件距离屏幕左边的宽度
parentLeft.value = tabRect.left
// tabs组件的宽度
componentWidth.value = tabRect.width
getTabRect()
}
// 点击某一个tab菜单
const clickTab = (index) => {
// 点击当前活动tab不触发事件
if (index == currentIndex.value) return
nextTick(() => {
currentIndex.value = index
scrollByIndex()
})
// 发送事件给父组件
emit('change', index)
}
// 查询tab的布局信息
const getTabRect = () => {
// 创建节点查询
const query: any = uni.createSelectorQuery().in(ctx)
// 历遍所有tab这里是执行了查询最终使用exec()会一次性返回查询的数组结果
for (let i = 0; i < list.value.length; i++) {
// 只要size和rect两个参数
query.select(`#tab-item-${i}`).fields({
size: true,
rect: true
})
}
// 执行查询,一次性获取多个结果
query.exec((res) => {
tabQueryInfo.value = res
// 初始化滚动条和移动bar的位置
scrollByIndex()
})
}
// 滚动scroll-view让活动的tab处于屏幕的中间位置
const scrollByIndex = () => {
// 当前活动tab的布局信息有tab菜单的width和left(为元素左边界到父元素左边界的距离)等信息
const tabInfo = tabQueryInfo.value[currentIndex.value]
if (!tabInfo) return
// 活动tab的宽度
const tabWidth = tabInfo.width
// 活动item的左边到tabs组件左边的距离用item的left减去tabs的left
const offsetLeft = tabInfo.left - parentLeft.value
// 将活动的tabs-item移动到屏幕正中间实际上是对scroll-view的移动
const scrollLefts = offsetLeft - (componentWidth.value - tabWidth) / 2
scrollLeft.value = scrollLefts < 0 ? 0 : scrollLefts
// 当前活动item的中点点到左边的距离减去滑块宽度的一半即可得到滑块所需的移动距离
const left = tabInfo.left + tabInfo.width / 2 - parentLeft.value
// 计算当前活跃item到组件左边的距离
scrollBarLeft.value = left - uni.upx2px(props.barWidth) / 2
// 第一次移动滑块的时候barFirstTimeMove为true放到延时中将其设置false
// 延时是因为scrollBarLeft作用于computed计算时需要一个过程需否则导致出错
if (barFirstTimeMove.value == true) {
setTimeout(() => {
barFirstTimeMove.value = false
}, 100)
}
// 更新子组件的显示
childrens.value.forEach((item, ind) => {
const active = ind === currentIndex.value
if (active !== item.event.active || !item.event.inited) {
item.updateRender(active)
}
})
}
// 子组件调用此函数而产生的事件通信
const handleChange = (event, updateRender) => {
childrens.value.push({ event: event, updateRender })
}
// 手指触摸
const onTouchStart = (event) => {
if (!props.swipeable) return
swiping.value = true
touchStart(event)
}
// 手指滑动
const onTouchMove = (event) => {
if (!props.swipeable || !swiping.value) return
touchMove(event)
}
// 手指滑动结束
const onTouchEnd = () => {
if (!props.swipeable || !swiping.value) return
const minSwipeDistance = 50
if (touch.direction === 'horizontal' && touch.offsetX >= minSwipeDistance) {
let index,
len = list.value.length,
curIndex = currentIndex.value
if (touch.deltaX <= 0) {
curIndex >= len - 1 ? (index = 0) : (index = curIndex + 1)
} else {
curIndex <= 0 ? (index = len - 1) : (index = curIndex - 1)
}
nextTick(() => {
currentIndex.value = index
scrollByIndex()
})
// 发送事件给父组件
emit('change', index)
}
swiping.value = false
}
onMounted(() => {
updateTabs()
})
provide('handleChange', handleChange)
provide('updateTabs', updateTabs)
</script>
<style lang="scss" scoped>
/* #ifndef APP-NVUE */
::-webkit-scrollbar,
::-webkit-scrollbar,
::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
/* #endif */
.scroll-box {
height: 100%;
position: relative;
/* #ifdef MP-TOUTIAO */
white-space: nowrap;
/* #endif */
}
.tab-fixed {
position: sticky;
top: 0;
width: 100%;
}
/* #ifdef H5 */
// 通过样式穿透隐藏H5下scroll-view下的滚动条
scroll-view ::v-deep ::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
/* #endif */
.scroll-view {
width: 100%;
white-space: nowrap;
position: relative;
}
.tab-item {
position: relative;
/* #ifndef APP-NVUE */
display: inline-block;
/* #endif */
text-align: center;
transition-property: background-color, color;
}
.tab-bar {
position: absolute;
bottom: 6rpx;
}
.tabs-scorll-flex {
display: flex;
justify-content: space-between;
}
// .tab-content {
// overflow: hidden;
// .tab-track {
// position: relative;
// width: 100%;
// height: 100%;
// }
// .tab-animated {
// display: flex;
// transition-property: left;
// }
// }
</style>

View File

@@ -0,0 +1,37 @@
<template>
<view class="banner translate-y-0">
<u-swiper borderRadius="16" height="auto" :list="banner" mode="none" @click="handleClick"></u-swiper>
</view>
</template>
<script setup lang="ts">
import { navigateTo, jumpTo } from '@/utils/util'
const props = defineProps({
banner: {
type: Object,
default: () => ({})
},
styles: {
type: Object,
default: () => ({})
}
})
const handleClick = (index : any) => {
let linkArr = {}
let link = props.banner[index]['link']
if (link == "") return
if (link.indexOf('http') == 0) {
jumpTo(link)
} else {
linkArr = { path: link }
}
if (link.indexOf('/pages/team/index') == 0) {
navigateTo(linkArr, 'switchTab')
} else {
navigateTo(linkArr)
}
}
</script>
<style></style>

View File

@@ -0,0 +1,56 @@
<template>
<view
class="customer-service bg-white flex flex-col justify-center items-center mx-[36rpx] mt-[20rpx] rounded-lg px-[110rpx] pt-[100rpx] pb-[160rpx]"
>
<u-image width="280" height="280" :src="getImageUrl(content.qrcode)" />
<view v-if="content.title" class="text-lg mt-[14rpx] font-medium">{{ content.title }}</view>
<view v-if="content.time" class="text-content mt-[40rpx]"
>服务时间{{ content.time }}</view
>
<view v-if="content.mobile" class="text-content mt-[14rpx] flex flex-wrap">
客服电话{{ content.mobile }}
<!-- #ifdef H5 -->
<a class="ml-[10rpx] phone text-muted underline" :href="'tel:' + content.mobile">
拨打
</a>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="ml-[10rpx] phone text-muted underline" @click="handleCall">拨打</view>
<!-- #endif -->
</view>
<view class="mt-[100rpx] w-full">
<u-button
type="primary"
shape="circle"
@click="saveImageToPhotosAlbum(getImageUrl(content.qrcode))"
>
保存二维码图片
</u-button>
</view>
</view>
</template>
<script lang="ts" setup>
import { useAppStore } from '@/stores/app'
import { saveImageToPhotosAlbum } from '@/utils/file'
const props = defineProps({
content: {
type: Object,
default: () => ({})
},
styles: {
type: Object,
default: () => ({})
}
})
const { getImageUrl } = useAppStore()
const handleCall = () => {
uni.makePhoneCall({
phoneNumber: String(props.content.mobile)
})
}
</script>
<style lang="scss"></style>

View File

@@ -0,0 +1,67 @@
<template>
<view class="" v-if="items.length>0">
<navigator v-for="(item, index) in items" :key="index" hover-class="none"
:url="'/pages/item/detail?id='+item.id">
<view class="bg-[#2C326B] rounded-lg mb-[30rpx] overflow-hidden">
<view class="p-[20rpx] rounded-lg overflow-hidden">
<u-image width="100%" mode="widthFix" :src="item.image" alt="" />
</view>
<view class="p-[20rpx] pt-0">
<view class="font-bold truncate">
{{ item.title }}
</view>
<view class="flex justify-between items-center">
<view class="">
<view class="mt-[20rpx]">
<text class="mr-[10rpx] text-[#00D2E0]">{{t('item.cycle')}}:</text><text
class="mr-[10rpx] ">{{ item.cycle }}</text>
<text v-if="item.type==1 || item.type==2" class="">{{t('item.days')}}</text>
<text v-if="item.type==3 || item.type==4" class="">{{t('item.hour')}}</text>
</view>
<view class="mt-[10rpx]">
<text class="mr-[10rpx] text-[#00D2E0]">
<text v-if="item.type==1">{{t('item.dailyRate')}}:</text>
<text v-if="item.type==4">{{t('item.hourRate')}}:</text>
<text v-if="item.type==2 || item.type==3">{{t('item.totalRate')}}:</text>
</text>
<text class="">{{ item.rate }}%</text>
</view>
<view class="mt-[10rpx]">
<text class="mr-[10rpx] text-[#00D2E0]">{{t('item.limitVip')}}:</text>
<text class="">{{ item.vip_name }}</text>
</view>
<!-- <view class="mt-[10rpx]">
{{t("item.type" + item.type)}}
</view> -->
</view>
<view class="mt-[20rpx]">
<u-circle-progress width="180" bg-color="#2C326B" active-color="#4E55AF"
:percent="item.progress">
<view class="u-progress-content">
<view class="u-progress-dot"></view>
<text class='u-progress-info'>{{item.progress}}%</text>
</view>
</u-circle-progress>
</view>
</view>
</view>
</view>
</navigator>
</view>
</template>
<script setup lang="ts">
import { useAppStore } from '@/stores/app'
import { t } from '@/utils/util'
const props = defineProps({
items: {
type: Object,
default: () => ({})
},
})
</script>
<style></style>

View File

@@ -0,0 +1,38 @@
<template>
<view class="" v-if="market.length>0">
<view class="float-left bg-[#2C326B] rounded-lg w-[49%] mb-[20rpx] p-[20rpx]" v-for="(item,key) in market"
:key="item.id" :class="key%2==0?'mr-[2%]':''">
<view class="flex justify-start items-center">
<u-icon class="mr-[15rpx]" width="60rpx" height="60rpx" :name="item.logo"></u-icon>
<text class="">{{item.name}}</text>
</view>
<view class="text-xl font-bold my-[10rpx]">
<view v-if="item.rise>=0" class="text-[#17cdaf] mr-[20rpx]">
+{{item.rise}}
</view>
<view v-if="item.rise<0" class="text-[#f85963] mr-[20rpx]">
{{item.rise}}
</view>
</view>
<view class="text-muted">
<view v-if="item.rise>=0" class="text-[#17cdaf] mr-[20rpx]">
{{item.price}}
</view>
<view v-if="item.rise<0" class="text-[#f85963] mr-[20rpx]">
{{item.price}}
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
const props = defineProps({
market: {
type: Object,
default: () => ({})
},
})
</script>
<style></style>

View File

@@ -0,0 +1,54 @@
<template>
<div class="my-service bg-white ">
<div
class="title px-[30rpx] py-[20rpx] font-medium text-xl border-light border-solid border-0 border-b">
<div>{{t('robot.myOrder')}}</div>
</div>
<div class="flex flex-wrap pt-[40rpx] pb-[20rpx] px-[24rpx] ">
<div v-for="(item, index) in data.menus1" :key="index" class="flex flex-col items-center w-1/3 mb-[15px] text-center"
@click="handleClick(item.link)">
<u-image width="64" height="64" :src="item.image" alt="" />
<div class="mt-[7px]">{{ item.name }}</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { navigateTo } from '@/utils/util'
import { reactive } from 'vue'
import { t } from '@/utils/util'
const data = reactive({
menus1: [
{
"image": "/static/images/robot/pending.png",
"name": t('robot.orderStatus5'),
"link": {
"path": "/pages/robot/record?status=5",
"type": "shop"
}
},
{
"image": "/static/images/robot/paying.png",
"name": t('robot.orderStatus4'),
"link": {
"path": "/pages/robot/record?status=4",
"type": "shop"
}
},
{
"image": "/static/images/robot/finish.png",
"name": t('robot.orderStatus1'),
"link": {
"path": "/pages/robot/record?status=1",
"type": "shop"
}
}
]
})
const handleClick = (link : any) => {
navigateTo(link)
}
</script>
<style lang="scss"></style>

View File

@@ -0,0 +1,38 @@
<template>
<view class="bg-[#2C326B] rounded-lg mx-[30rpx] py-[30rpx] px-[30rpx]" v-if="navs.length>0">
<view class="">
<view v-for="(item, index) in navs" :key="index" class="flex justify-between items-center"
:class="index<(navs.length-1)?'mb-[30rpx]':''" @click="handleClick(item.link)">
<view class="flex justify-start items-center">
<u-image width="26px" height="26px" :src="item.image" alt="" />
<view class="ml-[30rpx]">{{ item.name }}</view>
</view>
<view class="">
<u-icon color="#A1DBF5" size="32" name="arrow-right"></u-icon>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { navigateTo, jumpTo } from '@/utils/util'
const props = defineProps({
navs: {
type: Object,
default: () => ({})
},
})
const handleClick = (link : any) => {
let linkArr = {}
if (link.indexOf('http') == 0) {
jumpTo(link)
} else {
linkArr = { path: link }
}
navigateTo(linkArr)
}
</script>
<style lang="scss"></style>

View File

@@ -0,0 +1,44 @@
<template>
<view class="nav pt-[40rpx] " v-if="navs.length>0">
<view class="nav-item flex flex-wrap">
<view
v-for="(item, index) in navs"
:key="index"
class="flex flex-col items-center w-1/4 mb-[40rpx]"
@click="handleClick(item.link)"
>
<u-image width="46px" height="46px" :src="item.image" alt="" />
<view class="mt-[14rpx] text-center text-[#F7F7F7]">{{ item.name }}</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { useAppStore } from '@/stores/app'
import { navigateTo,jumpTo } from '@/utils/util'
const props = defineProps({
navs: {
type: Object,
default: () => ({})
},
})
const handleClick = (link: any) => {
let linkArr = {}
if (link.indexOf('http') == 0) {
jumpTo(link)
} else {
linkArr = { path: link }
}
if(link.indexOf('/pages/team/index') == 0){
navigateTo(linkArr, 'switchTab')
}else{
navigateTo(linkArr)
}
}
</script>
<style></style>

View File

@@ -0,0 +1,13 @@
<template>
<navigator
url="/pages/search/search"
class="search px-[24rpx] py-[14rpx] bg-white"
hover-class="none"
>
<u-search placeholder="请输入关键词搜索" disabled :show-action="false"></u-search>
</navigator>
</template>
<script setup lang="ts"></script>
<style></style>

View File

@@ -0,0 +1,44 @@
<template>
<view class="bg-[#2C326B] mx-[30rpx] px-[30rpx] py-[40rpx] rounded-lg">
<view class="flex justify-between">
<view class="w-1/2 text-center">
<view class="text-2xl text-[#A1DBF5] mb-[10rpx]">
{{getCurrency()}}{{formatMoney(user.user_money)}} {{getCurrency2()}}
</view>
<view class="">
{{t('user.userBalance')}}
</view>
</view>
<view class="">
|
</view>
<view class="w-1/2 text-center">
<view class="text-2xl text-[#A1DBF5] mb-[10rpx]">
{{user.user_point}}
</view>
<view class="">
{{t('user.userPoint')}}
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { getCurrency,getCurrency2, formatMoney, t } from '@/utils/util'
const props = defineProps({
user: {
type: Object,
default: () => ({})
},
})
const navigateTo = (url : string) => {
uni.navigateTo({
url
})
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,42 @@
<template>
<view class="banner h-[200rpx] mx-[30rpx] translate-y-0">
<swiper class="swiper h-full" :indicator-dots="banner.length > 0" indicator-active-color="#4173ff"
:autoplay="true">
<swiper-item v-for="(item, index) in banner" :key="index" @click="handleClick(item.link)">
<u-image mode="aspectFit" width="100%" height="100%" :src="item.image" />
</swiper-item>
</swiper>
</view>
</template>
<script setup lang="ts">
import { useAppStore } from '@/stores/app'
import { navigateTo,jumpTo } from '@/utils/util'
const props = defineProps({
banner: {
type: Object,
default: () => ({})
},
styles: {
type: Object,
default: () => ({})
}
})
const { getImageUrl } = useAppStore()
const handleClick = (link : any) => {
let linkArr = {}
if (link.indexOf('http') == 0) {
jumpTo(link)
} else {
linkArr = { path: link }
}
if(link.indexOf('/pages/team/index') == 0){
navigateTo(linkArr, 'switchTab')
}else{
navigateTo(linkArr)
}
}
</script>
<style></style>

View File

@@ -0,0 +1,47 @@
<template>
<view class="px-[30rpx] mb-[40rpx]">
<view class="flex">
<u-avatar :src="user.avatar" :size="120"></u-avatar>
<view class="ml-[20rpx]">
<view class="text-2xl font-bold mb-[20rpx]">{{ user.mobile }}</view>
<view class="flex mb-[20rpx]">
<view class="flex items-center justify-center" @click.stop="copyText(user.sn)">
<text class="mr-[10rpx]">SN:</text>
<text class="">{{ user.sn }}</text>
<u-icon class="ml-[15rpx]" size="26" name="/static/images/common/copy.png"></u-icon>
</view>
</view>
<view class="text-[#00D2E0] flex items-center">
<u-icon class="mr-[10rpx]" size="42" :name="member.logo"></u-icon>
<text class="">{{ member.name }}</text>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { useCopy } from '@/hooks/useCopy'
import { t } from '@/utils/util'
const props = defineProps({
user: {
type: Object,
default: () => ({})
},
member: {
type: Object,
default: () => ({})
}
})
const { copy } = useCopy()
const copyText = async (text : string) => {
copy(text);
uni.$u.toast(t('common.copySuccess'))
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,46 @@
<template>
<view class="mx-[30rpx] relative overflow-hidden rounded-lg">
<view class="w-full h-[200rpx]">
<u-image width="100%" height="200rpx" src="/static/images/user/vip_bg.png"></u-image>
</view>
<view class="absolute w-full top-0 flex items-center justify-between px-[30rpx] py-[40rpx]">
<view class="">
<view class="flex items-center justify-between text-[#FFE4BC] mb-[30rpx] ">
<u-icon size="32" name="/static/images/user/viplogo.png"></u-icon>
<view class="ml-[10rpx]">
{{t('vip.unlocked')}}
</view>
</view>
<view class="text-[#FFFFFF] text-2xl">
{{ member.name }}
</view>
</view>
<view class="">
<navigator hover-class="none" url="/pages/user_vip/index">
<view class="text-[#FFE4BC]">
{{t('vip.upgrade')}}
</view>
</navigator>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { t } from '@/utils/util'
const props = defineProps({
member: {
type: Object,
default: () => ({})
}
})
</script>
<style>
.vip-btn {
background-color: #ffd200;
}
</style>

View File

@@ -0,0 +1,5 @@
//菜单主题类型
export enum AgreementEnum {
PRIVACY = 'privacy',
SERVICE = 'service'
}

49
src/enums/appEnums.ts Normal file
View File

@@ -0,0 +1,49 @@
//菜单主题类型
export enum ThemeEnum {
LIGHT = 'light',
DARK = 'dark'
}
// 客户端
export enum ClientEnum {
MP_WEIXIN = 1, // 微信-小程序
OA_WEIXIN = 2, // 微信-公众号
H5 = 3, // H5
IOS = 5, //苹果
ANDROID = 6 //安卓
}
export enum SMSEnum {
LOGIN = 'YZMDL',
BIND_MOBILE = 'BDSJHM',
CHANGE_MOBILE = 'BGSJHM',
FIND_PASSWORD = 'ZHDLMM'
}
export enum SearchTypeEnum {
HISTORY = 'history'
}
// 用户资料
export enum FieldType {
NONE = '',
AVATAR = 'avatar',
USERNAME = 'account',
NICKNAME = 'nickname',
SEX = 'sex'
}
// 支付结果
export enum PayStatusEnum {
SUCCESS = 'success',
FAIL = 'fail',
PENDING = 'pending'
}
// 页面状态
export enum PageStatusEnum {
LOADING = 'loading', // 加载中
NORMAL = 'normal', // 正常
ERROR = 'error', // 异常
EMPTY = 'empty' // 为空
}

View File

@@ -0,0 +1,11 @@
// 本地缓冲key
//token
export const TOKEN_KEY = 'token'
// 搜索历史记录
export const HISTORY = 'history'
export const BACK_URL = 'back_url'
export const PAY_STATUS_EVENT = 'event:payStatus'

22
src/enums/requestEnums.ts Normal file
View File

@@ -0,0 +1,22 @@
export enum ContentTypeEnum {
// json
JSON = 'application/json;charset=UTF-8',
// form-data 上传资源(图片,视频)
FORM_DATA = 'multipart/form-data;charset=UTF-8'
}
export enum RequestMethodsEnum {
GET = 'GET',
POST = 'POST'
}
export enum RequestCodeEnum {
SUCCESS = 1, //成功
FAILED = 0, // 失败
TOKEN_INVALID = -1 // TOKEN参数无效
}
export enum RequestErrMsgEnum {
ABORT = 'request:fail abort',
TIMEOUT = 'request:fail timeout'
}

25
src/hooks/useCopy.ts Normal file
View File

@@ -0,0 +1,25 @@
export function useCopy() {
const copy = (text: string) => {
// #ifdef H5
let transfer = document.createElement('input');
document.body.appendChild(transfer);
transfer.value = text;
transfer.select();
if (document.execCommand('copy')) {
document.execCommand('copy');
}
transfer.blur();
document.body.removeChild(transfer);
// #endif
// #ifndef H5
uni.setClipboardData({
data: String(text),
showToast:false
})
// #endif
}
return {
copy
}
}

21
src/hooks/useLockFn.ts Normal file
View File

@@ -0,0 +1,21 @@
import { ref } from 'vue'
export function useLockFn(fn: (...args: any[]) => Promise<any>) {
const isLock = ref(false)
const lockFn = async (...args: any[]) => {
if (isLock.value) return
isLock.value = true
try {
const res = await fn(...args)
isLock.value = false
return res
} catch (e) {
isLock.value = false
throw e
}
}
return {
isLock,
lockFn
}
}

72
src/hooks/useTouch.ts Normal file
View File

@@ -0,0 +1,72 @@
import { reactive } from 'vue'
/**
* @description 触碰屏幕钩子函数
* @return { Function } 暴露钩子
*/
export function useTouch() {
// 最小移动距离
const MIN_DISTANCE = 10
const touch = reactive({
direction: '',
deltaX: 0,
deltaY: 0,
offsetX: 0,
offsetY: 0
})
/**
* @description 计算距离
* @return { string } 空字符串
*/
const getDirection = (x: number, y: number) => {
if (x > y && x > MIN_DISTANCE) {
return 'horizontal'
}
if (y > x && y > MIN_DISTANCE) {
return 'vertical'
}
return ''
}
/**
* @description 重置参数
*/
const resetTouchStatus = () => {
touch.direction = ''
touch.deltaX = 0
touch.deltaY = 0
touch.offsetX = 0
touch.offsetY = 0
}
/**
* @description 触发
*/
const touchStart = (event: any) => {
resetTouchStatus()
const events = event.touches[0]
touch.startX = events.clientX
touch.startY = events.clientY
}
/**
* @description 移动
*/
const touchMove = (event: any) => {
const events = event.touches[0]
touch.deltaX = events.clientX - touch.startX
touch.deltaY = events.clientY - touch.startY
touch.offsetX = Math.abs(touch.deltaX)
touch.offsetY = Math.abs(touch.deltaY)
touch.direction = touch.direction || getDirection(touch.offsetX, touch.offsetY)
}
return {
touch,
resetTouchStatus,
touchStart,
touchMove
}
}

16
src/main.ts Normal file
View File

@@ -0,0 +1,16 @@
import { createSSRApp } from 'vue'
import App from './App.vue'
import plugins from './plugins'
import { setupRouter } from './router'
import './styles/index.scss'
export function createApp() {
const app = createSSRApp(App)
Promise.resolve().then(() => {
setupRouter()
})
app.use(plugins)
return {
app
}
}

92
src/manifest.json Normal file
View File

@@ -0,0 +1,92 @@
{
"name" : "BD Market",
"appid" : "__UNI__F98ABA2",
"description" : "",
"versionName" : "1.0.2",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"compatible" : {
"ignoreVersion" : true
},
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {
"VideoPlayer" : {}
},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
],
"minSdkVersion" : 21,
"abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ]
},
/* ios */
"ios" : {
"dSYMs" : false
},
/* SDK */
"sdkConfigs" : {
"ad" : {}
}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wx386a75e518b38935",
"setting" : {
"urlCheck" : false,
"es6" : true,
"minified" : true
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "3",
"h5" : {
"router" : {
"mode" : "history",
"base" : "/"
},
"title" : "Loading..."
}
}

View File

@@ -0,0 +1,159 @@
<template>
<view>
<u-navbar :back-text="t('recharge.title')" :back-text-style="{color: '#FFFFFF'}" :background="{backgroundColor: '#4E55AF'}" back-icon-color="#FFFFFF" >
<view class="flex justify-end w-full pr-[20rpx] relative">
<navigator hover-class="none" url="/packages/pages/recharge/record">
<u-icon size="40" name="/static/images/common/record.png"></u-icon>
</navigator>
</view>
</u-navbar>
<view class="bg-[#4E55AF] px-[30rpx] pt-[50rpx] pb-[15rpx]">
<view class="flex flex-wrap">
<view class="flex flex-col items-center w-1/5" v-for="(item, index) in data.money_list" :key="index"
@click="moneyChange(item)">
<view class="item rounded-md px-[20rpx] py-[10rpx] bg-[#2C326B] mb-[15px]">
{{getCurrency()}}{{item}}
</view>
</view>
</view>
</view>
<view class="common-money my-[20rpx] mx-[30rpx] py-[20rpx] ">
<view class=" mb-[30rpx]">
{{t('recharge.rechargeMoney')}}
</view>
<view class="bg-[#3d4277] rounded-lg px-[20rpx] py-[10rpx]">
<u-input v-model="data.money" type="number" :border="false"
:placeholder="t('recharge.rechargeMoneyPlaceholder')" />
</view>
</view>
<view class="common-money my-[20rpx] px-[30rpx] py-[20rpx] mb-[80px]">
<view class=" mb-[30rpx]">
{{t('recharge.rechargeMethod')}}
</view>
<view class="">
<view class="flex justify-between items-center bg-[#2C326B] rounded-lg p-[20rpx] mb-[30rpx]" v-for="(item, index) in data.method_list"
:key="index" @click="methodChange(item)">
<view class="flex justify-start items-center">
<view class="">
<u-icon class="mr-[20rpx]" size="64" :name="item.logo"></u-icon>
</view>
<view class="">
{{item.name}}
</view>
</view>
<view class="">
<u-icon v-if="item.id==data.method_id" size="32" class="check-box"
name="checkbox-mark"></u-icon>
</view>
</view>
</view>
</view>
<view class="h-[10rpx]">
</view>
<view class="fixed flex bottom-0 w-full py-[30rpx] px-[50rpx] justify-between z-10 leading-loose max-w-[750px] bg-[#2C326B]">
<view class="money-tips">
{{t('recharge.money')}}
<text class="money font-bold text-2xl">{{getCurrency()}}{{data.money}} {{getCurrency2()}}</text>
</view>
<view class="">
<u-button type="primary" size="medium" shape="circle" :disabled="data.money == ''||data.method_type == 0"
@click="submitHandle"> {{t('recharge.rechargeBtn')}} </u-button>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { reactive, ref, nextTick } from 'vue'
import { rechargeMethod, commonMoney, rechargeConfig } from '@/api/finance'
import { getCurrency,getCurrency2, formatMoney, t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('recharge.title') })
const data = reactive({
money: '',
method_id: 0,
method_type: 0,//1USDT2扫码3银行卡6波场7自定义地址
money_list: [],
method_list: [],
min: 0,
})
const methodChange = (item : any) => {
data.method_type = item.type;
data.method_id = item.id;
}
const moneyChange = (money : string) => {
data.money = money;
}
const submitHandle = () => {
if (data.money == "") return uni.$u.toast(t('recharge.rechargeMoneyEmpty'))
//充值金额判断
if (parseFloat(data.money) < data.min) return uni.$u.toast(t('recharge.minMoney') + getCurrency() + data.min + getCurrency2())
if (data.method_type == 0) {
return uni.$u.toast(t('recharge.rechargeMethodEmpty'))
}
// 使用正则表达式匹配小数
var decimalMatch = data.money.match(/\.(\d+)/);
if (decimalMatch) {
if (decimalMatch[1].length > 2) {
data.money = formatMoney(data.money);
}
}
let params = '?id=' + data.method_id + '&money=' + data.money;
if (data.method_type == 1 || data.method_type == 2) {
uni.redirectTo({
url: '/packages/pages/recharge/recharge_qrcode' + params
})
} else if (data.method_type == 3) {
uni.redirectTo({
url: '/packages/pages/recharge/recharge_bank' + params
})
} else if (data.method_type == 4) {
uni.redirectTo({
url: '/packages/pages/recharge/recharge_online' + params
})
} else if (data.method_type == 5) {
uni.navigateTo({
url: '/packages/pages/recharge/recharge_udun' + params
})
} else if (data.method_type == 6) {
uni.navigateTo({
url: '/packages/pages/recharge/recharge_tron' + params
})
} else if (data.method_type == 7) {
uni.navigateTo({
url: '/packages/pages/recharge/recharge_address' + params
})
}
}
const getData = async () => {
const res = await rechargeConfig()
if(res.need_bing_wallet == 1){
uni.redirectTo({
url: '/packages/pages/withdraw/account'
})
return
}
data.min = res.min;
data.money_list = await commonMoney()
const apiData = await rechargeMethod()
if (apiData.length > 0) {
data.method_id = apiData[0]['id']
data.method_type = apiData[0]['type']
}
data.method_list = apiData
}
getData()
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,120 @@
<template>
<view>
<h-dark title=""></h-dark>
<view class="bg-[#4E55AF] px-[30rpx] pt-[30rpx] pb-[300rpx]">
<view class=" flex flex-col items-center">
<u-image width="300" height="180" mode="widthFix"
src="/static/images/withdraw/withdraw_bg.png"></u-image>
</view>
</view>
<view class="bg-[#2C326B] rounded-lg p-[50rpx] mx-[30rpx] mt-[-260rpx]">
<view class="flex justify-between mb-[40rpx]">
<view class="w-1/4">
{{t('recharge.rechargeMethod')}}
</view>
<view class="w-3/4 text-right ">
{{orderData.method_name}}
</view>
</view>
<view class="flex justify-between mb-[40rpx]">
<view class="w-1/4">
{{t('orderDetail.status')}}
</view>
<view v-if="orderData.status==1" class="w-3/4 text-right text-success ">
{{t('tabs.finish')}}
</view>
<view v-if="orderData.status==0" class="w-3/4 text-right text-primary ">
{{t('tabs.pending')}}
</view>
<view v-if="orderData.status==2" class="w-3/4 text-right text-error ">
{{t('tabs.fail')}}
</view>
</view>
<view class="flex justify-between mb-[40rpx]">
<view class="w-1/4">
{{t('orderDetail.orderSn')}}
</view>
<view class="w-3/4 text-right ">
{{orderData.sn}}
</view>
</view>
<view class="flex justify-between mb-[40rpx]">
<view class="w-1/4">
{{t('orderDetail.createTime')}}
</view>
<view class="w-3/4 text-right ">
{{orderData.create_time}}
</view>
</view>
<view class="flex justify-between mb-[40rpx]" v-if="orderData.status==2">
<view class="w-1/4">
{{t('orderDetail.remark')}}
</view>
<view class="w-3/4 text-error mt-[12rpx]">
{{t('recharge.rechargeFailTips')}}
</view>
</view>
<u-line class="text-info" margin="20rpx 0 40rpx 0" border-style="dashed" />
<view class="flex justify-between">
<view class="w-1/4">
{{t('orderDetail.money')}}
</view>
<view class="w-3/4 text-right">
<view v-if="orderData.status==1" class="text-lg font-bold text-success">
{{getCurrency()}}{{formatMoney(orderData.amount)}} {{getCurrency2()}}
</view>
<view v-if="orderData.status==0" class="text-lg font-bold text-primary">
{{getCurrency()}}{{formatMoney(orderData.amount)}} {{getCurrency2()}}
</view>
<view v-if="orderData.status==2" class="text-lg font-bold text-error">
{{getCurrency()}}{{formatMoney(orderData.amount)}} {{getCurrency2()}}
</view>
<view class="mt-[10rpx]" v-if="orderData.rate && orderData.rate != 1">
{{orderData.amount_act}} {{orderData.symbol}}
</view>
</view>
</view>
<view class="flex justify-between mt-[40rpx]" v-if="orderData.charge>0">
<view class="w-1/4">
{{t('orderDetail.charge')}}
</view>
<view class="w-3/4 text-right ">
{{getCurrency()}}{{formatMoney(orderData.charge)}} {{getCurrency2()}}
</view>
</view>
</view>
<view class="max-w-[750px] fixed bottom-[50rpx] w-4/5 ml-[10%]">
<u-button type="primary" @click="handleBack"> {{t('common.back')}} </u-button>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { rechargeRecordDetail } from '@/api/finance'
import { getCurrency,getCurrency2, formatDate, formatMoney,t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('recharge.title') })
const orderData = ref<any>({})
let orderId = ''
const getData = async (id) => {
orderData.value = await rechargeRecordDetail({ id, date: formatDate(Math.round(Date.now() / 1000)) })
}
const handleBack = async () => {
let canNavBack = getCurrentPages()
if (canNavBack && canNavBack.length > 1) {
uni.navigateBack()
} else {
history.back();
}
}
onLoad((options : any) => {
orderId = options.id
getData(orderId)
})
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,149 @@
<template>
<view>
<h-dark :title="t('recharge.title')"></h-dark>
<view class="mx-[30rpx] mt-[20rpx]">
<view class="flex items-center bg-[#2C326B] rounded-lg px-[50rpx] py-[20rpx]">
<u-icon size="80" :name="method.logo"></u-icon>
<view class="ml-[16rpx]">{{method.name}}</view>
</view>
<view class="bg-[#2C326B] rounded-lg mt-[20rpx] p-[30rpx]">
<view class="text-center">
<view class="mb-[16rpx] text-[#00D2E0]">{{t('recharge.address')}}</view>
<view class="bg-[#3d4277] rounded p-[20rpx] rounded-lg">
<view class="flex break-all items-center justify-center" @click.stop="copyText(method.address)">
<view class="mr-[10rpx]">{{method.address}}</view>
<view>
<u-icon size="26" name="/static/images/common/copy.png">
</u-icon>
</view>
</view>
</view>
</view>
</view>
<view class="bg-[#2C326B] rounded-lg mt-[20rpx] px-[30rpx] py-[30rpx]">
<view class="text-[#00D2E0] mb-[10rpx]">
{{t('recharge.rechargeMoney')}}
</view>
<view class="">
<view class="font-bold text-lg py-[10rpx]">
{{getCurrency()}}{{formData.money}} {{getCurrency2()}}
<text class="text-[#00D2E0]" v-if="method.rate!=1">(
{{formatMoney(formData.money,method.symbol,method.rate,method.precision)}})</text>
</view>
</view>
<view class="text-[#00D2E0] my-[20rpx]" v-if="method.is_voucher">
{{t('recharge.voucher')}}
</view>
<view class="" v-if="method.is_voucher">
<view class="bg-[#3d4277] rounded w-full flex justify-center">
<u-upload class="" ref="uploader" :custom-btn="true" :deletable="false" :auto-upload="false"
max-count="1" :max-size="20*1024*1024" v-model="formData.voucher">
<template v-slot:addBtn>
<view class="flex items-center justify-center slot-btn w-[180rpx] h-[180rpx] "
hover-class="slot-btn__hover" hover-stay-time="150">
<u-icon name="photo" size="80" class="text-[#00D2E0]"></u-icon>
</view>
</template>
</u-upload>
</view>
</view>
</view>
<view class="my-[30rpx]">
<u-button type="primary" @click="handleConfirm"> {{t('common.submit')}} </u-button>
</view>
<view class="leading-loose">
<view class="">
{{replaceStr(t('recharge.udunTips1'),'{code}',method.protocol)}}
</view>
<view class="">
{{t('recharge.udunTips2')}}
</view>
<view class="">
{{t('recharge.udunTips3')}}
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { rechargeMethodDetailAddress, recharge } from '@/api/finance'
import { uploadImage } from '@/api/app'
import { useUserStore } from '@/stores/user'
import { formatDate, formatMoney, getCurrency,getCurrency2, replaceStr } from '@/utils/util'
import { useCopy } from '@/hooks/useCopy'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('recharge.title') })
const { copy } = useCopy()
const userStore = useUserStore()
const uploader = ref();
const method = ref<any>({})
const formData = reactive({
id: 0,
money: '',
address: '',
voucher: '',
date: formatDate(Math.round(Date.now() / 1000))
})
const copyText = async (text : string) => {
copy(text);
uni.$u.toast(t('common.copySuccess'))
}
const handleConfirm = async () => {
if (method.value.is_voucher) {
if (uploader.value.lists.length <= 0) return uni.$u.toast(t('recharge.voucherEmpty'))
const res : any = await uploadImage(uploader.value.lists[0].url, userStore.temToken!)
formData.voucher = res.url;
}
await recharge(formData)
uni.redirectTo({
url: '/pages/common/successful?type=0'
})
}
const getData = async (id : number) => {
const res = await rechargeMethodDetailAddress({ id })
//计算每日获取次数
if (!uni.getStorageSync('address_avail_num_' + formData.id)) {
uni.setStorageSync('address_avail_num_' + formData.id, res.date + ',' + 1);
} else {
//对比时间
var str = uni.getStorageSync('address_avail_num_' + formData.id);
const str_arr = str.split(',');
if (str_arr[0] == res.date) {
if (parseInt(str_arr[1]) >= res.avail_num) {
uni.$u.toast(t('recharge.addressNumTips'))
let canNavBack = getCurrentPages()
if (canNavBack && canNavBack.length > 1) {
uni.navigateBack()
} else {
history.back();
}
return;
}
uni.setStorageSync('address_avail_num_' + formData.id, res.date + ',' + (parseInt(str_arr[1]) + 1));
} else {
uni.setStorageSync('address_avail_num_' + formData.id, res.date + ',' + 1);
}
}
method.value = res;
formData.address = res.address;
}
onLoad((options : any) => {
formData.id = options.id
formData.money = options.money
getData(formData.id)
})
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,126 @@
<template>
<view>
<h-dark :title="t('recharge.title')"></h-dark>
<view class="flex items-center bg-white px-[50rpx] py-[20rpx] mt-[12rpx]">
<u-icon size="80" :name="method.logo"></u-icon>
<view class="ml-[16rpx]">{{method.name}}</view>
</view>
<view class="bg-white mt-[20rpx] p-[40rpx]">
<view class="mb-[15rpx] mt-[30rpx]">
<view class="mb-[16rpx] text-muted">{{t('recharge.bankName')}}</view>
<view class="bg-[#f6f7fb] rounded p-[20rpx]">
<view class="flex break-all items-center justify-between ">
<view>{{method.bank_name}}</view>
<view @click.stop="copyText(method.bank_name)"><u-icon size="26"
name="/static/images/common/copy_1.png"></u-icon></view>
</view>
</view>
</view>
<view class="mb-[15rpx] mt-[30rpx]">
<view class="mb-[16rpx] text-muted">{{t('recharge.bankUsername')}}</view>
<view class="bg-[#f6f7fb] rounded p-[20rpx]">
<view class="flex break-all items-center justify-between ">
<view>{{method.bank_username}}</view>
<view @click.stop="copyText(method.bank_username)"><u-icon size="26"
name="/static/images/common/copy_1.png"></u-icon></view>
</view>
</view>
</view>
<view class="mb-[15rpx] mt-[30rpx]">
<view class="mb-[16rpx] text-muted">{{t('recharge.bankAccount')}}</view>
<view class="bg-[#f6f7fb] rounded p-[20rpx]">
<view class="flex break-all items-center justify-between ">
<view>{{method.account}}</view>
<view @click.stop="copyText(method.account)"><u-icon size="26"
name="/static/images/common/copy_1.png"></u-icon></view>
</view>
</view>
</view>
</view>
<view class="bg-white mt-[20rpx] px-[40rpx] py-[30rpx]">
<view class="text-muted mb-[16rpx]">
{{t('recharge.rechargeMoney')}}
</view>
<view class="font-bold text-lg mb-[50rpx]">
{{getCurrency()}}{{formData.money}}
<text class="text-muted" v-if="method.rate!=1">(
{{formatMoney(formData.money,method.symbol,method.rate,method.precision)}})</text>
</view>
<view class="text-muted mb-[16rpx]">
{{t('recharge.voucher')}}
</view>
<view class="">
<view class="bg-[#f6f7fb] rounded w-full flex justify-center">
<u-upload class="" ref="uploader" :custom-btn="true" :deletable="false" :auto-upload="false"
max-count="1" :max-size="20*1024*1024" v-model="formData.voucher">
<template v-slot:addBtn>
<view class="flex items-center justify-center slot-btn w-[180rpx] h-[180rpx] "
hover-class="slot-btn__hover" hover-stay-time="150">
<u-icon name="photo" size="80" class="text-muted"></u-icon>
</view>
</template>
</u-upload>
</view>
</view>
</view>
<view class=" px-[40rpx] my-[80rpx] pb-[50rpx]">
<u-button type="primary" @click="handleConfirm"> {{t('common.submit')}} </u-button>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { rechargeMethodDetail, recharge } from '@/api/finance'
import { uploadImage } from '@/api/app'
import { useUserStore } from '@/stores/user'
import { formatDate, formatMoney, getCurrency } from '@/utils/util'
import { useCopy } from '@/hooks/useCopy'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('recharge.title') })
const { copy } = useCopy()
const userStore = useUserStore()
const uploader = ref();
const method = ref<any>({})
const formData = reactive({
id: 0,
money: '',
voucher: '',
date: formatDate(Math.round(Date.now() / 1000))
})
const copyText = async (text : string) => {
copy(text);
uni.$u.toast(t('common.copySuccess'))
}
const handleConfirm = async () => {
if (uploader.value.lists.length <= 0) return uni.$u.toast(t('recharge.voucherEmpty'))
const res : any = await uploadImage(uploader.value.lists[0].url, userStore.temToken!)
formData.voucher = res.url;
await recharge(formData)
uni.redirectTo({
url: '/pages/common/successful?type=0'
})
}
const getData = async (id : number) => {
method.value = await rechargeMethodDetail({ id })
}
onLoad((options : any) => {
formData.id = options.id
formData.money = options.money
getData(formData.id)
})
</script>
<style lang="scss">
.shadow-inner {
border-radius: 10px;
padding: 5px 10px;
box-shadow: inset 0px 1px 4px 0px rgb(0 0 0 / 0.3);
}
</style>

View File

@@ -0,0 +1,12 @@
<template>
<view>
<h-dark title=""></h-dark>
<view></view>
</view>
</template>
<script lang="ts" setup>
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,117 @@
<template>
<view>
<h-dark :title="t('recharge.title')"></h-dark>
<view class="mx-[30rpx] mt-[20rpx]">
<view class="flex items-center bg-[#2C326B] rounded-lg px-[50rpx] py-[20rpx]">
<u-icon size="80" :name="method.logo"></u-icon>
<view class="ml-[16rpx]">{{method.name}}</view>
</view>
<view class="bg-[#2C326B] rounded-lg mt-[20rpx] p-[30rpx]">
<view class="flex justify-center">
<view class="rounded-lg overflow-hidden">
<u-image width="180px" height="180px" :src="method.qrcode"></u-image>
</view>
</view>
<view class="text-center text-[#00D2E0] mt-[30rpx]">
<view class="mb-[16rpx]" v-if="method.type==1&&method.account!=''">{{t('recharge.address')}}</view>
<view class="mb-[16rpx]" v-if="method.type==2 &&method.account!=''">{{t('recharge.account')}}</view>
<view class="bg-[#3d4277] rounded p-[20rpx]">
<view class="flex break-all items-center justify-center" v-if="method.account!=''"
@click.stop="copyText(method.account)">
<view class="mr-[10rpx]">{{method.account}}</view>
<view>
<u-icon size="26" name="/static/images/common/copy.png">
</u-icon>
</view>
</view>
</view>
</view>
</view>
<view class="bg-[#2C326B] rounded-lg mt-[20rpx] px-[30rpx] py-[30rpx]">
<view class="text-[#00D2E0] mb-[10rpx]">
{{t('recharge.rechargeMoney')}}
</view>
<view class="mb-[10rpx]">
<view class="font-bold text-lg py-[10rpx]">
{{getCurrency()}}{{formData.money}} {{getCurrency2()}}
<text class="text-[#00D2E0]" v-if="method.rate!=1">(
{{formatMoney(formData.money,method.symbol,method.rate,method.precision)}})</text>
</view>
</view>
<view class="text-[#00D2E0] my-[20rpx]" v-if="method.is_voucher">
{{t('recharge.voucher')}}
</view>
<view class="" v-if="method.is_voucher">
<view class="bg-[#3d4277] rounded w-full flex justify-center">
<u-upload class="" ref="uploader" :custom-btn="true" :deletable="false" :auto-upload="false"
max-count="1" :max-size="20*1024*1024" v-model="formData.voucher">
<template v-slot:addBtn>
<view class="flex items-center justify-center slot-btn w-[180rpx] h-[180rpx] "
hover-class="slot-btn__hover" hover-stay-time="150">
<u-icon name="photo" size="80" class="text-[#00D2E0]"></u-icon>
</view>
</template>
</u-upload>
</view>
</view>
</view>
<view class=" px-[40rpx] my-[80rpx] pb-[50rpx]">
<u-button type="primary" @click="handleConfirm"> {{t('common.submit')}} </u-button>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { rechargeMethodDetail, recharge } from '@/api/finance'
import { uploadImage } from '@/api/app'
import { useUserStore } from '@/stores/user'
import { formatDate, formatMoney, getCurrency,getCurrency2 } from '@/utils/util'
import { useCopy } from '@/hooks/useCopy'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('recharge.title') })
const { copy } = useCopy()
const userStore = useUserStore()
const uploader = ref();
const method = ref<any>({})
const formData = reactive({
id: 0,
money: '',
voucher: '',
date: formatDate(Math.round(Date.now() / 1000))
})
const copyText = async (text : string) => {
copy(text);
uni.$u.toast(t('common.copySuccess'))
}
const handleConfirm = async () => {
if (method.value.is_voucher) {
if (uploader.value.lists.length <= 0) return uni.$u.toast(t('recharge.voucherEmpty'))
const res : any = await uploadImage(uploader.value.lists[0].url, userStore.temToken!)
formData.voucher = res.url;
}
await recharge(formData)
uni.redirectTo({
url: '/pages/common/successful?type=0'
})
}
const getData = async (id : number) => {
method.value = await rechargeMethodDetail({ id })
}
onLoad((options : any) => {
formData.id = options.id
formData.money = options.money
getData(formData.id)
})
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,69 @@
<template>
<view>
<h-dark :title="t('recharge.title')"></h-dark>
<view class="flex items-center bg-white px-[50rpx] py-[20rpx] mt-[12rpx]">
<u-icon size="80" :name="method.logo"></u-icon>
<view class="ml-[16rpx]">{{method.name}}</view>
</view>
<view class="bg-white mt-[20rpx] p-[40rpx]">
<view class="flex flex-col items-center">
<u-image width="180px" height="180px" :src="method.qrcode"></u-image>
</view>
<view class="text-center text-muted mt-[30rpx]">
<view class="mb-[16rpx]">{{t('recharge.address')}}</view>
<view class="bg-[#f6f7fb] rounded p-[20rpx]">
<view class="flex break-all items-center justify-center" v-if="method.address!=''"
@click.stop="copyText(method.address)">
<view>{{method.address}}</view>
<view><u-icon size="26" name="/static/images/common/copy_1.png"></u-icon></view>
</view>
</view>
</view>
</view>
<view class="px-[40rpx] mt-[50rpx] leading-loose font-bold">
<view class="text-error">
{{replaceStr(t('recharge.udunTips1'),'{code}',method.protocol)}}
</view>
<view class="text-error">
{{t('recharge.udunTips2')}}
</view>
<view class="text-error">
{{t('recharge.udunTips3')}}
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { rechargeMethodDetailTron } from '@/api/finance'
import { replaceStr } from '@/utils/util'
import { useCopy } from '@/hooks/useCopy'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('recharge.title') })
const { copy } = useCopy()
const method = ref<any>({})
const formData = reactive({
id: 0,
money: '',
})
const copyText = async (text : string) => {
copy(text);
uni.$u.toast(t('common.copySuccess'))
}
const getData = async (id : number) => {
method.value = await rechargeMethodDetailTron({ id })
}
onLoad((options : any) => {
formData.id = options.id
formData.money = options.money
getData(formData.id)
})
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,71 @@
<template>
<view>
<h-dark :title="t('recharge.title')"></h-dark>
<view class="mx-[30rpx] mt-[20rpx]">
<view class="flex items-center bg-[#2C326B] rounded-lg px-[50rpx] py-[20rpx]">
<u-icon size="80" :name="method.logo"></u-icon>
<view class="ml-[16rpx]">{{method.name}}</view>
</view>
<view class="bg-[#2C326B] rounded-lg mt-[20rpx] p-[30rpx]">
<view class="flex flex-col items-center">
<u-image width="180px" height="180px" :src="method.qrcode"></u-image>
</view>
<view class="text-center text-[#00D2E0] mt-[30rpx]">
<view class="mb-[16rpx]">{{t('recharge.address')}}</view>
<view class="bg-[#3d4277] rounded p-[20rpx]">
<view class="flex break-all items-center justify-center" v-if="method.address!=''">
<view>{{method.address}}</view>
<view @click.stop="copyText(method.address)"><u-icon size="26"
name="/static/images/common/copy.png"></u-icon></view>
</view>
</view>
</view>
</view>
<view class="mt-[50rpx] leading-loose">
<view class="">
{{replaceStr(t('recharge.udunTips1'),'{code}',method.protocol)}}
</view>
<view class="">
{{t('recharge.udunTips2')}}
</view>
<view class="">
{{t('recharge.udunTips3')}}
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { rechargeMethodDetailUdun } from '@/api/finance'
import { replaceStr } from '@/utils/util'
import { useCopy } from '@/hooks/useCopy'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('recharge.title') })
const { copy } = useCopy()
const method = ref<any>({})
const formData = reactive({
id: 0,
money: '',
})
const copyText = async (text : string) => {
copy(text);
uni.$u.toast(t('common.copySuccess'))
}
const getData = async (id : number) => {
method.value = await rechargeMethodDetailUdun({ id })
}
onLoad((options : any) => {
formData.id = options.id
formData.money = options.money
getData(formData.id)
})
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,84 @@
<template>
<z-paging :refresher-enabled="false" :show-loading-more-when-reload="true" loading-more-default-text=""
loading-more-loading-text="" loading-more-fail-text="" :empty-view-text="t('common.noData')"
:show-loading-more-no-more-view="false" ref="paging" v-model="dataList" @query="queryList"
class="max-w-[750px]">
<h-dark :title="t('recharge.titleRecord')"></h-dark>
<view class="mx-[30rpx] mt-[20rpx]">
<navigator v-for="item in dataList" :key="item.id" hover-class="none"
:url="'/packages/pages/recharge/order_detail?id='+item.id">
<view class="bg-[#2C326B] mb-[20rpx] rounded-lg px-[40rpx] py-[35rpx]">
<view class="flex justify-between mb-[20rpx]">
<view class="w-3/5">
<view class="">{{item.create_time}}</view>
</view>
<view class="text-right w-2/5">
<view class="font-bold ">
{{item.amount}}
</view>
<view class="mt-[10rpx] text-xs" v-if="item.rate != 1">
{{item.amount_act}} {{item.symbol}}
</view>
</view>
</view>
<view class="flex justify-between ">
<view class="text-[#00D2E0]">
<view class="">
{{item.method_name}}
</view>
<view v-if="item.status==2" class="text-xs text-error mt-[12rpx]">
{{tips.rechargeFailTips}}
</view>
</view>
<view v-if="item.status==1" class="text-success">
{{tips.finish}}
</view>
<view v-if="item.status==0" class="text-primary">
{{tips.pending}}
</view>
<view v-if="item.status==2" class="text-error">
{{tips.fail}}
</view>
</view>
</view>
</navigator>
</view>
</z-paging>
</template>
<script lang="ts" setup>
import { ref, shallowRef, reactive } from 'vue'
import { rechargeRecordLists } from '@/api/finance'
import { getCurrency,getCurrency2, formatDate, formatMoney } from '@/utils/util'
import { t } from '@/utils/util'
//为了提高渲染速度
const tips = reactive({
rechargeFailTips: t('recharge.rechargeFailTips'),
fail: t('tabs.fail'),
pending: t('tabs.pending'),
finish: t('tabs.finish'),
})
uni.setNavigationBarTitle({ title: t('recharge.titleRecord') })
const paging = shallowRef()
const dataList = ref<any[]>([])
const queryList = async (pageNo : number, pageSize : number) => {
try {
const data = await rechargeRecordLists({
page_no: pageNo,
page_size: pageSize,
date: formatDate(Math.round(Date.now() / 1000))
})
Object.keys(data.lists).forEach(key => {
data.lists[key]['amount'] = getCurrency() + formatMoney(data.lists[key]['amount']) + ' ' + getCurrency2()
})
paging.value.complete(data.lists)
} catch (error) {
paging.value.complete(false)
}
}
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,97 @@
<template>
<view>
<h-dark title=""></h-dark>
<view class="bg-[#4E55AF] px-[30rpx] pt-[30rpx] pb-[300rpx]">
<view class=" flex flex-col items-center">
<u-image width="300" height="180" mode="widthFix"
src="/static/images/withdraw/withdraw_bg.png"></u-image>
</view>
</view>
<view class="bg-[#2C326B] rounded-lg p-[50rpx] mx-[30rpx] mt-[-260rpx]">
<view class="flex justify-between mb-[40rpx]">
<view class="w-1/4">
{{t('orderDetail.money')}}
</view>
<view class="w-3/4 text-right">
<text class=" font-bold " :class="orderData.action==1?'text-success':'text-error'">
{{getCurrency()}}{{formatMoney(orderData.amount)}} {{getCurrency2()}}
</text>
</view>
</view>
<view class="flex justify-between mb-[40rpx]">
<view class="w-1/4">
{{t('orderDetail.name')}}
</view>
<view class="w-3/4 text-right ">
{{t("finance.type"+orderData.type)}}
</view>
</view>
<view class="flex justify-between mb-[40rpx]">
<view class="w-1/4">
{{t('orderDetail.type')}}
</view>
<view class="w-3/4 text-right " :class="orderData.action==1?'text-success':'text-error'">
{{orderData.action==1?t('tabs.in'):t('tabs.out')}}
</view>
</view>
<view class="flex justify-between mb-[40rpx]">
<view class="w-1/4">
{{t('orderDetail.orderSn')}}
</view>
<view class="w-3/4 text-right ">
{{orderData.sn}}
</view>
</view>
<view class="flex justify-between mb-[40rpx]">
<view class="w-1/4">
{{t('orderDetail.createTime')}}
</view>
<view class="w-3/4 text-right ">
{{orderData.create_time}}
</view>
</view>
<view class="flex justify-between mb-[40rpx]" v-if="orderData.remark && orderData.remark!=''">
<view class="w-1/4">
{{t('orderDetail.remark')}}
</view>
<view class="w-3/4 text-right text-error break-words">
{{orderData.remark}}
</view>
</view>
</view>
<view class="max-w-[750px] fixed bottom-[50rpx] w-4/5 ml-[10%]">
<u-button type="primary" @click="handleBack"> {{t('common.back')}} </u-button>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { financeDetail } from '@/api/finance'
import { getCurrency, getCurrency2,formatDate, formatMoney } from '@/utils/util'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('funds.assetDetails') })
const orderData = ref<any>({})
let orderId = ''
const getData = async (id) => {
orderData.value = await financeDetail({ id, date: formatDate(Math.round(Date.now() / 1000)) })
}
const handleBack = async () => {
let canNavBack = getCurrentPages()
if (canNavBack && canNavBack.length > 1) {
uni.navigateBack()
} else {
history.back();
}
}
onLoad((options : any) => {
orderId = options.id
getData(orderId)
})
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,106 @@
<template>
<z-paging :refresher-enabled="false" :show-loading-more-when-reload="true" loading-more-default-text=""
loading-more-loading-text="" loading-more-fail-text="" :empty-view-text="t('common.noData')"
:show-loading-more-no-more-view="false" ref="paging" v-model="dataList" @query="queryList"
class="max-w-[750px]">
<h-dark :title="t('funds.assetDetails')"></h-dark>
<view class="user-wallet">
<view class="bg-[#4E55AF] pb-[30rpx]">
<u-tabs show-bar="false" bg-color="#4E55AF" active-color="#A1DBF5" inactive-color="#FFFFFF" :is-scroll="false" :list="tabList" v-model="current" @change="changeType"></u-tabs>
</view>
<view class="pt-2.5 mx-[30rpx]">
<navigator v-for="item in dataList" :key="item.id" hover-class="none"
:url="'/packages/pages/user_wallet/order_detail?id='+item.id">
<view class="bg-[#2C326B] mb-[20rpx] rounded-lg px-[40rpx] py-[35rpx]">
<view class="flex justify-between">
<view class="w-3/5">
<view class="">{{item.create_time}}</view>
</view>
<view class="text-right w-2/5">
<text class="font-bold">
{{item.amount}}
</text>
</view>
</view>
<view class="flex justify-between mt-[16rpx]">
<view class="text-[#00D2E0]">
<view class="">
{{item.type}}
</view>
<view class="text-error mt-[12rpx]" v-if="item.remark">
{{item.remark}}
</view>
</view>
<view class="text-success" v-if="item.action==1">
{{tabList[1]['name']}}
</view>
<view class="text-error" v-if="item.action==2">
{{tabList[2]['name']}}
</view>
</view>
</view>
</navigator>
</view>
</view>
</z-paging>
</template>
<script lang="ts" setup>
import { ref, shallowRef } from 'vue'
import { financeLists } from '@/api/finance'
import { getUserInfo } from '@/api/user'
import { getCurrency,getCurrency2, formatDate, formatMoney } from '@/utils/util'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('funds.assetDetails') })
const tabList = ref([
{
name: t('tabs.all'),
type: ''
},
{
name: t('tabs.in'),
type: 1
},
{
name: t('tabs.out'),
type: 2
}
])
const paging = shallowRef()
const dataList = ref<any[]>([])
const current = ref(0)
const user = ref([])
const changeType = (index : number) => {
current.value = index
paging.value.reload()
}
const queryList = async (pageNo : number, pageSize : number) => {
try {
const action = tabList.value[current.value].type
const data = await financeLists({
frozen: '',
action: action,
page_no: pageNo,
page_size: pageSize,
date: formatDate(Math.round(Date.now() / 1000))
})
Object.keys(data.lists).forEach(key => {
data.lists[key]['amount'] = getCurrency() + formatMoney(data.lists[key]['amount']) + ' ' + getCurrency2()
data.lists[key]['type'] = t("finance.type" + data.lists[key]['type'])
})
paging.value.complete(data.lists)
} catch (error) {
paging.value.complete(false)
}
}
const getData = async () => {
user.value = await getUserInfo()
}
getData()
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,70 @@
<template>
<z-paging :refresher-enabled="false" :show-loading-more-when-reload="true" loading-more-default-text=""
loading-more-loading-text="" loading-more-fail-text="" :empty-view-text="t('common.noData')"
:show-loading-more-no-more-view="false" ref="paging" v-model="dataList" @query="queryList"
class="max-w-[750px]">
<h-dark :title="t('user.usedNoMoney')"></h-dark>
<view class="user-wallet">
<view class="pt-2.5">
<view v-for="item in dataList" :key="item.id" class="bg-white mb-[8px] px-[40rpx] py-[20rpx] border-solid border-b border-0 border-light ">
<view class="flex justify-between items-center">
<view class="">
<view class="">
<view class="">
{{item.type}}
</view>
<view class="text-error mt-[12rpx]" v-if="item.remark">
{{item.remark}}
</view>
</view>
<view class="flex justify-between mt-[16rpx]">
<view class=" text-muted mr-1">{{item.create_time}}</view>
</view>
</view>
<view class="text-right">
<text class="font-bold">
{{item.amount}}
</text>
</view>
</view>
</view>
</view>
</view>
</z-paging>
</template>
<script lang="ts" setup>
import { ref, shallowRef } from 'vue'
import { financeLists } from '@/api/finance'
import { getCurrency, formatDate, formatMoney } from '@/utils/util'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('user.usedNoMoney') })
const paging = shallowRef()
const dataList = ref<any[]>([])
const queryList = async (pageNo : number, pageSize : number) => {
try {
const data = await financeLists({
action: '',
frozen: 1,
page_no: pageNo,
page_size: pageSize,
date: formatDate(Math.round(Date.now() / 1000))
})
Object.keys(data.lists).forEach(key => {
data.lists[key]['amount'] = getCurrency() + formatMoney(data.lists[key]['amount'])
data.lists[key]['type'] = t("finance.type" + data.lists[key]['type'])
})
paging.value.complete(data.lists)
} catch (error) {
paging.value.complete(false)
}
}
</script>
<style lang="scss" scoped>
page {
background-color: #FFFFFF;
}
</style>

View File

@@ -0,0 +1,82 @@
<template>
<view>
<h-dark :title="t('withdraw.withdrawAccountManage')"></h-dark>
<view class="mt-[40rpx]">
<u-cell-group>
<u-cell-item v-for="(method, index) in data.methods" :title="method.name"
:label="method.status?method.account:method.name" :arrow="false"
hover-class="cell-hover-class">
<template v-slot:icon>
<u-icon class="mr-[15rpx]" size="100" :name="method.logo"></u-icon>
</template>
<template v-slot:right-icon>
<view v-if="method.status" class="">
{{t('withdraw.binded')}}
</view>
<navigator class="text-primary" v-if="!method.status&&method.type==1" hover-class="none"
:url="'/packages/pages/withdraw/account_usdt?id='+method.id+'&name='+method.name+'&is_qrcode='+method.is_qrcode">
{{t('withdraw.toBind')}}
</navigator>
<navigator class="text-primary" v-if="!method.status&&method.type==2" hover-class="none"
:url="'/packages/pages/withdraw/account_qrcode?id='+method.id+'&name='+method.name">
{{t('withdraw.toBind')}}
</navigator>
<navigator class="text-primary" v-if="!method.status&&method.type==3" hover-class="none"
:url="'/packages/pages/withdraw/account_bank?id='+method.id+'&name='+method.name">
{{t('withdraw.toBind')}}
</navigator>
</template>
</u-cell-item>
</u-cell-group>
</view>
<view class="mt-[50rpx] mx-[26rpx] ">
<view class="font-bold flex justify-start items-center mb-[20rpx]">
<u-icon size="32" name="info-circle-fill"></u-icon>
<view class="ml-[10rpx]">
{{t('withdraw.withdrawWarm')}}
</view>
</view>
<view class="p-[20rpx] bg-[#F8F9FB] leading-loose rounded-lg" v-html="data.content"></view>
</view>
</view>
</template>
<script lang="ts" setup>
import { onShow } from '@dcloudio/uni-app'
import { reactive } from 'vue'
import { getWithdrawMethod, getWithdrawWallet } from '@/api/finance'
import { getHintDetail } from '@/api/news'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('withdraw.withdrawAccountManage') })
const data = reactive({
methods: [],
wallets: [],
content:''
})
const getData = async () => {
data.wallets = await getWithdrawWallet()
data.methods = await getWithdrawMethod()
data.methods.forEach((method : any) => {
data.wallets.forEach((wallet : any) => {
if (method['id'] - wallet['method_id'] == 0) {
method['status'] = true;
method['account'] = wallet['account'];
}
})
})
const res = await getHintDetail({id:12})
data.content = res.content;
}
onShow(() => {
getData()
})
</script>
<style lang="scss">
page{
background-color: #FFFFFF;
}
</style>

View File

@@ -0,0 +1,129 @@
<template>
<view>
<h-dark :title="title"></h-dark>
<view class="mt-[40rpx]">
<view class=" px-[40rpx] mb-[15rpx]">
* {{t('withdraw.bankname')}}
</view>
<view class="account px-[40rpx] py-[30rpx]">
<view class="bg-[#f6f7fb] rounded-lg px-[20rpx] py-[10rpx] ">
<view class="py-[16rpx]">
<u-picker class="max-w-[750px]" mode="selector" v-model="picker_show"
:default-selector="[banks.default_select]" :range="banks.list" range-key="name"
@confirm="handlePicker" :confirm-text="t('common.confirm')"
:cancel-text="t('common.cancel')">
</u-picker>
<view class="mr-[20rpx]" @click="picker_show=true">
<text class="mr-[10rpx]">{{formData.bank_name}}
</text>
<u-icon name="arrow-down" size="20"></u-icon>
</view>
</view>
</view>
</view>
<view class=" px-[40rpx] mb-[15rpx] mt-[30rpx]">
* {{t('withdraw.bankUsername')}}
</view>
<view class="account px-[40rpx] py-[15rpx]">
<view class="bg-[#f6f7fb] rounded-lg px-[20rpx] py-[10rpx] ">
<u-input class="flex-1" v-model="formData.name" :border="false"
:placeholder="t('withdraw.bankUsernamePlaceholder')" />
</view>
</view>
<view class=" px-[40rpx] mb-[15rpx] mt-[30rpx]">
* {{t('withdraw.bankAccount')}}
</view>
<view class="account px-[40rpx] py-[15rpx]">
<view class="bg-[#f6f7fb] rounded-lg px-[20rpx] py-[10rpx] ">
<u-input class="flex-1" v-model="formData.account" :border="false"
:placeholder="t('withdraw.bankAccountPlaceholder')" />
</view>
</view>
</view>
<view class="px-[40rpx] mt-[50rpx] leading-loose font-bold">
<view class="text-error ">
{{t('withdraw.tips1')}}
</view>
<view class="text-error">
{{t('withdraw.tips2')}}
</view>
</view>
<view class=" px-[40rpx] my-[80rpx] pb-[50rpx]">
<u-button type="primary" @click="handleConfirm"> {{t('withdraw.bindBtn')}} </u-button>
</view>
</view>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { t } from '@/utils/util'
import { withdrawWalletAdd, getWithdrawBanks } from '@/api/finance'
uni.setNavigationBarTitle({ title: t('withdraw.withdrawAccountManage') })
const title = ref('')
const picker_show = ref(false)
const banks = reactive<{
list : any[]
default_select : number
}>({
list: [],
default_select: 0
})
const formData = reactive({
bank_id: '',
name: '',
account: '',
type: 3,//类型1USDT2扫码3银行卡
method_id: '',
bank_name: ''
})
const getData = async () => {
let data = await getWithdrawBanks()
banks.list = data;
formData.bank_id = data[0]['id']
formData.bank_name = data[0]['name']
}
const handlePicker = (index : number) => {
banks.default_select = index;
formData.bank_id = banks.list[index]['id'];
formData.bank_name = banks.list[index]['name'];
}
const handleConfirm = async () => {
if (formData.bank_id == "") return uni.$u.toast(t('withdraw.banknameEmpty'))
if (!formData.name) return uni.$u.toast(t('withdraw.bankUsernameEmpty')
)
if (formData.name.length < 2 || formData.name.length > 30) return uni.$u.toast(t('withdraw.banknameError'))
if (!formData.account) return uni.$u.toast(t('withdraw.bankAccountEmpty'))
if (formData.account.length < 6 || formData.account.length > 30) return uni.$u.toast(t('withdraw.bankAccountError'))
await withdrawWalletAdd(formData)
uni.redirectTo({
url: '/pages/common/successful?type=3'
})
}
onLoad((options : any) => {
formData.method_id = options.id
title.value = options.name;
getData()
})
</script>
<style lang="scss">
page {
background-color: #FFFFFF;
}
.shadow-inner {
border-radius: 10px;
padding: 5px 10px;
box-shadow: inset 0px 1px 4px 0px rgb(0 0 0 / 0.3);
}
</style>

View File

@@ -0,0 +1,91 @@
<template>
<view>
<h-dark :title="title"></h-dark>
<view class="mt-[40rpx]">
<view class=" px-[40rpx] mb-[15rpx]">
* {{t('withdraw.qrcodeAccount')}}
</view>
<view class="account px-[40rpx] py-[15rpx]">
<view class="bg-[#f6f7fb] rounded-lg px-[20rpx] py-[10rpx]">
<u-input class="flex-1" v-model="formData.account" :border="false" :placeholder="t('withdraw.qrcodeAccountPlaceholder')" />
</view>
</view>
<view class=" px-[40rpx] mb-[15rpx] mt-[30rpx]">
* {{t('withdraw.qrcode')}}
</view>
<view class="px-[40rpx] py-[15rpx]">
<view class="bg-[#f6f7fb] rounded-lg w-full flex justify-center">
<u-upload class="" ref="uploader" :custom-btn="true" :deletable="false" :auto-upload="false"
max-count="1" :max-size="20*1024*1024" v-model="formData.img">
<template v-slot:addBtn>
<view class="flex items-center justify-center slot-btn w-[180rpx] h-[180rpx] "
hover-class="slot-btn__hover" hover-stay-time="150">
<u-icon name="photo" size="80" class="text-muted"></u-icon>
</view>
</template>
</u-upload>
</view>
</view>
</view>
<view class="px-[40rpx] mt-[50rpx] leading-loose font-bold">
<view class="text-error ">
{{t('withdraw.tips1')}}
</view>
<view class="text-error">
{{t('withdraw.tips2')}}
</view>
</view>
<view class=" px-[40rpx] my-[80rpx] pb-[50rpx]">
<u-button type="primary" @click="handleConfirm"> {{t('withdraw.bindBtn')}} </u-button>
</view>
</view>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { t } from '@/utils/util'
import { uploadImage } from '@/api/app'
import { useUserStore } from '@/stores/user'
import { withdrawWalletAdd } from '@/api/finance'
uni.setNavigationBarTitle({ title: t('withdraw.withdrawAccountManage') })
const title = ref('')
const formData = reactive({
account: '',
img: '',
type: 2,//类型1USDT2扫码3银行卡
method_id: ''
})
const userStore = useUserStore()
const uploader = ref();
const handleConfirm = async () => {
if (!formData.account) return uni.$u.toast(t('withdraw.qrcodeAccountEmpty'))
if (formData.account.length < 6 || formData.account.length > 30) return uni.$u.toast(t('withdraw.qrcodeAccountError'))
if (uploader.value.lists.length <= 0) return uni.$u.toast(t('withdraw.qrcodeEmpty'))
const res : any = await uploadImage(uploader.value.lists[0].url, userStore.temToken!)
formData.img = res.url;
await withdrawWalletAdd(formData)
uni.redirectTo({
url: '/pages/common/successful?type=3'
})
}
onLoad((options : any) => {
formData.method_id = options.id
title.value = options.name;
})
</script>
<style lang="scss">
page{
background-color: #FFFFFF;
}
.shadow-inner {
border-radius: 10px;
padding: 5px 10px;
box-shadow: inset 0px 1px 4px 0px rgb(0 0 0 / 0.3);
}
</style>

View File

@@ -0,0 +1,98 @@
<template>
<view>
<h-dark :title="title"></h-dark>
<view class="mt-[40rpx]">
<view class=" px-[40rpx] mb-[15rpx]">
* {{t('withdraw.address')}}
</view>
<view class="account px-[40rpx] py-[15rpx] ">
<view class="bg-[#f6f7fb] rounded-lg px-[20rpx] py-[10rpx]">
<u-input class="flex-1" v-model="formData.account" :border="false"
:placeholder="t('withdraw.addressPlaceholder')" />
</view>
</view>
<view class=" px-[40rpx] mb-[15rpx] mt-[30rpx]" v-if="formData.is_qrcode==1">
* {{t('withdraw.qrcode')}}
</view>
<view class="px-[40rpx] py-[15rpx]" v-if="formData.is_qrcode==1">
<view class="bg-[#f6f7fb] rounded-lg w-full flex justify-center ">
<u-upload class="" ref="uploader" :custom-btn="true" :deletable="false" :auto-upload="false"
max-count="1" :max-size="20*1024*1024" v-model="formData.img">
<template v-slot:addBtn>
<view class="flex items-center justify-center slot-btn w-[180rpx] h-[180rpx] "
hover-class="slot-btn__hover" hover-stay-time="150">
<u-icon name="photo" size="80" class="text-muted"></u-icon>
</view>
</template>
</u-upload>
</view>
</view>
</view>
<view class="px-[40rpx] mt-[50rpx] leading-loose font-bold">
<view class="text-error ">
{{t('withdraw.tips1')}}
</view>
<view class="text-error">
{{t('withdraw.tips2')}}
</view>
</view>
<view class=" px-[40rpx] my-[80rpx] pb-[50rpx]">
<u-button type="primary" @click="handleConfirm"> {{t('withdraw.bindBtn')}} </u-button>
</view>
</view>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { uploadImage } from '@/api/app'
import { useUserStore } from '@/stores/user'
import { withdrawWalletAdd } from '@/api/finance'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('withdraw.withdrawAccountManage') })
const title = ref('')
const formData = reactive({
account: '',
img: '',
type: 1,//类型1USDT2扫码3银行卡
method_id: '',
is_qrcode: 0
})
const userStore = useUserStore()
const uploader = ref();
const handleConfirm = async () => {
if (!formData.account) return uni.$u.toast(t('withdraw.addressEmpty'))
if (formData.account.length < 8 || formData.account.length > 128) return uni.$u.toast(t('withdraw.addressError'))
if(formData.is_qrcode==1){
if (uploader.value.lists.length <= 0) return uni.$u.toast(t('withdraw.qrcodeEmpty'))
const res : any = await uploadImage(uploader.value.lists[0].url, userStore.temToken!)
formData.img = res.url;
}
await withdrawWalletAdd(formData)
uni.redirectTo({
url: '/pages/common/successful?type=3'
})
}
onLoad((options : any) => {
formData.method_id = options.id
title.value = options.name;
formData.is_qrcode = options.is_qrcode;
})
</script>
<style lang="scss">
page {
background-color: #FFFFFF;
}
.shadow-inner {
border-radius: 10px;
padding: 5px 10px;
box-shadow: inset 0px 1px 4px 0px rgb(0 0 0 / 0.3);
}
</style>

View File

@@ -0,0 +1,226 @@
<template>
<view>
<u-navbar :back-text="t('withdraw.title')" :back-text-style="{color: '#FFFFFF'}"
:background="{backgroundColor: '#4E55AF'}" back-icon-color="#FFFFFF">
<view class="flex justify-end w-full pr-[20rpx] relative">
<navigator hover-class="none" url="/packages/pages/withdraw/record">
<u-icon size="40" name="/static/images/common/record.png"></u-icon>
</navigator>
</view>
</u-navbar>
<view class="mx-[30rpx] pt-[20rpx]">
<u-form :border-bottom="false" :label-width="150" label-position="top">
<u-form-item :itemStyle="{'background':'#3d4277'}" :border-bottom="false" :label="t('withdraw.mainNet')">
<u-input v-model="formData.method_name" type="select" :placeholder="t('withdraw.mainNetPlaceholder')" @click="showAtionSheet" />
</u-form-item>
<u-form-item :itemStyle="{'background':'#3d4277'}" :border-bottom="false" :label="t('withdraw.waddress')">
<u-input v-model="formData.address" :border="false" :placeholder="t('withdraw.waddressPlaceholder')" />
</u-form-item>
<u-form-item :itemStyle="{'background':'#3d4277'}" :border-bottom="false"
:label="t('withdraw.withdrawMoney')">
<u-input type="number" v-model="formData.money" @input="checkMoney" :border="false"
:placeholder="t('withdraw.withdrawMoneyPlaceholder')" />
</u-form-item>
<view class="py-[25rpx]">
<view class="text-[#00D2E0] mb-[10rpx]">
{{t('withdraw.userMoney')}} {{getCurrency()}}{{formatMoney(data.config.balance)}} {{getCurrency2()}}
</view>
<view class="text-[#00D2E0]" v-if="formData.money >0">
<view class=" mb-[10rpx]" v-if="data.method_rate !=1">
{{t('withdraw.rate')}} {{data.method_rate}}
</view>
<view class=" mb-[10rpx]" v-if="data.charge > 0.01">
{{t('withdraw.charge')}}
{{formatMoney(data.charge,data.method_symbol,data.method_rate,data.method_precision)}}
</view>
<view class="">
{{t('withdraw.actMoney')}}
{{formatMoney(data.act_money,data.method_symbol,data.method_rate,data.method_precision)}}
</view>
</view>
</view>
</u-form>
</view>
<view class="mt-[30rpx] mx-[30rpx] max-w-[750px]">
<u-button type="primary" @click="submitHandle" :disabled="formData.method_id=='' || formData.address=='' || formData.money==''"> {{t('common.submit')}} </u-button>
</view>
<view class="mt-[50rpx] mx-[30rpx] pb-[30rpx]">
<view class="font-bold flex justify-start items-center mb-[20rpx]">
<u-icon size="32" name="info-circle-fill"></u-icon>
<view class="ml-[10rpx]">
{{t('withdraw.withdrawWarm')}}
</view>
</view>
<view class="p-[20rpx] bg-[#3d4277] leading-loose rounded-lg" v-html="data.content"></view>
</view>
<u-keyboard class="max-w-[750px]" ref="uKeyboard1" mode="number" :dot-enabled="false" :tips="t('pwd.payPwd')"
confirm-text="" cancel-text="" v-model="show_keyboard" @change="keyboardChange" @backspace="backspace"
:show-input-val="true" :input-value="formData.pay_pwd"></u-keyboard>
<u-action-sheet class="max-w-[750px]" :list="data.methods" v-model="show" @click="handleClick"
:safe-area-inset-bottom="true" :cancel-text="t('common.cancel')"></u-action-sheet>
</view>
</template>
<script lang="ts" setup>
import { onShow } from '@dcloudio/uni-app'
import { reactive, ref, nextTick } from 'vue'
import { withdraw, withdrawConfig, getWithdrawMethod } from '@/api/finance'
import { getHintDetail } from '@/api/news'
import { formatDate, getCurrency,getCurrency2, formatMoney } from '@/utils/util'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('withdraw.title') })
const formData = reactive({
address: '',
money: '',
method_id: 0,
method_name: '',
pay_pwd: '',
date: formatDate(Math.round(Date.now() / 1000))
})
const data = reactive<{
config : any,
methods : any,
charge : number,
act_money : number,
method_charge : number,
method_symbol : string,
method_rate : number,
method_precision : number,
content : string
}>({
config: {
balance: 0,
noused_money: 0,
min: 0,
max: 0,
num: 0,
is_withdraw: false,
need_realname: 0
},
methods: [],
charge: 0,
act_money: 0,
method_charge: 0,
method_symbol: '',
method_rate: 0,
method_precision: 2,
content: ''
})
const show_keyboard = ref(false)
const show = ref(false)
const getData = async () => {
data.config = await withdrawConfig({ date: formatDate(Math.round(Date.now() / 1000)) })
if (data.config.need_realname == 1) {
uni.redirectTo({
url: '/pages/auth/realname'
})
return
}
if (data.config.need_set_pwd == 1) {
uni.$u.toast(t('pwd.pwdSetFirstTips'))
uni.redirectTo({
url: '/pages/change_password/set_password_pay'
})
return
}
data.methods = await getWithdrawMethod()
if (data.methods.length > 0) {
let index = 0
//预输入
if (data.config.ahead.method_id != 0) {
index = data.methods.findIndex(item => item.id === data.config.ahead.method_id)
formData.address = data.config.ahead.address
console.log(index)
}
formData.method_id = data.methods[index]['id']
formData.method_name = data.methods[index]['text']
data.method_charge = data.methods[index]['charge']
data.method_symbol = data.methods[index]['symbol']
data.method_rate = parseFloat(data.methods[index]['rate'])
data.method_precision = data.methods[index]['precision']
}
const res = await getHintDetail({ id: 12 })
data.content = res.content;
}
const keyboardChange = (val : string) => {
formData.pay_pwd += val;
if (formData.pay_pwd.length == 6) {
show_keyboard.value = false;
submit();
}
}
const backspace = () => {
// 删除value的最后一个字符
if (formData.pay_pwd.length) formData.pay_pwd = (formData.pay_pwd).substr(0, (formData.pay_pwd.length) - 1);
}
const showAtionSheet = () => {
show.value = true;
}
const handleClick = (index : number) => {
formData.method_id = data.methods[index]['id'];
formData.method_name = data.methods[index]['text'];
data.method_charge = data.methods[index]['charge'];
data.method_symbol = data.methods[index]['symbol'];
data.method_rate = parseFloat(data.methods[index]['rate']);
data.method_precision = data.methods[index]['precision'];
}
const checkMoney = (money : string) => {
var precision = uni.getStorageSync('precision')
// 使用正则表达式匹配小数
var decimalMatch = money.match(/\.(\d+)/);
if (decimalMatch) {
if (decimalMatch[1].length >= precision) {
nextTick(() => {
formData.money = parseFloat(money).toFixed(precision);
})
}
}
money = parseFloat(money).toFixed(precision);
data.charge = parseFloat(money) * data.method_charge / 100;
data.charge = parseFloat((data.charge).toFixed(precision));
data.act_money = parseFloat((parseFloat(money) - data.charge).toFixed(precision));
}
const submitHandle = async () => {
//提现金额判断
if (parseFloat(formData.money) - data.config.balance > 0) return uni.$u.toast(t('common.InsufficientBalance'));
if (parseFloat(formData.money) < data.config.min) return uni.$u.toast(t('withdraw.moneyMin') + getCurrency() + data.config.min + ' ' + getCurrency2())
if (parseFloat(formData.money) > data.config.max) return uni.$u.toast(t('withdraw.moneyMax') + getCurrency() + data.config.max + ' ' + getCurrency2())
//判断提现次数
if (!data.config.is_withdraw) return uni.$u.toast(t('withdraw.withdrawNum') + data.config.num)
formData.pay_pwd = '';
show_keyboard.value = true;
}
const submit = async () => {
await withdraw(formData)
uni.redirectTo({
url: '/pages/common/successful?type=1'
})
}
getData()
</script>
<style lang="scss">
.u-tooltip-tips {
flex: none !important;
}
</style>

View File

@@ -0,0 +1,120 @@
<template>
<view>
<h-dark title=""></h-dark>
<view class="bg-[#4E55AF] px-[30rpx] pt-[30rpx] pb-[300rpx]">
<view class=" flex flex-col items-center">
<u-image width="300" height="180" mode="widthFix"
src="/static/images/withdraw/withdraw_bg.png"></u-image>
</view>
</view>
<view class="bg-[#2C326B] rounded-lg p-[50rpx] mx-[30rpx] mt-[-260rpx]">
<view class="flex justify-between mb-[40rpx]">
<view class="w-1/4">
{{t('withdraw.withdrawMethod')}}
</view>
<view class="w-3/4 text-right ">
{{orderData.account}}
</view>
</view>
<view class="flex justify-between mb-[40rpx]">
<view class="w-1/4">
{{t('orderDetail.status')}}
</view>
<view v-if="orderData.status==1" class="w-3/4 text-right text-success ">
{{t('tabs.finish')}}
</view>
<view v-if="orderData.status==0" class="w-3/4 text-right text-primary ">
{{t('tabs.pending')}}
</view>
<view v-if="orderData.status==2" class="w-3/4 text-right text-error ">
{{t('tabs.fail')}}
</view>
</view>
<view class="flex justify-between mb-[40rpx]">
<view class="w-1/4">
{{t('orderDetail.orderSn')}}
</view>
<view class="w-3/4 text-right ">
{{orderData.sn}}
</view>
</view>
<view class="flex justify-between mb-[40rpx]">
<view class="w-1/4">
{{t('orderDetail.createTime')}}
</view>
<view class="w-3/4 text-right ">
{{orderData.create_time}}
</view>
</view>
<view class="flex justify-between mb-[40rpx]" v-if="orderData.status==2">
<view class="w-1/4">
{{t('orderDetail.remark')}}
</view>
<view class="w-3/4 text-right text-error break-words">
<text v-if="orderData.reamrk==''">{{t('withdraw.withdrawFailTips')}}</text>
<text v-if="orderData.reamrk!=''">{{orderData.remark}}</text>
</view>
</view>
<u-line class="text-info" margin="20rpx 0 40rpx 0" border-style="dashed" />
<view class="flex justify-between">
<view class="w-1/4">
{{t('orderDetail.money')}}
</view>
<view class="w-3/4 text-right">
<view v-if="orderData.status==1" class="text-lg font-bold text-success">
{{getCurrency()}}{{formatMoney(orderData.amount)}} {{getCurrency2()}}
</view>
<view v-if="orderData.status==0" class="text-lg font-bold text-primary">
{{getCurrency()}}{{formatMoney(orderData.amount)}} {{getCurrency2()}}
</view>
<view v-if="orderData.status==2" class="text-lg font-bold text-error">
{{getCurrency()}}{{formatMoney(orderData.amount)}} {{getCurrency2()}}
</view>
<view class="mt-[10rpx]" v-if="orderData.rate && orderData.rate != 1">
{{orderData.amount_act}} {{orderData.symbol}}
</view>
</view>
</view>
<view class="flex justify-between mt-[40rpx]" v-if="orderData.charge>0">
<view class="w-1/4">
{{t('orderDetail.charge')}}
</view>
<view class="w-3/4 text-right text-muted">
{{getCurrency()}}{{formatMoney(orderData.charge)}} {{getCurrency2()}}
</view>
</view>
</view>
<view class="max-w-[750px] fixed bottom-[50rpx] w-4/5 ml-[10%]">
<u-button type="primary" @click="handleBack"> {{t('common.back')}} </u-button>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { withdrawRecordDetail } from '@/api/finance'
import { getCurrency,getCurrency2, formatDate, formatMoney, t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('withdraw.title') })
const orderData = ref<any>({})
let orderId = ''
const getData = async (id) => {
orderData.value = await withdrawRecordDetail({ id, date: formatDate(Math.round(Date.now() / 1000)) })
}
const handleBack = async () => {
let canNavBack = getCurrentPages()
if (canNavBack && canNavBack.length > 1) {
uni.navigateBack()
} else {
history.back();
}
}
onLoad((options : any) => {
orderId = options.id
getData(orderId)
})
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,86 @@
<template>
<z-paging :refresher-enabled="false" :show-loading-more-when-reload="true" loading-more-default-text=""
loading-more-loading-text="" loading-more-fail-text="" :empty-view-text="t('common.noData')"
:show-loading-more-no-more-view="false" ref="paging" v-model="dataList" @query="queryList"
class="max-w-[750px]">
<h-dark :title="t('withdraw.titleRecord')"></h-dark>
<view class="mx-[30rpx] mt-[20rpx]">
<navigator v-for="item in dataList" :key="item.id" hover-class="none"
:url="'/packages/pages/withdraw/order_detail?id='+item.id">
<view class="bg-[#2C326B] mb-[20rpx] rounded-lg px-[40rpx] py-[35rpx]">
<view class="flex justify-between mb-[20rpx]">
<view class="w-3/5">
<view class="">{{item.create_time}}</view>
</view>
<view class="text-right w-2/5">
<view class=" font-bold ">
{{item.amount}}
</view>
<view class="mt-[10rpx] text-xs" v-if="item.rate != 1">
{{item.amount_act}} {{item.symbol}}
</view>
</view>
</view>
<view class="flex justify-between">
<view class="text-[#00D2E0]">
<view class="">
{{item.account}}
</view>
<view v-if="item.status==2" class="text-xs text-error mt-[12rpx] break-words">
<text v-if="item.reamrk==''">{{tips.withdrawFailTips}}</text>
<text v-if="item.reamrk!=''">{{item.remark}}</text>
</view>
</view>
<view v-if="item.status==1" class="text-success">
{{tips.finish}}
</view>
<view v-if="item.status==0" class="text-primary">
{{tips.pending}}
</view>
<view v-if="item.status==2" class="text-error">
{{tips.fail}}
</view>
</view>
</view>
</navigator>
</view>
</z-paging>
</template>
<script lang="ts" setup>
import { ref, reactive, shallowRef } from 'vue'
import { withdrawRecordLists } from '@/api/finance'
import { getCurrency,getCurrency2, formatDate, formatMoney } from '@/utils/util'
import { t } from '@/utils/util'
//为了提高渲染速度
const tips = reactive({
charge: t('withdraw.charge'),
withdrawFailTips: t('withdraw.withdrawFailTips'),
fail: t('tabs.fail'),
pending: t('tabs.pending'),
finish: t('tabs.finish'),
})
uni.setNavigationBarTitle({ title: t('withdraw.titleRecord') })
const paging = shallowRef()
const dataList = ref<any[]>([])
const queryList = async (pageNo : number, pageSize : number) => {
try {
const data = await withdrawRecordLists({
page_no: pageNo,
page_size: pageSize,
date: formatDate(Math.round(Date.now() / 1000))
})
Object.keys(data.lists).forEach(key => {
data.lists[key]['amount'] = getCurrency() + formatMoney(data.lists[key]['amount']) + ' ' + getCurrency2()
})
paging.value.complete(data.lists)
} catch (error) {
paging.value.complete(false)
}
}
</script>
<style lang="scss">
</style>

329
src/pages.json Normal file
View File

@@ -0,0 +1,329 @@
{
"pages": [{
"path": "pages/index/index"
}, {
"path": "pages/login/login"
},
{
"path": "pages/team/index",
"auth": true
},
{
"path": "pages/robot/index",
"auth": true
},
{
"path": "pages/fund/index",
"auth": true
},
{
"path": "pages/user/user",
"auth": true
},
{
"path": "pages/register/register"
},
{
"path": "pages/bind_mobile/bind_mobile"
},
{
"path": "pages/forget_pwd/forget_pwd"
},
{
"path": "pages/change_password/change_password",
"auth": true
},
{
"path": "pages/change_password/change_password_pay",
"auth": true
},
{
"path": "pages/change_password/set_password_pay",
"auth": true
},
{
"path": "pages/user_set/user_set",
"auth": true
},
{
"path": "pages/user_data/user_data",
"auth": true
},
{
"path": "pages/auth/google",
"auth": true
},
{
"path": "pages/auth/email",
"auth": true
},
{
"path": "pages/auth/realname",
"auth": true
},
{
"path": "pages/news/news",
"style": {
"disableScroll": true
}
},
{
"path": "pages/kefu/index"
},
{
"path": "pages/news_detail/news_detail"
},
{
"path": "pages/news_detail/detail"
},
{
"path": "pages/notice/index",
"auth": true
},
{
"path": "pages/notice/record",
"auth": true
},
{
"path": "pages/notice/detail",
"auth": true
},
{
"path": "pages/share/index",
"auth": true
},
{
"path": "pages/mission/index",
"auth": true
},
{
"path": "pages/activity/index",
"auth": true
},
{
"path": "pages/feedback/index",
"auth": true
},
{
"path": "pages/collection/collection",
"auth": true
},
{
"path": "pages/as_us/as_us"
},
{
"path": "pages/agreement/agreement"
},
{
"path": "pages/webview/webview"
},
{
"path": "pages/empty/empty",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "uni_modules/vk-uview-ui/components/u-avatar-cropper/u-avatar-cropper",
"style": {
"navigationBarBackgroundColor": "#000000"
},
"auth": true
},
{
"path": "pages/robot/record",
"auth": true
},
{
"path": "pages/team/record",
"auth": true
},
{
"path": "pages/faq/index"
},
{
"path": "pages/language/index"
},
{
"path": "pages/signin/index"
},
{
"path": "pages/user_vip/index",
"auth": true
},
{
"path": "pages/common/successful"
},
{
"path": "pages/item/index",
"auth": true
},
{
"path": "pages/item/detail",
"auth": true
},
{
"path": "pages/item/record",
"auth": true
},
{
"path": "pages/item/contract",
"auth": true
},
{
"path": "pages/transfer/index",
"auth": true
},
{
"path": "pages/transfer/record",
"auth": true
},
{
"path": "pages/mall/index",
"auth": true
},
{
"path": "pages/mall/record",
"auth": true
},
{
"path": "pages/mall/draw",
"auth": true
},
{
"path": "pages/mall/draw_record",
"auth": true
},
{
"path": "pages/mine/index",
"auth": true
},
{
"path": "pages/mine/record",
"auth": true
}
],
"subPackages": [{
"root": "packages",
"pages": [{
"path": "pages/recharge/index",
"auth": true
},
{
"path": "pages/recharge/recharge_bank",
"auth": true
},
{
"path": "pages/recharge/recharge_qrcode",
"auth": true
},
{
"path": "pages/recharge/recharge_udun",
"auth": true
},
{
"path": "pages/recharge/recharge_tron",
"auth": true
},
{
"path": "pages/recharge/recharge_address",
"auth": true
},
{
"path": "pages/recharge/recharge_online",
"auth": true
},
{
"path": "pages/recharge/record",
"auth": true
},
{
"path": "pages/user_wallet/record",
"auth": true
},
{
"path": "pages/user_wallet/record_freeze",
"auth": true
},
{
"path": "pages/user_wallet/order_detail",
"auth": true
},
{
"path": "pages/withdraw/account",
"auth": true
},
{
"path": "pages/withdraw/account_usdt",
"auth": true
},
{
"path": "pages/withdraw/account_qrcode",
"auth": true
},
{
"path": "pages/withdraw/account_bank",
"auth": true
},
{
"path": "pages/recharge/order_detail",
"auth": true
},
{
"path": "pages/withdraw/index",
"auth": true
},
{
"path": "pages/withdraw/record",
"auth": true
},
{
"path": "pages/withdraw/order_detail",
"auth": true
}
]
}],
"globalStyle": {
// "navigationBarTextStyle": "black",
// "navigationBarBackgroundColor": "#FFFFFF",
// "backgroundColor": "#F8F8F8",
// "h5": {
// "navigationStyle": "custom"
// },
"rpxCalcMaxDeviceWidth": 550,
"navigationStyle": "custom"
},
"easycom": {
"custom": {
"^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue",
"^w-(.*)": "@/components/widgets/$1/$1.vue",
"^h-(.*)": "@/components/header/$1.vue"
}
},
"tabBar": {
"list": [{
"pagePath": "pages/index/index"
}, {
"pagePath": "pages/team/index"
},
{
"pagePath": "pages/mine/index"
},
{
"pagePath": "pages/fund/index"
}, {
"pagePath": "pages/user/user"
}, {
"pagePath": "pages/item/index"
}],
"height": "0px"
}
}

View File

@@ -0,0 +1,77 @@
<template>
<h-dark :title="t('activity.title')"></h-dark>
<view class="mx-[30rpx]">
<view class="ad flex items-center">
<view class="flex justify-around items-center w-full text-white">
<view class="text-center">
<view class=" font-bold text-3xl mb-[10rpx]">
{{data.report.today_income}}
</view>
<view class=" ">
{{t('activity.todayIncome')}}
</view>
</view>
<view class="text-center">
<view class=" font-bold text-3xl mb-[10rpx]">
{{data.report.total_income}}
</view>
<view class=" ">
{{t('activity.totalIncome')}}
</view>
</view>
</view>
</view>
<view class="mt-[20rpx] pb-[30rpx]">
<view class="bg-[#2C326B] rounded-lg px-[24rpx] py-[40rpx] mb-[20rpx]"
v-for="(item, index) in data.activities" :key="index">
<view class="flex justify-between items-center">
<view class="">
<view class="font-bold">
{{replaceStr(replaceStr(t('activity.itemTitle1'),'{money}',item.recharge_money),'{reward}',item.money)}}
</view>
<view class="text-[#00D2E0] mt-[10rpx]" v-if="item.count>0">
<text class="mr-[10rpx]">{{t('activity.itemTips1')}}:</text>{{item.count}}
</view>
</view>
<view class="ml-[30rpx]">
<u-button type="primary" size="mini" shape="circle">{{t('tabs.pending')}}</u-button>
</view>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { getActivityIndex } from '@/api/user'
import { t,replaceStr } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('activity.title') })
const data = reactive<{
activities : any[]
report : any
}>({
activities: [],
report : []
})
const getData = async () => {
const res = await getActivityIndex()
data.activities = res.activities
data.report = res.report
}
getData()
</script>
<style lang="scss">
.ad {
background-image: url('/static/images/activity/activitybg.png');
background-size: 100% auto;
background-repeat: no-repeat;
background-position: center;
width: 100%;
height: 112px;
border-radius: 30rpx;
}
</style>

View File

@@ -0,0 +1,31 @@
<!-- <template>
<view class="">
<u-parse :html="agreementContent"></u-parse>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getPolicy } from '@/api/app'
let agreementType = ref('') // 协议类型
const agreementContent = ref('') // 协议内容
const getData = async (type) => {
const res = await getPolicy({ type })
agreementContent.value = res.content
uni.setNavigationBarTitle({
title: String(res.title)
})
}
onLoad((options: any) => {
if (options.type) {
agreementType = options.type
getData(agreementType)
}
})
</script>
<style lang="scss" scoped></style -->>

23
src/pages/as_us/as_us.vue Normal file
View File

@@ -0,0 +1,23 @@
<template>
<h-dark title="关于我们"></h-dark>
<view class="as-us flex flex-1 flex-col items-center">
<image :src="appStore.getWebsiteConfig.shop_logo" mode="" class="img"></image>
<view class="text-content mt-[20rpx]">当前版本{{ appStore.config.version }}</view>
</view>
</template>
<script setup lang="ts">
import { useAppStore } from '@/stores/app'
const appStore = useAppStore()
</script>
<style lang="scss" scoped>
.as-us {
.img {
width: 160rpx;
height: 160rpx;
border-radius: 20rpx;
margin-top: 96rpx;
}
}
</style>

114
src/pages/auth/email.vue Normal file
View File

@@ -0,0 +1,114 @@
<template>
<h-dark :title="t('auth.emailTitle')"></h-dark>
<view class="mx-[30rpx]" v-if="data.info.auth_email==1">
<view class="flex justify-center mt-[60rpx]">
<u-image width="180" height="180" mode="widthFix" src="/static/images/user/authemail.png"></u-image>
</view>
<view class="my-[30rpx] leading-loose">
{{t('auth.emailTips')}}
</view>
<view class="flex justify-between mt-[100rpx]">
<view class="">
<view class="text-[#00D2E0] mt-[10rpx]">
{{data.info.email}}
</view>
</view>
<view class="">
<text class="">{{t('tabs.Verified')}}</text>
</view>
</view>
</view>
<view class="mx-[30rpx]" v-if="data.info.auth_email==0">
<u-form :border-bottom="false" :label-width="150" label-position="top">
<u-form-item :itemStyle="{'background':'#3d4277'}" :label="t('auth.email')" :border-bottom="false">
<u-input class="flex-1" type="email" v-model="formData.email"
:placeholder="t('auth.emailPlaceholder')" :border="false" />
</u-form-item>
<u-form-item :itemStyle="{'background':'#3d4277'}" :label="t('pwd.captcha')" :border-bottom="false">
<u-input class="flex-1" v-model="formData.code" :placeholder="t('captcha.captchaPlaceholder')"
:border="false" />
<view class="border-l border-solid border-0 border-light pl-3 text-[#00D2E0] leading-4 ml-3 w-[180rpx]"
@click="sendSms">
<u-verification-code ref="uCodeRef" :seconds="60" @change="codeChange"
:change-text="t('captcha.changeText')" :startText="t('captcha.startText')"
:endText="t('captcha.endText')" />
<text :class="formData.email ? 'text-primary' : 'text-[#00D2E0]'">
{{ codeTips }}
</text>
</view>
</u-form-item>
</u-form>
<view class="font-bold flex justify-start items-center mb-[20rpx] mt-[30rpx]">
<u-icon size="32" name="info-circle-fill"></u-icon>
<view class="ml-[10rpx]">
{{t('auth.warm')}}
</view>
</view>
<view class="p-[20rpx] bg-[#3d4277] text-[#00D2E0] leading-loose rounded-lg">
{{t('auth.warmTips')}}
</view>
</view>
<view v-if="data.info.auth_email==0" class="max-w-[750px] fixed bottom-[50rpx] w-4/5 ml-[10%]">
<u-button type="primary" :disabled="formData.code == ''||formData.code == ''" @click="submitHandle"> {{t('auth.bindBtn')}}
</u-button>
</view>
</template>
<script lang="ts" setup>
import { reactive, ref,shallowRef } from 'vue'
import { emailSend } from '@/api/app'
import { getUserInfo,verifyEmail } from '@/api/user'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('auth.emailTitle') })
const uCodeRef = shallowRef()
const codeTips = ref('')
const data = ref<any>({
info:{
auth_email:2
}
})
const formData = reactive<any>({
email:'',
code: ''
})
const getData = async () => {
const res = await getUserInfo()
data.value = res;
}
const codeChange = (text : string) => {
codeTips.value = text
}
const sendSms = async () => {
if (!formData.email) return
var emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
if(!emailRegex.test(formData.email)){
return uni.$u.toast(t('auth.emailEmpty'))
}
if (uCodeRef.value?.canGetCode) {
await emailSend({
email: formData.email,
})
uni.$u.toast(t('captcha.sendSuccessfully'))
uCodeRef.value?.start()
}
}
const submitHandle = async () => {
await verifyEmail(formData)
uni.$u.toast(t('network.bindingSuccessful'))
getData();
}
getData()
</script>
<style lang="scss">
</style>

104
src/pages/auth/google.vue Normal file
View File

@@ -0,0 +1,104 @@
<template>
<h-dark :title="t('auth.googleTitle')"></h-dark>
<view class="mx-[30rpx]" v-if="data.info.auth_google==1">
<view class="flex justify-center mt-[60rpx]">
<u-image width="180" height="180" mode="widthFix" src="/static/images/user/authgoogle.png"></u-image>
</view>
<view class="my-[30rpx] leading-loose">
{{t('auth.googleTIps')}}
</view>
<view class="flex justify-between mt-[100rpx]">
<view class="">
<view class="text-[#00D2E0] mt-[10rpx]">
{{data.info.google_key}}
</view>
</view>
<view class="">
<text class="">{{t('tabs.Verified')}}</text>
</view>
</view>
</view>
<view class="mx-[30rpx]" v-if="data.info.auth_google==0">
<view class="font-bold my-[30rpx]">
{{t('auth.googleStep1')}}
</view>
<view class="text-[#00D2E0]">
{{t('auth.googleStep1Tips')}}
</view>
<view class="font-bold my-[30rpx]">
{{t('auth.googleStep2')}}
</view>
<view class="">
<view class="">
<view class="flex justify-center">
<u-image width="330rpx" height="330rpx" :src="data.info.google_qrcode"></u-image>
</view>
</view>
<view class="text-[#00D2E0] text-center my-[40rpx]">
{{t('auth.googleStep2Tips')}}
</view>
<view class="bg-[#3d4277] rounded-lg px-[30rpx] py-[20rpx] mx-[100rpx]">
<view class="flex justify-center items-center" @click.stop="copyText(data.info.google_key)">
<view class="">
{{data.info.google_key}}
</view>
<view class="ml-[10rpx]">
<u-icon size="30" name="/static/images/common/copy.png"></u-icon>
</view>
</view>
</view>
</view>
<view class="font-bold my-[30rpx]">
{{t('auth.googleStep3')}}
</view>
<view class="bg-[#3d4277] rounded-lg px-[20rpx] py-[10rpx]">
<u-input v-model="formData.code" type="number" :border="false"
:placeholder="t('auth.googleCodePlaceholder')" />
</view>
</view>
<view v-if="data.info.auth_google==0" class="max-w-[750px] fixed bottom-[50rpx] w-4/5 ml-[10%]">
<u-button type="primary" :disabled="formData.code == ''" @click="submitHandle"> {{t('auth.bindBtn')}}
</u-button>
</view>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { getUserInfo, verifyGoogle } from '@/api/user'
import { useCopy } from '@/hooks/useCopy'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('auth.googleTitle') })
const { copy } = useCopy()
const data = ref<any>({
info: {
auth_google: 2
}
})
const formData = reactive<any>({
code: ''
})
const getData = async () => {
const res = await getUserInfo()
data.value = res;
}
const copyText = async (text : string) => {
copy(text);
uni.$u.toast(t('common.copySuccess'))
}
const submitHandle = async () => {
await verifyGoogle(formData)
uni.$u.toast(t('network.bindingSuccessful'))
getData();
}
getData()
</script>
<style lang="scss">
</style>

186
src/pages/auth/realname.vue Normal file
View File

@@ -0,0 +1,186 @@
<template>
<h-dark :title="t('auth.realTitle')"></h-dark>
<view class="mx-[30rpx]" v-if="data.info.auth_card==1||data.info.auth_card==2||data.info.auth_card==3">
<view class="flex justify-center mt-[60rpx]">
<u-image width="180" height="180" mode="widthFix" src="/static/images/user/authrealname.png"></u-image>
</view>
<view class="my-[30rpx]">
{{t('auth.realTips')}}
</view>
<view class="flex justify-between items-center mt-[100rpx]">
<view class="">
<view class="text-[#00D2E0] mt-[10rpx]">
{{data.info.card_name}}({{data.info.card_num}})
</view>
</view>
<view class="">
<text class="" v-if="data.info.auth_card==1">{{t('tabs.Verified')}}</text>
<text class="" v-if="data.info.auth_card==2">{{t('tabs.auditing')}}</text>
<view class="text-error" v-if="data.info.auth_card==3" @click="data.info.auth_card=0">
{{t('tabs.authFailed')}}
<u-icon name="arrow-right" size="22"></u-icon>
</view>
</view>
</view>
<view class="text-error" v-if="data.info.auth_card==3">
{{data.info.card_remark}}
</view>
</view>
<view class="mx-[30rpx]" v-if="data.info.auth_card==0">
<u-form :border-bottom="false" :label-width="150" label-position="top">
<u-form-item :itemStyle="{'background':'#3d4277'}" :label="t('auth.realName')" :border-bottom="false">
<u-input class="flex-1" v-model="formData.card_name" maxlength="20"
:placeholder="t('auth.realNamePlaceholder')" :border="false" />
</u-form-item>
<u-form-item :itemStyle="{'background':'#3d4277'}" :label="t('auth.realNum')" :border-bottom="false">
<u-input class="flex-1" v-model="formData.card_num" maxlength="32"
:placeholder="t('auth.realNumPlaceholder')" :border="false" />
</u-form-item>
<u-form-item :label="t('auth.realImg')" :border-bottom="false">
<view class="w-full">
<view class="flex justify-between w-full">
<view class="w-[48%] flex justify-center img1">
<u-upload class="" ref="uploader1" :custom-btn="true" :deletable="false"
:auto-upload="false" max-count="1" :max-size="20*1024*1024" v-model="formData.card_img1">
<template v-slot:addBtn>
<view class="text-center items-center slot-btn" hover-class="slot-btn__hover"
hover-stay-time="150">
<u-icon name="/static/images/common/photo.png" size="80"></u-icon>
<view class="text-[#000000]">
{{t('auth.realImg1')}}
</view>
</view>
</template>
</u-upload>
</view>
<view class="w-[48%] flex justify-center img2">
<u-upload class="" ref="uploader2" :custom-btn="true" :deletable="false"
:auto-upload="false" max-count="1" :max-size="20*1024*1024" v-model="formData.card_img2">
<template v-slot:addBtn>
<view class="text-center items-center slot-btn" hover-class="slot-btn__hover"
hover-stay-time="150">
<u-icon name="/static/images/common/photo.png" size="80"></u-icon>
<view class="text-[#000000]">
{{t('auth.realImg2')}}
</view>
</view>
</template>
</u-upload>
</view>
</view>
<view class="flex justify-start w-full mt-[20rpx]">
<view class="w-[48%] flex justify-center img1">
<u-upload class="" ref="uploader3" :custom-btn="true" :deletable="false"
:auto-upload="false" max-count="1" :max-size="20*1024*1024" v-model="formData.card_img3">
<template v-slot:addBtn>
<view class="text-center items-center slot-btn" hover-class="slot-btn__hover"
hover-stay-time="150">
<u-icon name="/static/images/common/photo.png" size="80"></u-icon>
<view class="text-[#000000]">
{{t('auth.realImg3')}}
</view>
</view>
</template>
</u-upload>
</view>
</view>
</view>
</u-form-item>
</u-form>
</view>
<view v-if="data.info.auth_card==0" class="max-w-[750px] fixed bottom-[50rpx] w-4/5 ml-[10%]">
<u-button type="primary" :loading="uploading" :disabled="formData.card_name == ''||formData.card_num == ''"
@click="submitHandle"> {{t('common.submit')}}
</u-button>
</view>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { useUserStore } from '@/stores/user'
import { getUserInfo, verifyRealname } from '@/api/user'
import { uploadImage } from '@/api/app'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('auth.realTitle') })
const userStore = useUserStore()
const uploader1 = ref()
const uploader2 = ref()
const uploader3 = ref()
const uploading = ref(false)
const data = ref<any>({
info: {
auth_card: 4
}
})
const formData = reactive<any>({
card_name: '',
card_num: '',
card_img1: '',
card_img2: '',
card_img3: ''
})
const getData = async () => {
const res = await getUserInfo()
data.value = res;
}
const submitHandle = async () => {
if (uploading.value) return;
uploading.value = true;
if (uploader1.value.lists.length <= 0) {
uploading.value = false;
return uni.$u.toast(t('auth.realImg1Empty'))
}
if (uploader2.value.lists.length <= 0) {
uploading.value = false;
return uni.$u.toast(t('auth.realImg2Empty'))
}
if (uploader3.value.lists.length <= 0) {
uploading.value = false;
return uni.$u.toast(t('auth.realImg3Empty'))
}
if (formData.card_img1 == '') {
const res1 : any = await uploadImage(uploader1.value.lists[0].url, userStore.temToken!)
formData.card_img1 = res1.url;
}
if (formData.card_img2 == '') {
const res2 : any = await uploadImage(uploader2.value.lists[0].url, userStore.temToken!)
formData.card_img2 = res2.url;
} if (formData.card_img3 == '') {
const res3 : any = await uploadImage(uploader3.value.lists[0].url, userStore.temToken!)
formData.card_img3 = res3.url;
}
uploading.value = false;
await verifyRealname(formData)
uni.$u.toast(t('network.submittedSuccessfully'))
getData();
}
getData()
</script>
<style lang="scss">
.img1 {
background-image: url('/static/images/user/authrealnameimg1.png');
background-size: 100% auto;
background-repeat: no-repeat;
background-position: center;
height: 112px;
}
.img2 {
background-image: url('/static/images/user/authrealnameimg2.png');
background-size: 100% auto;
background-repeat: no-repeat;
background-position: center;
height: 112px;
}
</style>

View File

@@ -0,0 +1,117 @@
<template>
<h-dark :title="t('pwd.titleBind')"></h-dark>
<view class="bg-white min-h-full flex flex-col items-center px-[40rpx] pt-[120rpx] box-border">
<view>
<u-image src="../../static/images/login/register-bg.png" mode="widthFix" height="260" width="260" />
</view>
<view class="w-full">
<u-form :border-bottom="false" :label-width="150" label-position="top">
<u-form-item :border-bottom="false" :label="t('pwd.mobile')" :required="true">
<u-picker class="max-w-[750px]" mode="selector" v-model="picker_show"
:default-selector="[country_code.default_select]" :range="country_code.list"
@confirm="handlePicker" :confirm-text="t('common.confirm')" :cancel-text="t('common.cancel')">
</u-picker>
<view class="mr-[20rpx]" @click="picker_show=true">
<text class="mr-[10rpx]">{{formData.country_code}}
</text>
<u-icon name="arrow-down" size="20"></u-icon>
</view>
<u-input class="flex-1" v-model="formData.mobile" :border="false"
:placeholder="t('pwd.mobilePlaceholder')" />
</u-form-item>
<u-form-item :border-bottom="false" :label="t('captcha.captcha')" :required="true">
<u-input class="flex-1" v-model="formData.code" :placeholder="t('captcha.captchaPlaceholder')"
:border="false" />
<view class="border-l border-solid border-0 border-light pl-3 text-muted leading-4 ml-3 w-[180rpx]"
@click="sendSms">
<u-verification-code ref="uCodeRef" :seconds="60" @change="codeChange"
:change-text="t('captcha.changeText')" :startText="t('captcha.startText')"
:endText="t('captcha.endText')" />
<text :class="formData.mobile ? 'text-primary' : 'text-muted'">
{{ codeTips }}
</text>
</view>
</u-form-item>
</u-form>
<view class="mt-[40rpx]">
<u-button type="primary" shape="circle" @click="handleConfirm"> {{t('common.confirm')}} </u-button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { userBindMobile } from '@/api/user'
import { smsSend, getCountryCode } from '@/api/app'
import { SMSEnum } from '@/enums/appEnums'
import { reactive, ref, shallowRef } from 'vue'
import { useUserStore } from '@/stores/user'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('pwd.titleBind') })
const uCodeRef = shallowRef()
const codeTips = ref('')
const userStore = useUserStore()
const codeChange = (text : string) => {
codeTips.value = text
}
const formData = reactive({
type: 'bind',
mobile: '',
code: '',
country_code: ''
})
const picker_show = ref(false)
const country_code = reactive<{
list : any[]
default_select : number
}>({
list: [],
default_select: 0
})
const getData = async () => {
let data = await getCountryCode()
country_code.list = data;
formData.country_code = data[0]
}
const handlePicker = (index : number) => {
country_code.default_select = index;
formData.country_code = country_code.list[index];
}
const sendSms = async () => {
if (!formData.mobile) return uni.$u.toast(t('captcha.mobileEmpty'))
if (uCodeRef.value?.canGetCode) {
await smsSend({
scene: SMSEnum.BIND_MOBILE,
mobile: formData.mobile,
country_code: formData.country_code
})
uni.$u.toast(t('captcha.sendSuccessfully'))
uCodeRef.value?.start()
}
}
const handleConfirm = async () => {
if (!formData.mobile) return uni.$u.toast(t('captcha.mobileEmpty'))
if (!formData.code) return uni.$u.toast(t('captcha.captchaEmpty'))
await userBindMobile(formData, { token: userStore.temToken })
uni.$u.toast(t('pwd.bindSuccess'))
userStore.login(userStore.temToken!)
setTimeout(() => {
uni.reLaunch({
url: '/pages/index/index'
})
}, 1500)
}
getData()
</script>
<style lang="scss">
page {
height: 100%;
}
</style>

View File

@@ -0,0 +1,51 @@
<template>
<h-dark :title="t('pwd.chLoginPwd')"></h-dark>
<view class="flex flex-col items-center px-[30rpx] box-border">
<view class="w-full">
<u-form :border-bottom="false" :label-width="150" label-position="top">
<u-form-item :itemStyle="{'background':'#3d4277'}" :label="t('pwd.oldPwd')" :border-bottom="false">
<u-input class="flex-1" type="password" v-model="formData.old_password" :border="false"
:placeholder="t('pwd.oldPwdPlaceholder')" />
</u-form-item>
<u-form-item :itemStyle="{'background':'#3d4277'}" :label="t('pwd.newPwd')" :border-bottom="false">
<u-input class="flex-1" type="password" v-model="formData.password" :placeholder="t('pwd.newPwdPlaceholder')"
:border="false" />
</u-form-item>
<u-form-item :itemStyle="{'background':'#3d4277'}" :label="t('pwd.confirmPwd')" :border-bottom="false">
<u-input class="flex-1" type="password" v-model="formData.password_confirm" :placeholder="t('pwd.confirmPwdPlaceholder')"
:border="false" />
</u-form-item>
</u-form>
<view class="mt-[100rpx]">
<u-button type="primary" :disabled="formData.old_password==''||formData.password==''||formData.password_confirm==''" @click="handleConfirm"> {{t('common.submit')}} </u-button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { userChangePwd } from '@/api/user'
import { reactive } from 'vue'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('pwd.titleChangePwd') })
const formData = reactive<any>({
old_password:'',
password: '',
password_confirm: ''
})
const handleConfirm = async () => {
if (!formData.old_password) return uni.$u.toast(t('pwd.oldPwdEmpty'))
if (!formData.password) return uni.$u.toast(t('pwd.newPwdEmpty'))
if (!formData.password_confirm) return uni.$u.toast(t('pwd.confirmPwdEmpty'))
if (formData.password != formData.password_confirm) return uni.$u.toast(t('pwd.twoPwdError'))
await userChangePwd(formData)
uni.$u.toast(t('network.modifySuccessfully'))
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,150 @@
<template>
<h-dark :title="t('pwd.chPayPwd')"></h-dark>
<view class="flex flex-col items-center px-[40rpx] box-border">
<view class="w-full">
<u-form :border-bottom="false" :label-width="150" label-position="top">
<u-form-item :itemStyle="{'background':'#3d4277'}" :label="t('pwd.oldPwd')" :border-bottom="false"
@click="inputClick(1)">
<text v-if="formData.old_password_pay != ''">{{formData.old_password_pay}}</text>
<text v-if="formData.old_password_pay == ''" class="text-muted">{{t('pwd.oldPwdEmpty')}}</text>
</u-form-item>
<u-form-item :itemStyle="{'background':'#3d4277'}" :label="t('pwd.newPwd')" :border-bottom="false"
@click="inputClick(2)">
<text v-if="formData.password_pay != ''">{{formData.password_pay}}</text>
<text v-if="formData.password_pay == ''" class="text-muted">{{t('pwd.newPwdEmpty')}}</text>
</u-form-item>
<u-form-item :itemStyle="{'background':'#3d4277'}" :label="t('pwd.confirmPwd')" :border-bottom="false"
@click="inputClick(3)">
<text v-if="formData.password_pay_confirm != ''">{{formData.password_pay_confirm}}</text>
<text v-if="formData.password_pay_confirm == ''"
class="text-muted">{{t('pwd.confirmPwdEmpty')}}</text>
</u-form-item>
</u-form>
<view class="mt-[100rpx]">
<u-button type="primary" @click="handleConfirm"> {{t('common.submit')}} </u-button>
</view>
<u-keyboard class="max-w-[750px]" ref="uKeyboard1" mode="number" :mask-close-able="false" :dot-enabled="false"
tips=" " confirm-text="" cancel-text="" v-model="show_keyboard" @change="keyboardChange"
@backspace="backspace" :show-input-val="true" :input-value="input.pwd"></u-keyboard>
</view>
</view>
</template>
<script setup lang="ts">
import { getUserInfo, userChangePayPwd } from '@/api/user'
import { reactive, ref } from 'vue'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('pwd.titleChangePayPwd') })
const formData = reactive<any>({
old_password_pay: '',
password_pay: '',
password_pay_confirm: ''
})
const input = reactive<any>({
pwd: '',
})
const show_keyboard = ref(false)
const show_type = ref(1)
const loading_keyboard = ref(false)
const keyboardChange = (val : string) => {
switch (show_type.value) {
case 1:
formData.old_password_pay += val;
if (formData.old_password_pay.length == 6) {
show_keyboard.value = false;
//延时触发,防止误触
setTimeout(() => {
loading_keyboard.value = false;
}, 500)
}
break
case 2:
formData.password_pay += val;
if (formData.password_pay.length == 6) {
show_keyboard.value = false;
setTimeout(() => {
loading_keyboard.value = false;
}, 500)
}
break
case 3:
formData.password_pay_confirm += val;
if (formData.password_pay_confirm.length == 6) {
show_keyboard.value = false;
setTimeout(() => {
loading_keyboard.value = false;
}, 500)
}
break
}
input.pwd += val
}
const backspace = () => {
// 删除value的最后一个字符
switch (show_type.value) {
case 1:
if (formData.old_password_pay.length) formData.old_password_pay = (formData.old_password_pay).substr(0, (formData.old_password_pay.length) - 1);
break
case 2:
if (formData.password_pay.length) formData.password_pay = (formData.password_pay).substr(0, (formData.password_pay.length) - 1);
break
case 3:
if (formData.password_pay_confirm.length) formData.password_pay_confirm = (formData.password_pay_confirm).substr(0, (formData.password_pay_confirm.length) - 1);
break
}
if (input.pwd.length) input.pwd = (input.pwd).substr(0, (input.pwd.length) - 1);
}
const inputClick = (type : number) => {
if (loading_keyboard.value) return
show_type.value = type
input.pwd = ''
switch (show_type.value) {
case 1:
formData.old_password_pay = '';
break
case 2:
formData.password_pay = '';
break
case 3:
formData.password_pay_confirm = '';
break
}
show_keyboard.value = true;
loading_keyboard.value = true;
}
const handleConfirm = async () => {
if (formData.old_password_pay.length != 6) return uni.$u.toast(t('pwd.oldPwdEmpty'))
if (formData.password_pay.length != 6) return uni.$u.toast(t('pwd.newPwdEmpty'))
if (formData.password_pay_confirm.length != 6) return uni.$u.toast(t('pwd.confirmPwdEmpty'))
if (formData.password_pay != formData.password_pay_confirm) return uni.$u.toast(t('pwd.twoPwdError'))
await userChangePayPwd(formData)
uni.$u.toast(t('network.modifySuccessfully'))
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
const getData = async () => {
const res = await getUserInfo()
if (res.need_set_pwd == 1) {
uni.$u.toast(t('pwd.pwdSetFirstTips'))
uni.redirectTo({
url: '/pages/change_password/set_password_pay'
})
return
}
}
getData()
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,110 @@
<template>
<h-dark :title="t('pwd.titleSetPwd')"></h-dark>
<view class="flex flex-col items-center px-[40rpx] box-border">
<view class="w-full">
<u-form :border-bottom="false" :label-width="150" label-position="top">
<u-form-item :itemStyle="{'background':'#3d4277'}" :label="t('pwd.pwd')" :border-bottom="false" @click="inputClick(2)">
<text v-if="formData.password_pay != ''">{{formData.password_pay}}</text>
<text v-if="formData.password_pay == ''" class="text-muted">{{t('pwd.pwdEmpty')}}</text>
</u-form-item>
<u-form-item :itemStyle="{'background':'#3d4277'}" :label="t('pwd.pwdConfirm')" :border-bottom="false" @click="inputClick(3)">
<text v-if="formData.password_pay_confirm != ''">{{formData.password_pay_confirm}}</text>
<text v-if="formData.password_pay_confirm == ''" class="text-muted">{{t('pwd.pwdConfirmEmpty')}}</text>
</u-form-item>
</u-form>
<view class="mt-[100rpx]">
<u-button type="primary" :disabled="formData.password_pay==''||formData.password_pay_confirm==''"
@click="handleConfirm"> {{t('common.submit')}} </u-button>
</view>
<u-keyboard class="max-w-[750px]" ref="uKeyboard1" mode="number" :mask-close-able="false" :dot-enabled="false"
tips=" " confirm-text="" cancel-text="" v-model="show_keyboard" @change="keyboardChange"
@backspace="backspace" :show-input-val="true" :input-value="input.pwd"></u-keyboard>
</view>
</view>
</template>
<script setup lang="ts">
import { setPayPwd } from '@/api/user'
import { reactive, ref } from 'vue'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('pwd.titleSetPwd') })
const formData = reactive<any>({
password_pay: '',
password_pay_confirm: ''
})
const input = reactive<any>({
pwd: '',
})
const show_keyboard = ref(false)
const show_type = ref(1)
const loading_keyboard = ref(false)
const keyboardChange = (val : string) => {
switch (show_type.value) {
case 2:
formData.password_pay += val;
if (formData.password_pay.length == 6) {
show_keyboard.value = false;
setTimeout(() => {
loading_keyboard.value = false;
}, 500)
}
break
case 3:
formData.password_pay_confirm += val;
if (formData.password_pay_confirm.length == 6) {
show_keyboard.value = false;
setTimeout(() => {
loading_keyboard.value = false;
}, 500)
}
break
}
input.pwd += val
}
const backspace = () => {
// 删除value的最后一个字符
switch (show_type.value) {
case 2:
if (formData.password_pay.length) formData.password_pay = (formData.password_pay).substr(0, (formData.password_pay.length) - 1);
break
case 3:
if (formData.password_pay_confirm.length) formData.password_pay_confirm = (formData.password_pay_confirm).substr(0, (formData.password_pay_confirm.length) - 1);
break
}
if (input.pwd.length) input.pwd = (input.pwd).substr(0, (input.pwd.length) - 1);
}
const inputClick = (type : number) => {
if (loading_keyboard.value) return
show_type.value = type
input.pwd = ''
switch (show_type.value) {
case 2:
formData.password_pay = '';
break
case 3:
formData.password_pay_confirm = '';
break
}
show_keyboard.value = true;
loading_keyboard.value = true;
}
const handleConfirm = async () => {
if (formData.password_pay.length != 6) return uni.$u.toast(t('pwd.pwdEmpty'))
if (formData.password_pay_confirm.length != 6) return uni.$u.toast(t('pwd.pwdConfirmEmpty'))
if (formData.password_pay != formData.password_pay_confirm) return uni.$u.toast(t('pwd.twoPwdError'))
await setPayPwd(formData)
uni.$u.toast(t('network.setSuccessful'))
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,63 @@
<!-- <template>
<h-dark title="我的收藏"></h-dark>
<z-paging
ref="paging"
v-model="collectData"
@query="queryList"
:fixed="false"
height="100%"
use-page-scroll
>
<u-swipe-action
:show="item.show"
:index="index"
v-for="(item, index) in collectData"
:key="item.id"
@click="handleCollect"
:options="options"
btn-width="120"
>
<news-card :item="item" :newsId="item.article_id"></news-card>
</u-swipe-action>
</z-paging>
</template>
<script lang="ts" setup>
import { ref, reactive, shallowRef } from 'vue'
import { getCollect, cancelCollect } from '@/api/news'
const paging = shallowRef()
const options = reactive([
{
text: '取消收藏',
style: {
color: '#FFFFFF',
backgroundColor: '#FF2C3C'
}
}
])
const collectData: any = ref([])
const queryList = async (pageNo, pageSize) => {
const { lists } = await getCollect()
lists.forEach((item: any) => {
item.show = false
})
collectData.value = lists
paging.value.complete(lists)
}
const handleCollect = async (index: number): Promise<void> => {
try {
const article_id: number = collectData.value[index].article_id
await cancelCollect({ id: article_id })
uni.$u.toast('已取消收藏')
paging.value.reload()
} catch (err) {
//TODO handle the exception
console.log('取消收藏报错=>', err)
}
}
</script>
<style scoped></style -->>

View File

@@ -0,0 +1,66 @@
<template>
<view class="absolute w-full h-full bg-[#4E55AF]">
<!-- <u-navbar :background="background" title=""></u-navbar> -->
<view class=" flex flex-col items-center mt-[180rpx]">
<u-image width="500" height="300" mode="widthFix" src="/static/images/withdraw/withdraw_bg.png"></u-image>
</view>
<view class="my-[100rpx] text-center px-[50rpx]">
<view class="type-primary font-bold mb-[50rpx]">
{{text1}}
</view>
<view class="leading-loose">
{{text2}}
</view>
</view>
<view class="max-w-[750px] fixed bottom-[50rpx] w-4/5 ml-[10%]">
<u-button type="primary" @click="submitHandle"> {{t('common.confirm')}} </u-button>
</view>
</view>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { t } from '@/utils/util'
const text1 = ref('')
const text2 = ref('')
let type_arr = [{
text1: t('orderDetail.rechargeSuccess'),
text2: t('orderDetail.rechargeSuccessTips')
}, {
text1: t('orderDetail.withdrawSuccess'),
text2: t('orderDetail.withdrawSuccessTips')
}, {
text1: t('orderDetail.vipOpenSuccess'),
text2: ""
}, {
text1: t('orderDetail.bindSuccess'),
text2: ""
}]
const submitHandle = () => {
let canNavBack = getCurrentPages()
if( canNavBack && canNavBack.length>1) {
uni.navigateBack({
delta: 2
});
} else {
history.back();
}
}
onLoad((options : any) => {
text1.value = type_arr[options.type]['text1'];
text2.value = type_arr[options.type]['text2'];
})
</script>
<style lang="scss">
.type-primary {
color: $u-type-primary;
}
</style>

View File

@@ -0,0 +1,7 @@
<template>
<div></div>
</template>
<script setup lang="ts"></script>
<style></style>

60
src/pages/faq/index.vue Normal file
View File

@@ -0,0 +1,60 @@
<template>
<z-paging :refresher-enabled="false" :show-loading-more-when-reload="true" loading-more-default-text=""
loading-more-loading-text="" loading-more-fail-text="" :empty-view-text="t('common.noData')"
:show-loading-more-no-more-view="false" ref="paging" v-model="dataList" @query="queryList"
class="max-w-[750px]">
<h-dark :title="t('article.titleHelp')"></h-dark>
<view class="mx-[30rpx] mt-[20rpx]">
<view class="team-item p-[30rpx] mb-[20rpx] bg-[#2C326B] rounded-lg" v-for="(item, index) in dataList"
:key="index">
<navigator :url="'/pages/news_detail/news_detail?id='+item.id">
<view class="news-card flex">
<view class="news-card-content flex flex-col justify-between flex-1">
<view class="news-card-content-title text-lg font-medium">{{ item.title }}</view>
</view>
<u-icon name="arrow-right" class="text-muted" size="28"></u-icon>
</view>
</navigator>
</view>
</view>
</z-paging>
</template>
<script lang="ts" setup>
import { ref, shallowRef } from 'vue'
import { getArticleList } from '@/api/news'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('article.titleHelp') })
const paging = shallowRef()
const dataList = ref<any[]>([])
const queryList = async (pageNo : number, pageSize : number) => {
try {
const data = await getArticleList({
page_no: pageNo,
page_size: pageSize,
cid: 3
})
paging.value.complete(data.lists)
} catch (error) {
paging.value.complete(false)
}
}
</script>
<style lang="scss">
.news-card {
&-content {
&-title {
-webkit-line-clamp: 1;
overflow: hidden;
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
}
}
}
</style>

View File

@@ -0,0 +1,80 @@
<template>
<h-dark :title="t('feedback.title')"></h-dark>
<view class="mx-[30rpx]">
<view class="py-[20rpx]">
{{t('feedback.type')}}
</view>
<view class="bg-[#2C326B] rounded-lg p-[24rpx] mb-[20rpx]">
<u-radio-group v-model="formData.cid" :wrap="true">
<u-radio v-for="(item, index) in data.cates" :key="index" :name="item.id" @change="handelChange(item)">
{{item.name}}
</u-radio>
</u-radio-group>
</view>
<view class="py-[20rpx]">
{{t('feedback.opinion')}}
</view>
<view class="bg-[#2C326B] rounded-lg p-[24rpx]">
<u-input v-model="formData.content" type="textarea" height="200" maxlength="256"
:placeholder="t('feedback.opinionPlaceholder')" />
</view>
</view>
<view class="max-w-[750px] fixed bottom-[50rpx] w-4/5 ml-[10%]">
<u-button type="primary" @click="handleSubmit"> {{t('common.submit')}} </u-button>
</view>
</template>
<script lang="ts" setup>
import { reactive } from 'vue'
import { feedback, getFeedbackCate } from '@/api/user'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('feedback.title') })
const data = reactive<{
cates : any[]
}>({
cates: [],
})
const formData = reactive({
cid: '',
content: '',
})
const getData = async () => {
const res = await getFeedbackCate()
data.cates = res.cates
if (data.cates.length > 0) {
formData.cid = data.cates[0]['id']
}
}
const handelChange = async (item : any) => {
formData.cid = item.id
}
const handleSubmit = async () => {
if (!formData.cid) return uni.$u.toast(t('feedback.typeEmpty'))
if (!formData.content) return uni.$u.toast(t('feedback.opinionEmpty'))
await feedback(formData)
formData.content = ''
uni.$u.toast(t('feedback.feedbackSuccess'))
setTimeout(() => {
handleBack()
}, 1500)
}
const handleBack = async () => {
let canNavBack = getCurrentPages()
if (canNavBack && canNavBack.length > 1) {
uni.navigateBack()
} else {
history.back();
}
}
getData()
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,184 @@
<template>
<u-navbar :title="t('pwd.fgLoginPwd')" :background="{backgroundColor: '#0066ff'}" back-icon-color="#FFFFFF"
title-color="#FFFFFF">
<view class="flex justify-end w-full pr-[20rpx]">
<navigator hover-class="none" url="/pages/kefu/index">
<u-icon size="40" name="/static/images/common/kefu.png"></u-icon>
</navigator>
</view>
</u-navbar>
<view class="flex flex-col items-center px-[40rpx] box-border">
<view class="w-full">
<u-form :border-bottom="false" :label-width="150" label-position="top">
<u-form-item :itemStyle="{'background':'#f6f7fb'}" :label="t('pwd.mobile')" :border-bottom="false"
:required="true">
<country-code :list="country_code.list" :show="picker_show" @update="handleCountryCode">
</country-code>
<view class="mr-[20rpx]" @click="picker_show=true">
<text class="mr-[10rpx]">{{formData.country_code}}
</text>
<u-icon name="arrow-down" size="20"></u-icon>
</view>
<u-input class="flex-1" v-model="formData.mobile" type="number" :border="false"
:placeholder="t('pwd.mobilePlaceholder')" />
</u-form-item>
<view class="rounded-lg overflow-hidden mt-[20rpx] p-[7rpx] bg-[#F0F2F6]">
<u-tabs :list="tabList" v-model="formData.type" @change="changeType" :is-scroll="false" height="60"
:show-bar="false" bg-color="#F0F2F6"
:active-item-style="{'background-color':'#FFFFFF','border-radius':'5px'}"></u-tabs>
</view>
<u-form-item :itemStyle="{'background':'#f6f7fb'}" :label="t('auth.email')" :border-bottom="false"
:required="true" v-if="formData.type==1">
<u-input class="flex-1" type="email" v-model="formData.email"
:placeholder="t('auth.emailPlaceholder')" :border="false" />
</u-form-item>
<u-form-item :itemStyle="{'background':'#f6f7fb'}" :label="t('pwd.captcha')" :border-bottom="false"
:required="true" v-if="formData.type==1">
<u-input class="flex-1" v-model="formData.code" :placeholder="t('captcha.captchaPlaceholder')"
:border="false" />
<view class="border-l border-solid border-0 border-light pl-3 text-muted leading-4 ml-3 w-[180rpx]"
@click="sendSms">
<u-verification-code ref="uCodeRef" :seconds="60" @change="codeChange"
:change-text="t('captcha.changeText')" :startText="t('captcha.startText')"
:endText="t('captcha.endText')" />
<text :class="formData.email ? 'text-primary' : 'text-muted'">
{{ codeTips }}
</text>
</view>
</u-form-item>
<u-form-item :itemStyle="{'background':'#f6f7fb'}" :label="t('pwd.captcha')" :border-bottom="false" :required="true"
v-if="formData.type==0">
<u-input class="flex-1" v-model="formData.code" :placeholder="t('captcha.captchaPlaceholder')"
:border="false" />
</u-form-item>
<u-form-item :itemStyle="{'background':'#f6f7fb'}" :label="t('pwd.newPwd')" :border-bottom="false"
:required="true">
<u-input class="flex-1" type="password" v-model="formData.password"
:placeholder="t('pwd.newPwdPlaceholder')" :border="false" />
</u-form-item>
<u-form-item :itemStyle="{'background':'#f6f7fb'}" :label="t('pwd.confirmPwd')" :border-bottom="false"
:required="true">
<u-input class="flex-1" type="password" v-model="formData.password_confirm"
:placeholder="t('pwd.confirmPwdPlaceholder')" :border="false" />
</u-form-item>
</u-form>
<view class="mt-[100rpx] pb-[60rpx]">
<u-button type="primary" v-if="formData.type==0"
:disabled="formData.mobile==''||formData.code==''||formData.password==''||formData.password_confirm==''"
@click="handleConfirm"> {{t('common.submit')}} </u-button>
<u-button type="primary" v-if="formData.type==1"
:disabled="formData.mobile==''||formData.email==''||formData.code==''||formData.password==''||formData.password_confirm==''"
@click="handleConfirm"> {{t('common.submit')}} </u-button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { sendEmailNoLogin, getCountryCode } from '@/api/app'
import { useAppStore } from '@/stores/app'
import { forgotPassword } from '@/api/user'
import { SMSEnum } from '@/enums/appEnums'
import { reactive, ref, shallowRef } from 'vue'
import { t, isEmpty } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('pwd.titleForgetPwd') })
const appStore = useAppStore()
const uCodeRef = shallowRef()
const codeTips = ref('')
const picker_show = ref(false)
const country_code = reactive<{
list : any[]
default_select : number
}>({
list: [],
default_select: 0
})
const tabList = ref([
{
name: t('pwd.authGoogle'),
},
{
name: t('pwd.authEmail'),
}
])
const changeType = (index : number) => {
formData.type = index
}
const formData = reactive({
mobile: '',
email: '',
code: '',
password: '',
password_confirm: '',
country_code: '',
type: 0
})
const getData = async () => {
let data = appStore.getLoginConfig.region_code
if (isEmpty(data)) data = await getCountryCode()
country_code.list = data;
formData.country_code = data[0]['code']
}
const handlePicker = (index : number) => {
country_code.default_select = index;
formData.country_code = country_code.list[index];
}
const handleCountryCode = (data : any) => {
picker_show.value = data['show']
if (data['code'] != '') {
formData.country_code = data['code'];
}
}
const codeChange = (text : string) => {
codeTips.value = text
}
const sendSms = async () => {
if (!formData.email) return
var emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
if (!emailRegex.test(formData.email)) {
return uni.$u.toast(t('auth.emailEmpty'))
}
if (uCodeRef.value?.canGetCode) {
await sendEmailNoLogin({
country_code: formData.country_code,
mobile: formData.mobile,
email: formData.email,
})
uni.$u.toast(t('captcha.sendSuccessfully'))
uCodeRef.value?.start()
}
}
const handleConfirm = async () => {
if (!formData.mobile) return uni.$u.toast(t('pwd.mobileEmpty'))
if (!formData.code) return uni.$u.toast(t('captcha.captchaEmpty'))
if (!formData.password) return uni.$u.toast(t('pwd.newPwdEmpty'))
if (!formData.password_confirm) return uni.$u.toast(t('pwd.confirmPwdEmpty'))
if (formData.password != formData.password_confirm) return uni.$u.toast(t('pwd.twoPwdError'))
await forgotPassword(formData)
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
getData()
</script>
<style lang="scss">
page {
background-color: #FFFFFF;
height: 100%;
}
.register {
height: 100%;
}
</style>

161
src/pages/fund/index.vue Normal file
View File

@@ -0,0 +1,161 @@
<template>
<view>
<u-navbar :is-back="false" title="" :background="{backgroundColor: '#FFFFFF'}" title-color="#000000">
<view class="slot-wrap w-full pl-[30rpx]">
<view class="">
{{t('funds.title')}}
</view>
</view>
<view class="flex justify-end w-full pr-[30rpx] relative">
<navigator hover-class="none" url="/packages/pages/user_wallet/record">
<u-icon size="42" name="/static/images/common/record_b.png"></u-icon>
</navigator>
</view>
</u-navbar>
<view class="px-[30rpx] pt-[30rpx] mb-[30rpx]">
<navigator hover-class="none" url="/pages/news_detail/detail?id=11">
<view class="flex justify-between items-center mb-[30rpx] py-[20rpx] px-[30rpx] rounded-lg bg-white">
<view class="flex justify-start items-center">
<view class="mr-[20rpx]">
<u-icon size="40" name="/static/images/fund/fund_desc.png"></u-icon>
</view>
<view class="">
{{t('funds.introduction')}}
</view>
</view>
<view class="">
<u-icon size="26" name="arrow-right"></u-icon>
</view>
</view>
</navigator>
<view class="bg-white rounded-lg py-[50rpx] px-[50rpx]">
<view class="flex justify-around items-center mb-[30rpx]">
<view class="text-center w-1/2">
<view class="text-muted mb-[10rpx]">
{{t('funds.totalAssets')}}
</view>
<view class="font-bold text-[#0066FF]">
{{getCurrency()}}{{formatMoney(state.report.user_money)}}
</view>
</view>
<view class="text-center w-1/2">
<view class="text-muted mb-[10rpx]">
{{t('funds.userPoint')}}
</view>
<view class="font-bold">
{{state.report.user_point}}
</view>
</view>
<!-- <view class="text-center w-1/3">
<view class="text-muted mb-[10rpx]">
{{t('funds.usedNoMoney')}}
</view>
<view class="font-bold">
{{getCurrency()}}{{formatMoney(state.report.noused_money)}}
</view>
</view> -->
</view>
<view class="flex justify-between py-[20rpx]">
<view @click="handleJump('/packages/pages/recharge/index')"
class="text-center w-[48%] flex items-center justify-center rounded-lg py-[20rpx]"
style="border: 1px solid #0066ff;">
<u-icon class="mr-[20rpx]" size="44" name="/static/images/fund/recharge.png"></u-icon>
{{t('funds.recharge')}}
</view>
<view @click="handleJump('/packages/pages/withdraw/index')"
class="text-center w-[48%] flex items-center justify-center rounded-lg py-[20rpx] text-white"
style="background: linear-gradient(114.5deg, #00A3FF 16.6%, #0066FF 85.41%);">
<u-icon class="mr-[20rpx]" size="44" name="/static/images/fund/withdraw.png"></u-icon>
{{t('funds.withdraw')}}
</view>
</view>
</view>
</view>
<view class="mx-[30rpx] mb-[30rpx] font-bold flex justify-between">
<view class=" ">
{{t('funds.assetDetails')}}
</view>
<view class="text-[#0066FF]" @click="handleJump('/packages/pages/user_wallet/record')">
{{t('funds.viewAll')}}
</view>
</view>
<view class="mx-[30rpx] ">
<view class="mt-[100rpx]" v-if="state.fund_list.length == 0">
<u-empty :text="t('common.noData')" mode="data"></u-empty>
</view>
<navigator v-for="item in state.fund_list" :key="item.id" hover-class="none"
:url="'/packages/pages/user_wallet/order_detail?id='+item.id">
<view class="bg-white mb-[8px] px-[40rpx] py-[40rpx] rounded-lg">
<view
class="flex justify-between mb-[30rpx] pb-[20rpx] border-solid border-b border-0 border-light ">
<view class=" text-muted mr-1">{{item.create_time}}</view>
<view class="text-success" v-if="item.action==1">
{{t('tabs.in')}}
</view>
<view class="text-error" v-if="item.action==2">
{{t('tabs.out')}}
</view>
</view>
<view class="flex justify-between">
<view class="w-[70%]">
<view class="">
{{t("finance.type" + item.type)}}
</view>
<view class="text-error mt-[12rpx]" v-if="item.remark">
{{item.remark}}
</view>
</view>
<view class="text-right w-[30%]">
<text class="font-bold" :class="item.action==1?'text-success':'text-error'">
{{getCurrency() }}{{formatMoney(item.amount)}}
</text>
</view>
</view>
</view>
</navigator>
</view>
</view>
<tabbar />
</template>
<script lang="ts" setup>
import { onShow } from '@dcloudio/uni-app'
import { useAppStore } from '@/stores/app'
import { reactive } from 'vue'
import { fundIndex } from '@/api/finance'
import { t, formatDate, getCurrency, formatMoney } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('funds.title') })
const appStore = useAppStore()
const state = reactive<{
fund_list : any[]
report : any[]
}>({
fund_list: [],
report: {
user_money:0,
user_point: 0
}
})
const handleJump = async (val : string) => {
uni.navigateTo({
url: val
})
}
const getData = async () => {
const data = await fundIndex({ date: formatDate(Math.round(Date.now() / 1000)) })
state.fund_list = data.fund_list
state.report = data.report
}
onShow(() => {
getData()
})
</script>
<style lang="scss">
</style>

169
src/pages/index/index.vue Normal file
View File

@@ -0,0 +1,169 @@
<template>
<view class="">
<u-navbar :is-back="false" :background="{backgroundColor: '#4E55AF'}" height="50">
<view class="flex justify-between items-center w-full">
<view class="flex justify-start items-center">
<view class="rounded-lg ml-[30rpx] bg-white p-[10rpx] mr-[12rpx]">
<u-image width="40rpx" height="40rpx" :src="appStore.getWebsiteConfig.shop_logo"></u-image>
</view>
<view class="">
{{appStore.getWebsiteConfig.shop_name}}
</view>
</view>
<view class="flex justify-end pr-[30rpx]">
<navigator hover-class="none" url="/pages/language/index">
<u-icon size="50" name="/static/images/common/change_lang_w.png"></u-icon>
</navigator>
</view>
</view>
</u-navbar>
<view class="bg-[#4E55AF] pb-[200rpx] pt-[30rpx]">
<view class="pb-[20rpx] mb-[50rpx] mx-[30rpx] bg-[#5C62B5] rounded-lg" v-if="state.notice.is_show">
<navigator hover-class="none" :url="'/pages/news_detail/news_detail?id='+state.notice.id">
<u-notice-bar class="h-[60rpx]" mode="horizontal" bg-color="unset" color="#FFFFFF"
:list="state.notice.text" />
</navigator>
</view>
</view>
<view class="mb-[20rpx] bg-[#2C326B] mx-[30rpx] rounded-lg mt-[-200rpx]" v-if="state.banner.length>0">
<w-banner :banner="state.banner" />
</view>
<view class="mb-[20rpx]" v-if="state.navs.length>0">
<view class="px-[30rpx]">
<w-nav :navs="state.navs" />
</view>
</view>
<view class="px-[30rpx] mb-[20rpx] inline-block" v-if="show_market">
<w-market :market="state.market" />
</view>
<view class="mb-[20rpx] rounded-xl overflow-hidden" v-if="state.video.is_show">
<view class="mx-[30rpx]">
<video class="w-full rounded-xl" :src="state.video.url" controls :poster="state.video.image"></video>
</view>
</view>
<view class="px-[30rpx] mb-[20rpx] inline-block " v-if="state.partner.is_show">
<view class="mb-[20rpx]">
{{t('index.partner')}}
</view>
<view class="" v-html="state.partner.content"></view>
</view>
<u-popup class="max-w-[750px]" v-model="state.popup.is_show" mode="center" border-radius="30" width="90%"
height="880rpx" :closeable="true" close-icon-color="#ECECEC" close-icon-size="36" @close="modalHandle">
<view class="">
<view class="notice text-center">
<view class="flex justify-center items-center py-[15px]">
<view class="bg-[#2C326B] rounded-full p-[15rpx]">
<u-image width="80rpx" height="80rpx" :src="appStore.getWebsiteConfig.shop_logo"></u-image>
</view>
</view>
</view>
<scroll-view scroll-y="true" style="height: 650rpx;">
<view class="leading-loose pb-[24rpx] px-[24rpx]" v-html="state.popup.content">
</view>
</scroll-view>
</view>
</u-popup>
<!-- 客服 -->
<view class="fixed right-[30rpx] bottom-[150rpx]" v-if="state.show_kefu" @click="jumpTo(state.kefu_link)">
<u-icon size="100" :name="state.kefu_logo"></u-icon>
</view>
</view>
<tabbar />
</template>
<script setup lang="ts">
import { onLoad, onUnload, onHide, onShow } from '@dcloudio/uni-app'
import { getIndex, getMarket } from '@/api/shop'
import { reactive, ref } from 'vue'
import { useAppStore } from '@/stores/app'
import { t, jumpTo } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('index.title') })
const appStore = useAppStore()
let timer : any = null
const show_market = ref(false)
const state = reactive<{
banner : any[]
notice : any
navs : any[]
partner : any[]
video : any[]
popup : any
market : any[]
kefu_logo : string
show_kefu : number
kefu_link : string
}>({
banner: [],
notice: {
text: '',
is_show: false
},
navs: [],
partner: [],
video: [],
popup: {
content: '',
is_show: false
},
market: [],
kefu_logo: '',
show_kefu: 0,
kefu_link: '',
})
const modalHandle = async () => {
state.popup.is_show = false;
}
const getData = async () => {
const data = await getIndex()
state.banner = data.banner
state.navs = data.navs
state.notice = data.notice
state.notice.text = [data.notice.text]
state.partner = data.partner
state.video = data.video
state.popup = data.popup
state.market = data.market
if (state.market.length > 0) show_market.value = true;
state.kefu_logo = data.kefu_logo;
state.show_kefu = data.show_kefu;
state.kefu_link = data.kefu_link;
}
const getTimer = async () => {
timer = setInterval(() => {
getMarketData();
}, 3000)
}
const getMarketData = async () => {
const res = await getMarket()
state.market = res;
if (state.market.length > 0) show_market.value = true;
}
onUnload(() => {
timer && clearInterval(timer)
})
onLoad(() => {
getData();
})
onShow(() => {
getTimer();
})
onHide(() => {
timer && clearInterval(timer)
})
</script>
<style lang="scss">
.nva-bg {
background-image: url('/static/images/index/noticebarbg.png');
background-size: 100% auto;
background-repeat: no-repeat;
background-position: bottom;
height: 80px;
}
</style>

View File

@@ -0,0 +1,58 @@
<template>
<view class="">
<h-dark :title="t('item.titleContract')"></h-dark>
<view class="my-[30rpx] mx-[30rpx]">
<view class="content leading-loose" v-html="data.content"></view>
</view>
</view>
</template>
<script lang="ts" setup>
import { reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getContract } from '@/api/item'
import { t, replaceStr } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('item.titleContract') })
const data = reactive({
content: ''
})
const getData = async (id : number) => {
const res = await getContract({ id: id })
data.content = res.content;
let cycle = res.cycle + ' ' + t('item.hour');
if (res.type == 1 || res.type == 2) {
cycle = res.cycle + ' ' + t('item.days');
}
let y_img = '<img src="' + res.contract_y_logo + '" style="width:120px;position: absolute;left: 30px;margin-top: -30px;">'
let b_img = '<img src="' + res.contract_b_logo + '" style="width:120px;position: absolute;left: 30px;margin-top: -30px;">'
data.content = replaceStr(data.content, '{type}', t("item.type" + res.type))
data.content = replaceStr(data.content, '{cycle}', cycle)
data.content = replaceStr(data.content, '{y_img}', y_img)
data.content = replaceStr(data.content, '{b_img}', b_img)
}
onLoad((options : any) => {
getData(options.id)
})
</script>
<style lang="scss">
page {
background-color: #4E55AF;
}
.content {
::v-deep table {
border-collapse: collapse;
}
::v-deep td {
padding: 0 5px;
border: 1px solid #EAEAEA;
}
}
</style>

240
src/pages/item/detail.vue Normal file
View File

@@ -0,0 +1,240 @@
<template>
<u-navbar :background="{backgroundColor: '#4E55AF'}" back-icon-color="#FFFFFF" height="50">
<view class="flex justify-end items-center w-full">
<view class="flex justify-end pr-[30rpx] ">
<navigator hover-class="none" url="/pages/item/record">
<u-icon size="40" name="/static/images/common/record.png"></u-icon>
</navigator>
</view>
</view>
</u-navbar>
<view class="bg-[#4E55AF] pt-[10rpx] px-[30rpx] mb-[30rpx] pb-[50rpx]">
<view class="mb-[30rpx]">
<text class="font-bold text-xl">{{data.item.title}}</text>
</view>
<view class="">
{{t("item.type" + data.item.type)}}
</view>
</view>
<view class="mt-[20rpx] pb-[30rpx]">
<view class="bg-[#2C326B] mx-[30rpx] mb-[20rpx] rounded-lg">
<view class=" rounded-lg p-[30rpx]">
<view class="flex justify-between mb-[20rpx]">
<view class="text-[#00D2E0]">
{{t('item.moneyRange')}}
</view>
<view class="">
{{getCurrency() }}{{formatMoney(data.item.min_money)}} -
{{getCurrency() }}{{formatMoney(data.item.max_money)}} {{getCurrency2()}}
</view>
</view>
<view class="flex justify-between mb-[20rpx]">
<view class="text-[#00D2E0]">
{{t('item.cycle')}}
</view>
<view class="">
<text class="mr-[5rpx]">{{ data.item.cycle }}</text>
<text v-if="data.item.type==1 || data.item.type==2">{{t('item.days')}}</text>
<text v-if="data.item.type==3 || data.item.type==4">{{t('item.hour')}}</text>
</view>
</view>
<view class="flex justify-between mb-[20rpx]">
<view class="text-[#00D2E0]">
<text v-if="data.item.type==1">{{t('item.dailyRate')}}</text>
<text v-if="data.item.type==4">{{t('item.hourRate')}}</text>
<text v-if="data.item.type==2 || data.item.type==3">{{t('item.totalRate')}}</text>
</view>
<view class="">
{{data.item.rate}}%
</view>
</view>
<view class="flex justify-between mb-[20rpx]" v-if="data.user.member.item_add_rate > 0">
<view class="text-[#00D2E0]">
{{t('item.vipAddRate')}}
</view>
<view class="">
{{data.user.member.item_add_rate}}%
</view>
</view>
<view class="flex justify-between mb-[20rpx]" v-if="data.item.point > 0">
<view class="text-[#00D2E0]">
{{t('item.point')}}
</view>
<view class="w-3/5 text-right ">
{{data.item.point}}
</view>
</view>
<view class="flex justify-between mb-[40rpx]">
<view class="text-[#00D2E0]">
{{t('item.limitVip')}}
</view>
<view class="">
{{data.item.vip_name}}
</view>
</view>
<view class="flex justify-between">
<u-line-progress :striped="true" :percent="data.item.progress" :striped-active="true"
active-color="#4E55AF"></u-line-progress>
</view>
</view>
</view>
<view class="bg-[#2C326B] mx-[30rpx] mb-[20rpx] rounded-lg p-[30rpx]">
<view class="flex justify-between mb-[40rpx]">
<view class="text-[#00D2E0]">
{{t('item.myFunds')}}
</view>
<view class="">
{{getCurrency() }}{{formatMoney(data.user.balance)}} {{getCurrency2()}}
</view>
</view>
<view class="">
<view class="bg-[#3d4277] rounded-lg px-[20rpx] py-[10rpx] mb-[30rpx]">
<u-input v-model="formData.money" type="number" @input="checkMoney"
:placeholder="t('item.moneyPlaceholder')" :border="false" />
</view>
<view class="text-right mb-[30rpx]" v-if="data.income > 0">
<text
class="mr-[10rpx]">{{t('item.expIncome')}}:</text>{{getCurrency() }}{{formatMoney(data.income)}} {{getCurrency2()}}
</view>
<u-button type="primary" :disabled="formData.money==''" @click="handleInvest()">
{{t('item.investBtn')}}
</u-button>
</view>
</view>
<view class="bg-[#2C326B] mx-[30rpx] mb-[20rpx] rounded-lg">
<view class="leading-loose rounded-lg p-[30rpx]">
<view class="" v-html="data.item.content"></view>
</view>
</view>
<u-keyboard class="max-w-[750px]" ref="uKeyboard1" mode="number" :dot-enabled="false" :tips="t('pwd.payPwd')"
confirm-text="" cancel-text="" v-model="show_keyboard" @change="keyboardChange" @backspace="backspace"
:show-input-val="true" :input-value="formData.pay_pwd"></u-keyboard>
</view>
</template>
<script lang="ts" setup>
import { reactive, ref, nextTick } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getDetail, invest } from '@/api/item'
import { t, formatMoney, getCurrency, getCurrency2,replaceStr } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('item.titleDetail') })
const show_keyboard = ref(false)
const data = reactive({
item: {
min_money: 0,
max_money: 0,
member_id: 0,
progress: 0,
point: 0
},
user: {
today_order: 0,
balance: 0,
member: {
id: 0,
item_num: 0,
item_add_rate: 0
}
},
income: 0
})
const formData = reactive({
id: 0,
money: '',
pay_pwd: '',
invest: 1//验证器标识
})
const handleInvest = async () => {
if (!formData.money) {
return uni.$u.toast(t('item.moneyEmpty'))
}
if (parseFloat(formData.money) < data.item.min_money || parseFloat(formData.money) > data.item.max_money) {
return uni.$u.toast(t('item.moneyError'))
}
if (parseFloat(formData.money) - data.user.balance > 0) {
return uni.$u.toast(t('common.InsufficientBalance'))
}
if (data.item.member_id - data.user.member.id > 0) {
return uni.$u.toast(replaceStr(t('item.limitVIpTips'), '{vip_name}', data.item.vip_name))
}
if (data.item.progress >= 100) {
return uni.$u.toast(t('item.progressFull'))
}
if (data.user.today_order - data.user.member.item_num >= 0) {
return uni.$u.toast(t('item.itemNumInsufficient'))
}
formData.pay_pwd = '';
show_keyboard.value = true;
}
const submit = async () => {
await invest(formData)
uni.redirectTo({
url: '/pages/item/record'
})
}
const keyboardChange = (val : string) => {
formData.pay_pwd += val;
if (formData.pay_pwd.length == 6) {
show_keyboard.value = false;
submit();
}
}
const backspace = () => {
// 删除value的最后一个字符
if (formData.pay_pwd.length) formData.pay_pwd = (formData.pay_pwd).substr(0, (formData.pay_pwd.length) - 1);
}
const getData = async (id : number) => {
formData.id = id;
const res = await getDetail({ id: id })
if (res.need_set_pwd == 1) {
uni.$u.toast(t('pwd.pwdSetFirstTips'))
uni.redirectTo({
url: '/pages/change_password/set_password_pay'
})
return
}
data.item = res.item;
data.user = res.user;
}
const checkMoney = (money : string) => {
var precision = uni.getStorageSync('precision')
// 使用正则表达式匹配小数
var decimalMatch = money.match(/\.(\d+)/);
if (decimalMatch) {
if (decimalMatch[1].length >= precision) {
nextTick(() => {
formData.money = parseFloat(money).toFixed(precision);
})
}
}
money = parseFloat(money).toFixed(precision);
var rate = parseFloat(data.item.rate);
if (data.user.member.item_add_rate > 0) rate += parseFloat(data.user.member.item_add_rate);
if (data.item.type == 1 || data.item.type == 4) {
data.income = parseFloat(money) * rate * data.item.cycle / 100;
} else {
data.income = parseFloat(money) * rate / 100;
}
data.income = parseFloat((data.income).toFixed(precision));
}
onLoad((options : any) => {
getData(options.id)
})
</script>
<style lang="scss">
</style>

135
src/pages/item/index.vue Normal file
View File

@@ -0,0 +1,135 @@
<template>
<view>
<u-navbar :is-back="false" :background="{backgroundColor: '#4E55AF'}" height="50">
<view class="flex justify-between items-center w-full">
<view class="flex justify-start items-center">
<view class="rounded-lg ml-[30rpx] bg-white p-[10rpx] mr-[12rpx]">
<u-image width="40rpx" height="40rpx" :src="appStore.getWebsiteConfig.shop_logo"></u-image>
</view>
<view class="">
{{t('item.titleIndex')}}
</view>
</view>
<view class="flex justify-end pr-[30rpx] ">
<navigator hover-class="none" url="/pages/item/record">
<u-icon size="40" name="/static/images/common/record.png"></u-icon>
</navigator>
</view>
</view>
</u-navbar>
<view class="bg-[#4E55AF] pt-[30rpx] mb-[30rpx] pb-[150rpx]">
<view class="vipbg mx-[30rpx] relative items-center flex">
<view class="text-white text-left mx-[30rpx]">{{t('robot.currentLevel')}}: {{data.user.member.name}}
</view>
<view class="absolute right-[30rpx] top-[-30rpx]">
<u-image width="200rpx" mode="widthFix" :src="data.user.member.logo"></u-image>
</view>
</view>
</view>
<view class="mb-[30rpx] mt-[-150rpx]">
<view class="bg-[#2C326B] mx-[30rpx] px-[30rpx] py-[40rpx] rounded-lg">
<view class="flex justify-between items-center">
<view class="w-1/2 text-center">
<view class="text-2xl text-[#A1DBF5] mb-[10rpx]">
{{formatMoney(data.user.totalMoney)}}
</view>
<view class="">
{{t('item.myItems')}}
</view>
</view>
<view class="">
|
</view>
<view class="w-1/2 text-center">
<view class="text-2xl text-[#A1DBF5] mb-[10rpx]">
{{formatMoney(data.user.totalIncome)}}
</view>
<view class="">
{{t('item.totalIncome')}}
</view>
</view>
</view>
</view>
</view>
<view class="mx-[30rpx]">
<view class="flex mb-[30rpx]">
<u-icon size="38" name="clock-fill"></u-icon>
<text class="ml-[6rpx]">{{date}}</text>
</view>
</view>
<view class="mx-[30rpx] mb-[20rpx]">
<view class="">
<w-item :items="data.items" />
</view>
</view>
</view>
<tabbar />
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { useAppStore } from '@/stores/app'
import { onUnload, onHide, onShow } from '@dcloudio/uni-app'
import { formatDate, formatMoney, t } from '@/utils/util'
import { getIndex } from '@/api/item'
uni.setNavigationBarTitle({ title: t('item.titleIndex') })
const appStore = useAppStore()
let timer : any = null
const date = ref('')
const data = reactive({
user: {
totalMoney: 0,
todayIncome: 0,
totalIncome: 0,
balance: 0,
member: []
},
items: [],
date: ''
})
const getTimer = async () => {
timer && clearInterval(timer)
timer = setInterval(() => {
let serverDateTime = new Date(data.date)
serverDateTime.setSeconds(serverDateTime.getSeconds() + 1)
date.value = formatDate(new Date(serverDateTime).getTime() / 1000, uni.getStorageSync('formatTime'));
data.date = date.value;
}, 1000)
}
const getData = async () => {
const res = await getIndex()
data.date = res.server_date
getTimer();
data.user = res.user;
data.items = res.items;
}
onUnload(() => {
timer && clearInterval(timer)
})
onShow(() => {
getData()
})
onHide(() => {
timer && clearInterval(timer)
})
</script>
<style lang="scss">
.vipbg {
background-image: url('/static/images/quantify/vipbg.png');
background-size: 100% auto;
background-repeat: no-repeat;
background-position: center;
height: 68px;
}
</style>

155
src/pages/item/record.vue Normal file
View File

@@ -0,0 +1,155 @@
<template>
<z-paging :refresher-enabled="false" :show-loading-more-when-reload="true" loading-more-default-text=""
loading-more-loading-text="" loading-more-fail-text="" :empty-view-text="t('common.noData')"
:show-loading-more-no-more-view="false" ref="paging" v-model="dataList" @query="queryList"
class="max-w-[750px]">
<h-dark :title="t('item.titleRecord')"></h-dark>
<view class="bg-[#2C326B] rounded-lg p-[30rpx] mt-[20rpx] mx-[30rpx]" v-for="item in dataList" :key="item.id">
<view class="">
<view class="font-bold mb-[30rpx]">
{{item.item_title}}
</view>
<view class="flex justify-between mb-[20rpx]">
<view class="text-[#00D2E0]">
{{t('item.money')}}
</view>
<view class="">
{{item.money}}
</view>
</view>
<view class="flex justify-between mb-[20rpx]">
<view class="text-[#00D2E0]">
{{t('item.cycle')}}
</view>
<view class="">
<text class="">{{ item.cycle }}</text>
</view>
</view>
<view class="flex justify-between mb-[20rpx]">
<view class="text-[#00D2E0]">
{{item.rateName}}
</view>
<view class="">
{{item.rate}}%
</view>
</view>
<view class="flex justify-between mb-[20rpx]">
<view class="text-[#00D2E0]">
{{t('item.toIncome')}}
</view>
<view class="">
{{item.total_income}}
</view>
</view>
<view class="flex justify-between mb-[20rpx]" v-if="item.point > 0">
<view class="text-[#00D2E0]">
{{t('item.point')}}
</view>
<view class="">
{{item.point}}
</view>
</view>
<view class="flex justify-between mb-[20rpx]">
<view class="text-[#00D2E0]">
{{t('item.status')}}
</view>
<view class="">
<view v-if="item.status==2" class="text-success">
{{tips.finish}}
</view>
<view v-if="item.status==1" class="">
{{tips.pending}}
</view>
</view>
</view>
<view class="flex justify-between mb-[20rpx]">
<view class="text-[#00D2E0]">
{{t('item.contractNo')}}
</view>
<view class="text-[#6EA2FF] font-bold underline">
<navigator hover-class="none" :url="'/pages/item/contract?id='+item.id">
{{item.contract_no}}
</navigator>
</view>
</view>
<view class="flex justify-between mb-[20rpx]">
<view class="text-[#00D2E0]">
{{t('item.timeInvest')}}
</view>
<view class="">
{{item.create_time}}
</view>
</view>
<view class="flex justify-between mb-[20rpx]">
<view class="text-[#00D2E0]">
{{t('item.timeExp')}}
</view>
<view class="">
{{item.end_time}}
</view>
</view>
</view>
</view>
</z-paging>
</template>
<script lang="ts" setup>
import { ref, reactive, shallowRef } from 'vue'
import { recordLists } from '@/api/item'
import { getCurrency, getCurrency2,formatMoney } from '@/utils/util'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('item.titleRecord') })
//为了提高渲染速度
const tips = reactive({
pending: t('tabs.pending'),
finish: t('tabs.finish'),
hour: t('item.hour'),
days: t('item.days'),
totalRate: t('item.totalRate'),
dailyRate: t('item.dailyRate'),
hourRate: t('item.hourRate')
})
const paging = shallowRef()
const dataList = ref<any[]>([])
const queryList = async (pageNo : number, pageSize : number) => {
try {
const data = await recordLists({
page_no: pageNo,
page_size: pageSize
})
Object.keys(data.lists).forEach(key => {
data.lists[key]['money'] = getCurrency() + formatMoney(data.lists[key]['money']) + ' ' + getCurrency2()
data.lists[key]['total_income'] = getCurrency() + formatMoney(data.lists[key]['total_income']) + ' ' + getCurrency2()
var type = data.lists[key]['type'];
var cycleTime = tips.hour;
var rateName = tips.totalRate;
switch (type) {
case 1:
cycleTime = tips.days;
rateName = tips.dailyRate;
break;
case 2:
cycleTime = tips.days;
break;
case 3:
break;
case 4:
rateName = tips.hourRate;
break;
}
data.lists[key]['cycle'] = data.lists[key]['cycle'] + ' ' + cycleTime;
data.lists[key]['rateName'] = rateName;
})
paging.value.complete(data.lists)
} catch (error) {
paging.value.complete(false)
}
}
</script>
<style lang="scss">
</style>

42
src/pages/kefu/index.vue Normal file
View File

@@ -0,0 +1,42 @@
<template>
<view>
<h-dark :title="t('kefu.title')"></h-dark>
<view class="mx-[30rpx] mt-[20rpx]">
<view v-for="(item, index) in data.list" :key="index"
class="flex items-center bg-[#2C326B] rounded-lg p-[30rpx] mb-[20rpx]" @click="handleClick(item.link)">
<u-image width="64" height="64" :src="item.logo" alt="" />
<view class="ml-[20rpx] flex-1">{{ item.name }}</view>
<view class="text-muted">
<u-icon name="arrow-right" />
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { onLoad } from '@dcloudio/uni-app'
import { reactive } from 'vue'
import { getKefuLists } from '@/api/kefu'
import { jumpTo, t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('kefu.title') })
const data = reactive({
list: []
})
const handleClick = (link : any) => {
jumpTo(link)
}
const getData = async () => {
const res = await getKefuLists()
data.list = res;
}
onLoad(() => {
getData()
})
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,88 @@
<template>
<view>
<h-dark :title="t('language.language')" backgroundColor="#1B1F45"></h-dark>
<view class="mx-[120rpx] my-[30rpx]">
<u-image width="100%" mode="widthFix" src="/static/images/common/country.png" alt="" />
</view>
<view class="py-[20rpx] mx-[30rpx]">
<view class="flex justify-around px-[40rpx] py-[35rpx] bg-[#2C326B] rounded-lg mb-[20rpx]" v-for="(item, index) in data.list" @click="langChange(item)">
<view class="w-4/5">
{{item.name}}
</view>
<view class="w-1/5 text-right">
<u-icon v-if="!loading && item.symbol==data.lang_name" name="checkbox-mark"></u-icon>
<u-loading v-if="loading && item.symbol==data.lang_name" mode="flower"></u-loading>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { onLoad } from '@dcloudio/uni-app'
import { reactive, ref } from 'vue'
import { getLangAll, getLangPag } from '@/api/lang'
import { t } from '@/utils/util'
uni.setNavigationBarTitle({ title: t('language.language') })
const loading = ref(false)
const data = reactive({
lang_name: uni.getStorageSync('lang'),
list: []
})
const langChange = (item : any) => {
loading.value = true;
data.lang_name = item.symbol;
uni.setStorageSync('lang', item.symbol)
uni.setStorageSync('formatTime', item.time_format)
getPags()
const current = getCurrentPages();
let prevPageRoute = '';
if (current.length >= 2) {
let prevPage = current[current.length - 2];
prevPageRoute = "/" + prevPage.route; // 上一个页面的路由
}
setTimeout(() => {
/*#ifdef H5*/
if (current && current.length > 1) {
uni.reLaunch({
url: prevPageRoute// 替换为你的页面路径
});
} else {
history.back();
}
/*#endif*/
/*#ifdef APP-PLUS*/
uni.reLaunch({
url: prevPageRoute// 替换为你的页面路径
});
/*#endif*/
}, 3000);
}
const getPags = async () => {
const pag = await getLangPag({ 'lang': uni.getStorageSync('lang') })
pag.forEach((item : any) => {
//先销毁再重新赋值
uni.removeStorageSync(item.type + '.' + item.name);
uni.setStorageSync(item.type + '.' + item.name, item.value);
});
}
const getData = async () => {
const apiData = await getLangAll()
data.list = apiData;
}
onLoad(() => {
getData()
})
</script>
<style lang="scss">
</style>

Some files were not shown because too many files have changed in this diff Show More