20912bef by 华明祺

feat(personal): 实现个人中心页面及绑定/解绑学员功能

- 新增个人中心页面,包含会员卡展示、用户信息、功能入口
- 实现绑定学员弹框,支持输入姓名和证件号进行绑定
- 添加绑定状态判断,已绑定时按钮切换为解绑
- 实现解绑功能,调用unbindUser接口
- 新增退出登录按钮
- 添加bindUser和unbindUser API接口
1 parent dc453c5f
<script>
import config from '@/config.js';
import {
getInfo
getInfo,
getWebInfo
} from '@/common/login.js';
import * as api from '@/common/api.js';
import config from '@/config.js';
let loginUrl = ['login/login', 'login/register', 'personal/addVip_per']
let firstload = false
export default {
globalData: {
isLogin: false,
baseUrl_api: '',
user: null,
userType: '',
userInfo: null,
deptType: '',
genFlag: '',
authenticationStatus: '',
memberInfo: null,
isExam: false
user: null
},
onLaunch: function(options) {
console.log('App Launch', options);
this.globalData.baseUrl_api = config.baseUrl_api;
if (loginUrl.indexOf(options.path) == -1) {
let userName = uni.getStorageSync('userName')
if (userName) {
getInfo().then(() => {
this.globalData.isLogin = true;
firstload = true
let firstLoadCallback = getApp().firstLoadCallback;
if (firstLoadCallback) {
firstLoadCallback();
}
}).catch(() => {
firstload = true
uni.redirectTo({
url: '/login/login'
})
let userName = uni.getStorageSync('userName')
if (userName) {
getInfo().then(() => {
this.globalData.isLogin = true
uni.reLaunch({
url: '/pages/index/home'
})
}).catch(() => {
uni.reLaunch({
url: '/login/login'
})
} else {
this.globalData.isLogin = false;
firstload = true
uni.redirectTo({
})
return
}
let webUserName = uni.getStorageSync('webUserName')
if (webUserName) {
getWebInfo().then(() => {
uni.reLaunch({
url: '/personal/home'
})
}).catch(() => {
uni.reLaunch({
url: '/login/login'
})
}
})
return
}
this.globalData.isLogin = false
uni.reLaunch({
url: '/login/login'
})
},
onShow: function() {
console.log('App Show', firstload, this.globalData.isLogin);
// if (firstload && !this.globalData.isLogin) {
// uni.redirectTo({
// url: '/login/login'
// })
// }
console.log('App Show');
},
onHide: function() {
console.log('App Hide');
......
import request from './request.js'
import config from '@/config.js'
import * as loginServer from '@/common/login.js';
// 激活
export function active(data) {
......@@ -1415,10 +1414,34 @@ export function createMemberPayRange(data) {
})
}
// 获取最近认证记录
export function getMyRecent() {
return request({
url: '/system/certifiedNew/getMyRecent',
method: 'get'
})
}
// 获取订单详情
export function getOrderInfo(orderId) {
return request({
url: `/common/order/${orderId}`,
method: 'get'
})
}
// 绑定学员
export function bindUser(data) {
return request({
url: `/person/info/bindUser`,
method: 'post',
params: data
})
}
export function unbindUser() {
return request({
url: `/person/info/unbindUser`,
method: 'post'
})
}
\ No newline at end of file
......
import {
useUserStore
} from '../store/modules/user'
import request from './request'
import * as api from '@/common/api.js'
function pcLogin(data) {
return request({
url: '/login',
method: 'post',
params: data
}).then((res) => {
uni.setStorageSync('token', 'Bearer ' + res.data.token)
}).then(getInfo)
return request({
url: '/login',
method: 'post',
params: data
}).then((res) => {
uni.setStorageSync('token', 'Bearer ' + res.data.token)
}).then(getInfo)
}
function h5Login(userName) {
return request({
url: `/h5Login`,
method: 'post',
params: {
username: userName
}
}).then((res) => {
uni.setStorageSync('token', 'Bearer ' + res.data.token)
}).then(getInfo)
return request({
url: `/h5Login`,
method: 'post',
params: {
username: userName
}
}).then((res) => {
uni.setStorageSync('token', 'Bearer ' + res.data.token)
}).then(getInfo)
}
function h5LoginAuto() {
const userName = uni.getStorageSync('userName')
if (userName) {
return h5Login(userName)
} else {
uni.redirectTo({
url: '/login/login'
})
}
const userName = uni.getStorageSync('userName')
if (userName) {
return h5Login(userName)
} else {
uni.redirectTo({
url: '/login/login'
})
}
}
function logout() {
return request({
url: '/logout',
method: 'post'
}).then(() => {
uni.removeStorageSync('token')
uni.removeStorageSync('userName')
})
return request({
url: '/logout',
method: 'post'
}).then(() => {
const userStore = useUserStore()
const app = getApp()
uni.removeStorageSync('token')
uni.removeStorageSync('userName')
uni.removeStorageSync('webUserName')
userStore.setUser(null)
app.globalData.isLogin = false
})
}
function getCodeImg() {
return request({
url: '/captchaImage',
method: 'get'
})
return request({
url: '/captchaImage',
method: 'get'
})
}
// 代退图形认证的获取手机验证码
function getSmsCode(data) {
return request({
// url: '/captchaSmsWithCaptchaImage',
url: '/captchaSmsWithCaptchaImageForMiniApp',
method: 'post',
params: data
})
return request({
// url: '/captchaSmsWithCaptchaImage',
url: '/captchaSmsWithCaptchaImageForMiniApp',
method: 'post',
params: data
})
}
function loginByPhone(phonenumber, code) {
const data = {
phonenumber,
code
}
return request({
url: '/userLoginByPhone',
method: 'post',
params: data
}).then((res) => {
uni.showToast({
title: res.msg,
icon: 'none'
})
uni.setStorageSync('token', 'Bearer ' + res.data.token)
}).then(getInfo)
const data = {
phonenumber,
code
}
return request({
url: '/userLoginByPhone',
method: 'post',
params: data
}).then((res) => {
uni.showToast({
title: res.msg,
icon: 'none'
})
uni.setStorageSync('token', 'Bearer ' + res.data.token)
}).then(getInfo)
}
// 获取用户详细信息
function getInfo() {
return request({
url: '/getInfo',
method: 'get'
}).then(res => {
const app = getApp()
const user = res.data.user
// const personInfo = res.data.personInfo
uni.setStorageSync('userName', user.userName)
// uni.setStorageSync('perId', personInfo.perId||-1)
app.globalData.user = res.data.user
app.globalData.deptType = user.dept.deptType
app.globalData.genFlag = user.dept.genFlag
// user.dept.deptType = '3'
switch (user.dept.deptType) {
case '1': // 中跆协
app.globalData.userType = '1'
break
case '2': // 省
case '3':
app.globalData.userType = '2'
break
case '6': // 道馆
app.globalData.userType = '4'
break
default: // 市、区
app.globalData.userType = '3'
break
}
app.globalData.userInfo = user
})
return request({
url: '/getInfo',
method: 'get'
}).then(res => {
const userStore = useUserStore()
const app = getApp()
const user = res.data.user
uni.setStorageSync('userName', user.userName)
uni.removeStorageSync('webUserName')
userStore.setUser(user)
app.globalData.deptType = user.dept.deptType
app.globalData.genFlag = user.dept.genFlag
app.globalData.changePassFlag = user.changePassFlag
switch (user.dept.deptType) {
case '1': // 中跆协
app.globalData.userType = '1'
break
case '2': // 省
case '3':
app.globalData.userType = '2'
break
case '6': // 道馆
app.globalData.userType = '4'
break
default: // 市、区
app.globalData.userType = '3'
break
}
})
}
function getWebInfo() {
return request({
url: '/person/info/getInfo',
method: 'get'
}).then(res => {
const userStore = useUserStore()
const user = res.data.user
delete res.data.user
const perInfo = res.data
uni.setStorageSync('webUserName', user.userName)
uni.removeStorageSync('userName')
userStore.setUser(user)
userStore.setPerInfo(perInfo)
})
}
// 团队会员用户注册接口
function groupMemberRegister(data) {
return request({
url: '/groupMemberRegister',
method: 'post',
params: data
})
return request({
url: '/groupMemberRegister',
method: 'post',
params: data
})
}
// 获取道馆信息
function getMyOwnMemberInfo() {
return request({
url: '/member/info/getMyOwnMemberInfo',
method: 'get'
}).then(res => {
const app = getApp()
app.globalData.authenticationStatus = res.data.authenticationStatus
app.globalData.memberInfo = res.data.memberInfo
app.globalData.isExam = res.data?.memberInfo?.isPoints
})
return request({
url: '/member/info/getMyOwnMemberInfo',
method: 'get'
}).then(res => {
const app = getApp()
app.globalData.authenticationStatus = res.data.authenticationStatus
app.globalData.memberInfo = res.data.memberInfo
app.globalData.isExam = res.data?.memberInfo?.isPoints
})
}
function wxLogin() {
return new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success: (res) => {
resolve(res)
},
fail: (res) => {
uni.showToast({
title: '获取用户信息失败',
icon: 'none',
duration: 2000
})
reject(res)
}
})
}).then(res => {
return pcLoginByCode(res.code)
})
}
function pcLoginByCode(code) {
return request({
url: `/loginByJsCode?jsCode=${code}`,
method: "POST"
}).then((res) => {
uni.setStorageSync('token', 'Bearer ' + res.data);
}).then(getWebInfo)
}
export {
pcLogin,
getCodeImg,
getSmsCode,
h5Login,
h5LoginAuto,
loginByPhone,
groupMemberRegister,
getMyOwnMemberInfo,
logout,
getInfo
}
pcLogin,
getCodeImg,
getSmsCode,
h5Login,
h5LoginAuto,
loginByPhone,
groupMemberRegister,
getMyOwnMemberInfo,
logout,
getInfo,
getWebInfo,
wxLogin
}
\ No newline at end of file
......
<template>
<view>
<view class="wBox">
<view class="tt">基本信息</view>
<view class="ddd">
<text class="lab">结算编号:</text>{{ form.flowCode }}
</view>
<view class="ddd">
<text class="lab">{{ type=='1'?'考级名称':'考段名称' }}</text>{{form.mergeName}}
</view>
<view class="ddd">
<text class="lab">申请单位:</text>{{ form.memName }}
</view>
<view class="ddd" v-if="form.mergeTime">
<text class="lab">申请日期:</text>{{form.mergeTime?.slice(0,10)}}
</view>
<view class="ddd">
<text class="lab">{{ type=='1'?'考级人数':'考段人数' }}</text>{{form.totalNum}}
</view>
<view class="ddd">
<text class="lab">总金额:</text>¥{{ (form.totalAmount*1).toFixed(2) }}
</view>
</view>
<view class="wBox">
<view class="tt">
考试信息
</view>
<view class="userlist">
<view class="item" v-for="(n,index) in infoList" :key="index" @click="goDetail(n)" style="background-color: #fffafa;">
<view class="w100">
<view class="text-primary">{{n.examCode}}</view>
<view class="name">{{n.name}}</view>
<!-- <view class="date">{{n.idcTypeStr}}{{n.idcCode}}</view> -->
<view class="flexbox">
<view>
上报单位
<text>{{n.memberName}}</text>
</view>
<view>
{{type=='1'?'考级考生数':'考段考生数'}}
<text>
{{n.totalNum}}
</text>
</view>
<view>
金额
<text class="text-danger">¥{{ (n.totalAmount*1).toFixed(2) }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import * as api from '@/common/api.js'
import config from '@/config.js'
import _ from 'underscore'
import {
onMounted,
ref
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app'
const app = getApp();
const queryParams = ref({
recordId: ''
})
const form = ref({})
const list = ref([])
const infoList = ref([])
const deptType = ref()
const type = ref(null)
let rangeId = ''
onLoad((option) => {
if ('form' in option) {
form.value = JSON.parse(decodeURIComponent(option.form))
console.log(111,form.value)
}
type.value = option.type
// console.log(222,form.value)
if (app.globalData.isLogin) {
init()
} else {
app.firstLoadCallback = () => {
init()
};
}
})
function init() {
deptType.value = app.globalData.deptType
getForm()
}
function getForm() {
uni.showLoading({
title: '加载中'
})
api.getMergePaymentInfo(form.value.recordId).then(res => {
_.each(res.rows, (r) => {
const item = JSON.parse(r.content)
item.recordId = r.recordId
infoList.value.push(item)
})
console.log(infoList.value)
form.value.totalNum = Math.floor(_.sumBy(infoList.value, (o) => parseFloat(o.totalNum || 0)))
form.value.totalAmount = Math.floor(_.sumBy(infoList.value, (o) => parseFloat(o.totalAmount || 0)))
uni.hideLoading()
})
}
function goDetail(item){
const form = encodeURIComponent(JSON.stringify(item))
let path = `/level/applyDetail?examId=${item.examId}&form=${form}`
uni.navigateTo({
url: path
});
}
</script>
<style scoped lang="scss">
.wBox {
width: 700rpx;
padding: 30rpx;
margin: 20rpx auto;
background: #FFFFFF;
box-shadow: 0rpx 12rpx 116rpx 0rpx rgba(196, 203, 214, 0.1);
border-radius: 15rpx;
.tt {
color: #0A1629;margin: 0 0 30rpx;
font-size: 30rpx;
}
.ddd{font-size: 28rpx;color: #333; margin: 0 0 10rpx;
.lab{color: #999;display: inline-block;text-align: justify;
text{word-break: break-all;}
}
}
}
<template>
<view>
<view class="wBox">
<view class="tt">基本信息</view>
<view class="ddd">
<text class="lab">结算编号:</text>{{ form.flowCode }}
</view>
<view class="ddd">
<text class="lab">{{ type=='1'?'考级名称':'考段名称' }}</text>{{form.mergeName}}
</view>
<view class="ddd">
<text class="lab">申请单位:</text>{{ form.memName }}
</view>
<view class="ddd" v-if="form.mergeTime">
<text class="lab">申请日期:</text>{{form.mergeTime?.slice(0,10)}}
</view>
<view class="ddd">
<text class="lab">{{ type=='1'?'考级人数':'考段人数' }}</text>{{form.totalNum}}
</view>
<view class="ddd">
<text class="lab">总金额:</text>¥{{ (form.totalAmount*1).toFixed(2) }}
</view>
</view>
<view class="wBox">
<view class="tt">
考试信息
</view>
<view class="userlist">
<view class="item" v-for="(n,index) in infoList" :key="index" @click="goDetail(n)"
style="background-color: #fffafa;">
<view class="w100">
<view class="text-primary">{{n.examCode}}</view>
<view class="name">{{n.name}}</view>
<!-- <view class="date">{{n.idcTypeStr}}{{n.idcCode}}</view> -->
<view class="flexbox">
<view>
上报单位
<text>{{n.memberName}}</text>
</view>
<view>
{{type=='1'?'考级考生数':'考段考生数'}}
<text>
{{n.totalNum}}
</text>
</view>
<view>
金额
<text class="text-danger">¥{{ (n.totalAmount*1).toFixed(2) }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import * as api from '@/common/api.js'
import config from '@/config.js'
import _ from 'underscore'
import {
onMounted,
ref
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app'
const app = getApp();
const queryParams = ref({
recordId: ''
})
const form = ref({})
const list = ref([])
const infoList = ref([])
const deptType = ref()
const type = ref(null)
let rangeId = ''
onLoad((option) => {
if ('form' in option) {
form.value = JSON.parse(decodeURIComponent(option.form))
}
type.value = option.type
// console.log(222,form.value)
if (app.globalData.isLogin) {
init()
} else {
app.firstLoadCallback = () => {
init()
};
}
})
function init() {
deptType.value = app.globalData.deptType
getForm()
}
function getForm() {
uni.showLoading({
title: '加载中'
})
api.getMergePaymentInfo(form.value.recordId).then(res => {
_.each(res.rows, (r) => {
const item = JSON.parse(r.content)
item.recordId = r.recordId
infoList.value.push(item)
})
console.log(infoList.value)
form.value.totalNum = Math.floor(_.sumBy(infoList.value, (o) => parseFloat(o.totalNum || 0)))
form.value.totalAmount = Math.floor(_.sumBy(infoList.value, (o) => parseFloat(o.totalAmount || 0)))
uni.hideLoading()
})
}
function goDetail(item) {
const form = encodeURIComponent(JSON.stringify(item))
let path = `/level/applyDetail?examId=${item.examId}&form=${form}`
uni.navigateTo({
url: path
});
}
</script>
<style scoped lang="scss">
.wBox {
width: 700rpx;
padding: 30rpx;
margin: 20rpx auto;
background: #FFFFFF;
box-shadow: 0rpx 12rpx 116rpx 0rpx rgba(196, 203, 214, 0.1);
border-radius: 15rpx;
.tt {
color: #0A1629;
margin: 0 0 30rpx;
font-size: 30rpx;
}
.ddd {
font-size: 28rpx;
color: #333;
margin: 0 0 10rpx;
.lab {
color: #999;
display: inline-block;
text-align: justify;
text {
word-break: break-all;
}
}
}
}
</style>
\ No newline at end of file
......
This diff could not be displayed because it is too large.
import App from './App'
// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import {
createSSRApp
} from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
// #endif
import App from './App'
import store from './store'
// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.use(store)
app.$mount()
// #endif
// #ifdef VUE3
import {
createSSRApp
} from 'vue'
export function createApp() {
const app = createSSRApp(App)
app.use(store)
return {
app
}
}
// #endif
\ No newline at end of file
......
......@@ -129,10 +129,10 @@
if (orderErr) {
uni.hideLoading()
isPaying.value = false
uni.showToast({
title: '创建订单失败',
icon: 'none'
})
// uni.showToast({
// title: '创建订单失败',
// icon: 'none'
// })
return
}
......@@ -141,7 +141,7 @@
if (data.payFlag == 0) {
uni.hideLoading()
isPaying.value = false
uni.navigateTo({
uni.redirectTo({
url: `/myCenter/sucPay?orderId=${data.orderId}`
})
return
......@@ -153,7 +153,7 @@
uni.hideLoading()
isPaying.value = false
uni.navigateTo({
uni.redirectTo({
url: `/myCenter/sucPay?orderId=${data.orderId}`
})
}
......
<template>
<view>
<view class="wBox">
<view class="tt">
审核信息
</view>
<view>
<view class="stepItem" v-for="(n,index) in recordList" :key="index">
<view class="time">{{n.auditTime||'待审批'}}</view>
<view class="content">
<view class="status">
<text v-if="n.auditResult==0"> 审核中</text>
<text v-if="n.auditResult==1" class="text-success">审核通过</text>
<text v-if="n.auditResult==2" class="text-danger"> 审核拒绝</text>
<text v-if="n.auditResult==3" class="text-warning"> 已撤回</text>
</view>
<!-- <view class="name">{{index+1}}</view> -->
<view class="deptName">{{n.auditDeptName}}</view>
<view v-if="n.auditStatus==2">
备注:{{n.auditMsg||'/' }}
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import * as api from '@/common/api.js'
import config from '@/config.js'
import _ from 'underscore'
import {
onMounted,
ref
} from 'vue'
import {
onLoad,
onShow
} from '@dcloudio/uni-app'
const app = getApp();
const userType = ref('')
const recordList = ref([])
onLoad((option) => {
getMyCertStageFN()
})
function getMyCertStageFN() {
api.getMyCertStage().then(res => {
recordList.value = res.data
console.log(res)
})
}
</script>
<style scoped lang="scss">
.wBox {
width: 700rpx;
padding: 30rpx;
margin: 20rpx auto;
background: #FFFFFF;
box-shadow: 0rpx 12rpx 116rpx 0rpx rgba(196, 203, 214, 0.1);
border-radius: 15rpx;
.tt {
color: #0A1629;
margin: 0 0 30rpx;
font-size: 30rpx;
}
.ddd {
font-size: 28rpx;
color: #333;
margin: 0 0 10rpx;
.lab {
color: #999;
display: inline-block;
text-align: justify;
}
}
}
<template>
<view>
<view class="wBox">
<view class="tt">
审核信息
</view>
<view>
<view class="stepItem" v-for="(n,index) in recordList" :key="index">
<view class="time">{{n.auditTime||'待审批'}}</view>
<view class="content">
<view class="status">
<text v-if="n.auditResult==1" class="text-success">审核通过</text>
<text v-if="n.auditResult==0" class="text-danger"> 审核拒绝</text>
</view>
<!-- <view class="name">{{index+1}}</view> -->
<view class="deptName">{{n.auditDeptName}}</view>
<view v-if="n.auditResult==0">
备注:{{n.auditMsg||'/' }}
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import * as api from '@/common/api.js'
import config from '@/config.js'
import _ from 'underscore'
import to from 'await-to-js'
import {
onMounted,
ref
} from 'vue'
import {
onLoad,
onShow
} from '@dcloudio/uni-app'
const app = getApp();
const userType = ref('')
const recordList = ref([])
onLoad(async (option) => {
await getMyRecentFN()
})
async function getMyRecentFN() {
const [err, res] = await to(api.getMyRecent())
if (!err && res.data && res.data.auditLogs) {
recordList.value = JSON.parse(res.data.auditLogs)
}
}
</script>
<style scoped lang="scss">
.wBox {
width: 700rpx;
padding: 30rpx;
margin: 20rpx auto;
background: #FFFFFF;
box-shadow: 0rpx 12rpx 116rpx 0rpx rgba(196, 203, 214, 0.1);
border-radius: 15rpx;
.tt {
color: #0A1629;
margin: 0 0 30rpx;
font-size: 30rpx;
}
.ddd {
font-size: 28rpx;
color: #333;
margin: 0 0 10rpx;
.lab {
color: #999;
display: inline-block;
text-align: justify;
}
}
}
</style>
\ No newline at end of file
......
......@@ -4,6 +4,7 @@
"crypto-js": "^4.1.1",
"dayjs": "^1.11.6",
"lodash": "^4.17.21",
"pinia": "^3.0.4",
"underscore": "^1.13.6"
},
"devDependencies": {
......
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
<template>
<view class="mainbox">
<view class="title">{{form.name}}</view>
<view class="infos">
<text>{{ form.source }}</text>
<text>{{ form.belongTime }}</text>
</view>
<view class="content">
<view v-html="form.content"></view>
<view v-if="attachmentMp4.length>0">
<video v-for="(f,index) in attachmentMp4" :key="index" controls :src="config.baseUrl_api + f.url"></video>
</view>
<view v-if="attachmentFile.length>0" class="mt20">
<!-- 附件-->
<view class="fwb mt20">附件下载:</view>
<view v-for="(f,index) in attachmentFile" :key="index" class="text-primary underLine"
@click="downLoad(f.url)">
{{ index + 1 }}{{ f.name }}
</view>
</view>
<view>
<text v-if=" form.author">发布人:{{ form.author }}</text>
</view>
</view>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
import * as api from '@/common/api.js';
import {
onLoad
} from '@dcloudio/uni-app';
import _ from 'underscore'
import config from '@/config.js'
const form = ref({})
const attachmentFile = ref([])
const attachmentMp4 = ref([])
onLoad((option) => {
getData(option.noteId)
})
function getData(noteId) {
api.getNewsById(noteId).then(res => {
form.value = res.data
var html = res.data.content.replace(/<img([\s\w"-=\/\.:;]+)((?:(height="[^"]+")))/ig, '<img$1')
.replace(/<img([\s\w"-=\/\.:;]+)((?:(width="[^"]+")))/ig, '<img$1')
.replace(/<img([\s\w"-=\/\.:;]+)((?:(style="[^"]+")))/ig, '<img$1')
.replace(/<img([\s\w"-=\/\.:;]+)((?:(alt="[^"]+")))/ig, '<img$1')
.replace(/<img([\s\w"-=\/\.:;]+)/ig, '<img style="width: 100%;" $1');
if (form.value.attacthJson) {
const attachment = JSON.parse(form.value.attacthJson)
attachmentFile.value = _.filter(attachment, (a) => a.url.toLowerCase().indexOf('.mp4') === -1) || []
attachmentMp4.value = _.filter(attachment, (a) => a.url.toLowerCase().indexOf('.mp4') !== -1) || []
}
})
}
function downLoad(url){
console.log(url)
var str = config.baseUrl_api + url
if (url.indexOf('png') > -1 ||url.indexOf('jpg') > -1 ||url.indexOf('jpeg') > -1) {
uni.previewImage({
urls: [str],
success: function(res) {
console.log('success', res)
},
fail: function(res) {
console.log('fail', res)
},
complete: function(res) {
console.log('complete', res)
}
})
} else {
goWebView(str)
}
}
function goWebView(url) {
url = url.replace("http://", "https://")
uni.showLoading({
title: '下载中'
});
uni.downloadFile({
url: url,
success: function(res) {
console.log('111')
uni.hideLoading();
var filePath = res.tempFilePath;
uni.showLoading({
title: '正在打开'
});
uni.openDocument({
filePath: filePath,
showMenu: true,
success: function(res) {
console.log('222')
uni.hideLoading();
},
fail: function(err) {
console.log(err.errMsg)
uni.hideLoading();
let msg
if(err.errMsg.indexOf('not supported')>-1){
msg = '不支持该文件类型'
} else {
msg = err.errMsg
}
uni.showToast({
title: msg,
icon: 'none',
duration: 2000
});
}
});
},
fail: function(error) {
uni.hideLoading();
uni.showToast({
title: `下载失败`,
icon: 'none',
duration: 2000
});
}
});
}
</script>
<style scoped lang="scss">
.mainbox {
box-sizing: border-box;
padding: 50rpx 25rpx 160rpx;
background: #fff;
min-height: 100vh;
}
.title {
font-size: 36rpx;
font-weight: 500;
color: #29343C;
margin-bottom: 34rpx;
}
.infos {
border-bottom: 1px solid #DCDCDC;
padding-bottom: 40rpx;
overflow: hidden;
}
.infos>text {
margin-right: 18rpx;
color: #7B7F83;
font-size: 22rpx;
}
.content {
line-height: 1.6;
color: #4C5359;
font-size: 30rpx;
padding-top: 40rpx;
width: 100%;
word-wrap: break-word !important;
white-space: normal !important;
}
.content rich-text {
word-wrap: break-word !important;
white-space: normal !important;
}
.content span,
.content p {
word-wrap: break-word !important;
white-space: normal !important;
}
.content rich-text img{max-width: 100%;}
image {
max-width: 100%;
}
audio {
width: 100%;
}
video {
width: 100%;
}
<template>
<view class="mainbox">
<view class="title">{{form.name}}</view>
<view class="infos">
<text>{{ form.source }}</text>
<text>{{ form.belongTime }}</text>
</view>
<view class="content">
<view v-html="form.content"></view>
<view v-if="attachmentMp4.length>0">
<video v-for="(f,index) in attachmentMp4" :key="index" controls
:src="config.baseUrl_api + f.url"></video>
</view>
<view v-if="attachmentFile.length>0" class="mt20">
<!-- 附件-->
<view class="fwb mt20">附件下载:</view>
<view v-for="(f,index) in attachmentFile" :key="index" class="text-primary underLine"
@click="downLoad(f.url)">
{{ index + 1 }}{{ f.name }}
</view>
</view>
<view>
<text v-if=" form.author">发布人:{{ form.author }}</text>
</view>
</view>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
import * as api from '@/common/api.js';
import {
onLoad
} from '@dcloudio/uni-app';
import _ from 'underscore'
import config from '@/config.js'
const form = ref({})
const attachmentFile = ref([])
const attachmentMp4 = ref([])
onLoad((option) => {
getData(option.noteId)
})
function getData(noteId) {
api.getNewsById(noteId).then(res => {
form.value = res.data
var html = res.data.content.replace(/<img([\s\w"-=\/\.:;]+)((?:(height="[^"]+")))/ig, '<img$1')
.replace(/<img([\s\w"-=\/\.:;]+)((?:(width="[^"]+")))/ig, '<img$1')
.replace(/<img([\s\w"-=\/\.:;]+)((?:(style="[^"]+")))/ig, '<img$1')
.replace(/<img([\s\w"-=\/\.:;]+)((?:(alt="[^"]+")))/ig, '<img$1')
.replace(/<img([\s\w"-=\/\.:;]+)/ig, '<img style="width: 100%;" $1');
if (form.value.attacthJson) {
const attachment = JSON.parse(form.value.attacthJson)
attachmentFile.value = _.filter(attachment, (a) => a.url.toLowerCase().indexOf('.mp4') === -1) ||
[]
attachmentMp4.value = _.filter(attachment, (a) => a.url.toLowerCase().indexOf('.mp4') !== -1) || []
}
})
}
function downLoad(url) {
console.log(url)
var str = config.baseUrl_api + url
if (url.indexOf('png') > -1 || url.indexOf('jpg') > -1 || url.indexOf('jpeg') > -1) {
uni.previewImage({
urls: [str],
success: function(res) {
console.log('success', res)
},
fail: function(res) {
console.log('fail', res)
},
complete: function(res) {
console.log('complete', res)
}
})
} else {
goWebView(str)
}
}
function goWebView(url) {
url = url.replace("http://", "https://")
uni.showLoading({
title: '下载中'
});
uni.downloadFile({
url: url,
success: function(res) {
uni.hideLoading();
var filePath = res.tempFilePath;
uni.showLoading({
title: '正在打开'
});
uni.openDocument({
filePath: filePath,
showMenu: true,
success: function(res) {
uni.hideLoading();
},
fail: function(err) {
console.log(err.errMsg)
uni.hideLoading();
let msg
if (err.errMsg.indexOf('not supported') > -1) {
msg = '不支持该文件类型'
} else {
msg = err.errMsg
}
uni.showToast({
title: msg,
icon: 'none',
duration: 2000
});
}
});
},
fail: function(error) {
uni.hideLoading();
uni.showToast({
title: `下载失败`,
icon: 'none',
duration: 2000
});
}
});
}
</script>
<style scoped lang="scss">
.mainbox {
box-sizing: border-box;
padding: 50rpx 25rpx 160rpx;
background: #fff;
min-height: 100vh;
}
.title {
font-size: 36rpx;
font-weight: 500;
color: #29343C;
margin-bottom: 34rpx;
}
.infos {
border-bottom: 1px solid #DCDCDC;
padding-bottom: 40rpx;
overflow: hidden;
}
.infos>text {
margin-right: 18rpx;
color: #7B7F83;
font-size: 22rpx;
}
.content {
line-height: 1.6;
color: #4C5359;
font-size: 30rpx;
padding-top: 40rpx;
width: 100%;
word-wrap: break-word !important;
white-space: normal !important;
}
.content rich-text {
word-wrap: break-word !important;
white-space: normal !important;
}
.content span,
.content p {
word-wrap: break-word !important;
white-space: normal !important;
}
.content rich-text img {
max-width: 100%;
}
image {
max-width: 100%;
}
audio {
width: 100%;
}
video {
width: 100%;
}
</style>
\ No newline at end of file
......
......@@ -138,10 +138,10 @@
if (orderErr) {
uni.hideLoading()
isPaying.value = false
uni.showToast({
title: '创建订单失败',
icon: 'none'
})
// uni.showToast({
// title: '创建订单失败',
// icon: 'none'
// })
return
}
......@@ -161,7 +161,7 @@
isPaying.value = false
// 支付成功,跳转页面
uni.navigateTo({
uni.redirectTo({
url: `/personal/sucPay?orderId=${orderRes.data.orderId}`
})
}
......
<template>
<view class="pay-order-container">
<view class="order-info">
<view class="info-item total-price">
<text class="label">支付总费用:</text>
<text class="value red">{{ price ?? 0 }}</text>
</view>
</view>
<view class="pay-type-section">
<text class="section-title">选择支付方式</text>
<radio-group :value="payType" @change="handlePayTypeChange">
<label class="radio-item">
<radio value="0" color="#E60012" :checked="payType === '0'" />
<view class="pay-method">
<image class="icon" src="/static/min.png" mode="widthFix"></image>
<text class="pay-name">民生付</text>
</view>
</label>
</radio-group>
</view>
<!-- 底部支付按钮 -->
<view class="fixed-bottom">
<button class="pay-btn red-bg" :loading="payLoading" @click="handlePay">立即支付</button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app';
import * as api from '@/common/api.js'
// 核心数据
const formData = ref({}) // 订单统计数据
const price = ref('') // 核心业务ID
const payType = ref('0') // 支付方式(默认0=民生付)
const payLoading = ref(false) // 支付按钮加载状态
// 页面加载接收参数
onLoad(async (options) => {
console.log('订单ID:', options.price)
if (options.price) {
price.value = options.price
}
})
// 支付方式切换
function handlePayTypeChange(e) {
payType.value = e.detail.value
}
// 立即支付核心逻辑
async function handlePay() {
try {
payLoading.value = true
const res = await api.goPay()
if (res.data?.orderId) {
api.pcallBack2(res.data.orderId)
uni.navigateTo({
url: `/personal/sucPay`
})
}
// 跳转到支付成功页
} catch (err) {
const errMsg = err?.data?.msg || err?.message || '支付失败,请稍后重试'
uni.showToast({ title: errMsg, icon: 'none' })
} finally {
payLoading.value = false
}
}
</script>
<style scoped lang="scss">
.pay-order-container {
padding: 30rpx;
background-color: #fff;
min-height: 100vh;
box-sizing: border-box;
}
.icon{
width:30px;
}
// 页面头部
.page-header {
text-align: center;
padding: 20rpx 0;
border-bottom: 1px solid #eee;
margin-bottom: 40rpx;
.title {
font-size: 36rpx;
font-weight: 600;
color: #333;
}
}
// 订单信息区域
.order-info {
margin-bottom: 60rpx;
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 25rpx 0;
border-bottom: 1px solid #f5f5f5;
font-size: 32rpx;
.label {
color: #666;
}
.value {
font-weight: 600;
font-size: 34rpx;
}
.red {
color: #E60012;
}
}
.total-price {
border-bottom: none;
margin-top: 10rpx;
.label {
font-size: 34rpx;
color: #333;
}
.value {
font-size: 38rpx;
}
}
}
// 支付方式区域
.pay-type-section {
margin-bottom: 80rpx;
.section-title {
font-size: 32rpx;
color: #333;
margin-bottom: 20rpx;
display: block;
}
.radio-item {
display: flex;
align-items: center;
font-size: 32rpx;
padding: 10rpx 0;
.pay-method {
display: flex;
align-items: center;
margin-left: 10rpx;
.pay-name {
font-size: 32rpx;
margin-left: 20rpx;
color: #333;
}
}
}
}
// 底部支付按钮
.fixed-bottom {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx 30rpx 30rpx;
background-color: #fff;
border-top: 1px solid #eee;
.pay-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
border-radius: 44rpx;
font-size: 34rpx;
font-weight: 600;
}
.red-bg {
background-color: #E60012;
color: #fff;
}
}
<template>
<view class="pay-order-container">
<view class="order-info">
<view class="info-item total-price">
<text class="label">支付总费用:</text>
<text class="value red">{{ price ?? 0 }}</text>
</view>
</view>
<view class="pay-type-section">
<text class="section-title">选择支付方式</text>
<radio-group :value="payType" @change="handlePayTypeChange">
<label class="radio-item">
<radio value="0" color="#E60012" :checked="payType === '0'" />
<view class="pay-method">
<image class="icon" src="/static/min.png" mode="widthFix"></image>
<text class="pay-name">民生付</text>
</view>
</label>
</radio-group>
</view>
<!-- 底部支付按钮 -->
<view class="fixed-bottom">
<button class="pay-btn red-bg" :loading="payLoading" @click="handlePay">立即支付</button>
</view>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app';
import * as api from '@/common/api.js'
// 核心数据
const formData = ref({}) // 订单统计数据
const price = ref('') // 核心业务ID
const payType = ref('0') // 支付方式(默认0=民生付)
const payLoading = ref(false) // 支付按钮加载状态
// 页面加载接收参数
onLoad(async (options) => {
console.log('订单ID:', options.price)
if (options.price) {
price.value = options.price
}
})
// 支付方式切换
function handlePayTypeChange(e) {
payType.value = e.detail.value
}
// 立即支付核心逻辑
async function handlePay() {
try {
payLoading.value = true
const res = await api.goPay()
if (res.data?.orderId) {
api.pcallBack2(res.data.orderId)
uni.redirectTo({
url: `/personal/sucPay`
})
}
// 跳转到支付成功页
} catch (err) {
const errMsg = err?.data?.msg || err?.message || '支付失败,请稍后重试'
uni.showToast({
title: errMsg,
icon: 'none'
})
} finally {
payLoading.value = false
}
}
</script>
<style scoped lang="scss">
.pay-order-container {
padding: 30rpx;
background-color: #fff;
min-height: 100vh;
box-sizing: border-box;
}
.icon {
width: 30px;
}
// 页面头部
.page-header {
text-align: center;
padding: 20rpx 0;
border-bottom: 1px solid #eee;
margin-bottom: 40rpx;
.title {
font-size: 36rpx;
font-weight: 600;
color: #333;
}
}
// 订单信息区域
.order-info {
margin-bottom: 60rpx;
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 25rpx 0;
border-bottom: 1px solid #f5f5f5;
font-size: 32rpx;
.label {
color: #666;
}
.value {
font-weight: 600;
font-size: 34rpx;
}
.red {
color: #E60012;
}
}
.total-price {
border-bottom: none;
margin-top: 10rpx;
.label {
font-size: 34rpx;
color: #333;
}
.value {
font-size: 38rpx;
}
}
}
// 支付方式区域
.pay-type-section {
margin-bottom: 80rpx;
.section-title {
font-size: 32rpx;
color: #333;
margin-bottom: 20rpx;
display: block;
}
.radio-item {
display: flex;
align-items: center;
font-size: 32rpx;
padding: 10rpx 0;
.pay-method {
display: flex;
align-items: center;
margin-left: 10rpx;
.pay-name {
font-size: 32rpx;
margin-left: 20rpx;
color: #333;
}
}
}
}
// 底部支付按钮
.fixed-bottom {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx 30rpx 30rpx;
background-color: #fff;
border-top: 1px solid #eee;
.pay-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
border-radius: 44rpx;
font-size: 34rpx;
font-weight: 600;
}
.red-bg {
background-color: #E60012;
color: #fff;
}
}
</style>
\ No newline at end of file
......
......@@ -64,7 +64,8 @@
<uni-forms-item label="头像" required>
<uni-file-picker v-model="photoArr" @delete="delPhoto" return-type="object" limit="1"
@select="upPhoto" :del-ico="false" :image-styles="imageStylesTx"></uni-file-picker>
<image mode="aspectFill" v-if="baseFormData.photo2" style="height:200rpx;width:200rpx;" :src="config.baseUrl_api + baseFormData.photo2"/>
<image mode="aspectFill" v-if="baseFormData.photo2" style="height:200rpx;width:200rpx;"
:src="config.baseUrl_api + baseFormData.photo2" />
</uni-forms-item>
</view>
</uni-forms>
......@@ -80,7 +81,8 @@
<view class="fixedBottom"><button class="btn-red" @click="goSubmit">确 定</button></view>
<!-- 会员须知 -->
<uni-popup ref="popup" type="bottom" background-color="#fff" animation :disable-scroll="true" :mask-click="false">
<uni-popup ref="popup" type="bottom" background-color="#fff" animation :disable-scroll="true"
:mask-click="false">
<view class="tt">入会须知</view>
<view class="popBody">
_{{baseFormData.name}}_欢迎您申请成为中国跆拳道协会(以下简称中国跆协)会员,请确保本次申请是经过您本人或监护人授权同意后的自愿行为,请您务必仔细阅读本入会须知。
......@@ -102,7 +104,7 @@
</view>
</uni-popup>
<uni-popup ref="infoConfirm" type="center" :disable-scroll="true" :mask-click="false">
<uni-popup ref="infoConfirm" type="center" :disable-scroll="true" :mask-click="false">
<view class="tt">确认信息</view>
<view class="popBody">
<view>
......@@ -125,7 +127,7 @@
onLoad
} from '@dcloudio/uni-app'
import config from '@/config.js'
import * as aes2 from '@/common/utils.js'
import * as aes2 from '@/common/utils.js'
const current = ref(0)
const popup = ref(null)
const infoConfirm = ref(null)
......@@ -297,7 +299,7 @@
baseFormData.value.photo = data.data.fang;
baseFormData.value.photo2 = data.data.yuan;
photoArr.value = {
url: config.baseUrl_api+baseFormData.value.photo,
url: config.baseUrl_api + baseFormData.value.photo,
name: '头像',
extname: 'jpg'
}
......@@ -414,19 +416,19 @@
})
}
}
// if (baseFormData.value.idcType == 1 || baseFormData.value.idcType == 3) {
// //转换为大写并判断位数12
// baseFormData.value.idcCode = baseFormData.value.idcCode.toUpperCase()
// // var regex = /^[a-zA-Z]/
// if (baseFormData.value.idcCode.length > 12) {
// uni.showToast({
// icon: 'none',
// title: '请输入正确的证件号',
// duration: 2000
// })
// return
// }
// }
// if (baseFormData.value.idcType == 1 || baseFormData.value.idcType == 3) {
// //转换为大写并判断位数12
// baseFormData.value.idcCode = baseFormData.value.idcCode.toUpperCase()
// // var regex = /^[a-zA-Z]/
// if (baseFormData.value.idcCode.length > 12) {
// uni.showToast({
// icon: 'none',
// title: '请输入正确的证件号',
// duration: 2000
// })
// return
// }
// }
}
......@@ -492,8 +494,8 @@
content: '请确认信息正确',
success: function(res) {
if (res.confirm) {
if(baseFormData.value.idcType=='4'){
baseFormData.value.idcType='0'
if (baseFormData.value.idcType == '4') {
baseFormData.value.idcType = '0'
}
delete baseFormData.value.card
......@@ -523,16 +525,16 @@
uni.showModal({
content: '保存成功',
title: '提示',
confirmText:'继续添加',
cancelColor:'返回首页',
confirmText: '继续添加',
cancelColor: '返回首页',
success: function(res) {
uni.redirectTo({
url: `/personalVip/addVip?tab=${current.value}&idcType=${baseFormData.value.idcType}`
});
},
fail:function(){
fail: function() {
uni.reLaunch({
url:`/pages/index/index`
url: `/pages/index/home`
})
}
})
......@@ -542,6 +544,7 @@
}
});
}
function getUserInfo() {
api.getInfo(perId.value).then(res => {
baseFormData.value = res.data
......@@ -553,7 +556,6 @@
</script>
<style lang="scss">
/* 字段名左对齐 */
.uni-forms-item .uni-forms-item__label {
text-align: left !important;
......@@ -561,16 +563,16 @@
padding-left: 0 !important;
width: auto !important;
}
/* 内容右对齐 */
.uni-forms-item .uni-forms-item__content {
display: flex !important;
align-items: center !important;
justify-content: flex-end !important;
text-align: right !important;
flex-wrap: nowrap !important;
flex-wrap: nowrap !important;
}
/* 输入框内容右对齐 */
.uni-forms-item .uni-easyinput .uni-easyinput__content-input,
.uni-forms-item .uni-easyinput input,
......@@ -579,28 +581,29 @@
.uni-forms-item .uni-data-picker .uni-data-picker__input-box {
text-align: right !important;
}
/* 文本内容右对齐 */
.uni-forms-item .uni-forms-item__content text,
.uni-forms-item .uni-forms-item__content > text {
display: inline-block !important;
white-space: nowrap !important;
.uni-forms-item .uni-forms-item__content>text {
display: inline-block !important;
white-space: nowrap !important;
}
</style>
<style lang="scss" scoped>
:deep(.uni-popup__mask) {
overflow: hidden !important;
position: fixed !important;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden !important;
position: fixed !important;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
:deep(.uni-popup) {
overflow: hidden !important;
overflow: hidden !important;
}
:deep(.segmented-control) {
height: 100rpx;
}
......@@ -620,7 +623,7 @@
font-size: 28rpx;
line-height: 1.5;
height: 70vh;
overflow-y: auto;
overflow-y: auto;
font-family: 华文仿宋;
height: 80vh;
overflow: auto;
......@@ -636,7 +639,7 @@
box-sizing: border-box;
display: flex;
font-size: 30rpx;
text {
color: #014A9F;
......@@ -676,13 +679,14 @@
:deep(.item-text-overflow) {
text-align: left;
}
:deep(.fixUniFormItemStyle .uni-data-picker__input-box) {
justify-content: flex-start !important;
text-align: left !important;
justify-content: flex-start !important;
text-align: left !important;
}
/* 让地区选择器的文本左对齐 */
:deep(.fixUniFormItemStyle .uni-data-picker__text) {
text-align: left !important;
text-align: left !important;
}
</style>
\ No newline at end of file
......
No preview for this file type
import {
createPinia
} from "pinia";
const store = createPinia()
export default store
\ No newline at end of file
import {
defineStore
} from "pinia";
import {
ref
} from 'vue'
export const useUserStore = defineStore('user', () => {
const user = ref(null)
const perInfo = ref(null)
const setUser = (value) => {
user.value = value
}
const setPerInfo = (value) => {
perInfo.value = value
}
return {
user,
setUser,
perInfo,
setPerInfo
}
})
\ No newline at end of file
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!