初始提交

This commit is contained in:
2026-01-04 11:09:06 +08:00
commit 8fa31df250
1326 changed files with 213907 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
<template>
<u-popup v-model="showPopup" mode="center" length="auto">
<view class="jnpf-wrap jnpf-wrap-workflow">
<u-form :model="dataForm" :rules="rules" ref="dataForm" label-position="left" label-width="150"
label-align="left">
<u-form-item label="旧密码" prop="oldPassword" required>
<u-input v-model="dataForm.oldPassword" placeholder="旧密码" type="password"></u-input>
</u-form-item>
<u-form-item label="新密码" prop="password" required>
<u-input v-model="dataForm.password" placeholder="新密码" type="password"></u-input>
</u-form-item>
<u-form-item label="重复密码" prop="repeatPsd" required>
<u-input v-model="dataForm.repeatPsd" placeholder="重复密码" type="password"></u-input>
</u-form-item>
<u-form-item label="验证码" prop="code" required>
<view class="u-flex">
<u-input v-model="dataForm.code" placeholder="验证码"></u-input>
<view style="flex: 0.1">
<u-image :showLoading="true" :src="baseURL + imgUrl" width="130px" height="38px"
@click="changeCode">
</u-image>
</view>
</view>
</u-form-item>
<u-button class="buttom-btn" type="primary" @click.stop="dataFormSubmit">
{{ "保存" }}
</u-button>
</u-form>
</view>
</u-popup>
</template>
<script>
import md5Libs from "@/uni_modules/vk-uview-ui/libs/function/md5";
export default {
props: {
passwordShow: {
type: Boolean,
default: true
},
baseForm: {
type: Object,
default: () => {}
},
},
data() {
var validatePass = (rule, value, callback) => {
// const passwordreg = /(?=.*\d)(?=.*[a-zA-Z])(?=.*[^a-zA-Z0-9]).{6,16}/
//是否包含数字
const containsNumbers = /[0-9]+/;
//是否包含小写字符
const includeLowercaseLetters = /[a-z]+/;
//是否包含大写字符
const includeUppercaseLetters = /[A-Z]+/;
//是否包含字符
const containsCharacters = /\W/;
if (value === "") {
callback(new Error("新密码不能为空"));
} else if (this.baseForm.passwordStrengthLimit == 1) {
if (this.baseForm.passwordLengthMin) {
if (value.length < this.baseForm.passwordLengthMinNumber) {
callback(
new Error(
"新密码长度不能小于" +
this.baseForm.passwordLengthMinNumber +
"位"
)
);
}
}
if (this.baseForm.containsNumbers) {
if (!containsNumbers.test(value)) {
callback(new Error("新密码必须包含数字"));
}
}
if (this.baseForm.includeLowercaseLetters) {
if (!includeLowercaseLetters.test(value)) {
callback(new Error("新密码必须包含小写字母"));
}
}
if (this.baseForm.includeUppercaseLetters) {
if (!includeUppercaseLetters.test(value)) {
callback(new Error("新密码必须包含大写字字母"));
}
}
if (this.baseForm.containsCharacters) {
if (!containsCharacters.test(value)) {
callback(new Error("新密码必须包含字符"));
}
}
callback();
} else {
callback();
}
};
var validatePass2 = (rule, value, callback) => {
if (value === "") {
callback(new Error("重复密码不能为空"));
} else if (value !== this.dataForm.password) {
callback(new Error("两次密码输入不一致"));
} else {
callback();
}
};
return {
showPopup: false,
imgUrl: "",
timestamp: "",
dataForm: {
oldPassword: "",
password: "",
repeatPsd: "",
code: "",
timestamp: "",
},
rules: {
oldPassword: [{
required: true,
message: "旧密码不能为空",
trigger: "blur",
}, ],
password: [{
required: true,
validator: validatePass,
trigger: "blur",
}, ],
repeatPsd: [{
required: true,
validator: validatePass2,
trigger: "blur",
}, ],
code: [{
required: true,
message: "验证码不能为空",
trigger: "blur",
}, ],
},
}
},
computed: {
baseURL() {
return this.define.baseURL;
}
},
watch: {
passwordShow(val) {
if (val) {
this.showPopup = val
this.$nextTick(() => {
this.$refs.dataForm.setRules(this.rules);
});
}
},
},
created() {
this.changeCode()
},
methods: {
changeCode() {
let timestamp = Math.random();
this.timestamp = timestamp;
this.imgUrl = `/api/file/ImageCode/${timestamp}`;
},
dataFormSubmit() {
this.$refs["dataForm"].validate((valid) => {
if (valid) {
let query = {
oldPassword: md5Libs.md5(this.dataForm.oldPassword),
password: md5Libs.md5(this.dataForm.password),
code: this.dataForm.code,
timestamp: this.timestamp,
};
this.$emit('submit', query)
}
});
},
}
}
</script>
<style>
</style>

577
pages/index/dashboard.vue Normal file
View File

@@ -0,0 +1,577 @@
<template>
<view class="index-v">
<!-- #ifndef MP -->
<uni-nav-bar class='nav' :fixed="true" :statusBar="true" :border="false" :right-icon="rightIcon"
@clickRight="scan">
<!-- 左边插槽 -->
<template #left>
<view v-if="portalList.length > 1 && userInfo.appPortalId">
<uni-icons class='icon-ym icon-ym-header-role-toggle' color="#666" size="20"
@click="showSelectBox" />
</view>
</template>
<template #default>
<view class="nav-left">
<view class="nav-left-text">{{portalTitle}}</view>
</view>
</template>
</uni-nav-bar>
<template v-if="userInfo.appPortalId">
<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" :down="downOption"
@up="upCallback" :up="upOption" :bottombar="false" top='10'>
<view class="portal-v" v-if="authConfig.type==0">
<template v-if="formData.length">
<view class="portal-box" v-for="(item,index) in formData" :key="index">
<portalItem :item='item' ref="portalItem" :key="key" v-if="item.show" />
</view>
</template>
<JnpfEmpty v-else></JnpfEmpty>
</view>
<template v-if="authConfig.type==1">
<!-- #ifdef APP -->
<view v-if="authConfig.linkType==1 && showWebView">
<web-view :src="authConfig.customUrl"></web-view>
</view>
<!-- #endif -->
<!-- #ifdef H5 -->
<view v-if="authConfig.linkType==1 && showWebView">
<web-view :src="authConfig.customUrl" :fullscreen="false"
style="width: 100%;height: calc(100vh - 300rpx);"></web-view>
</view>
<!-- #endif -->
<view v-else class="portal-v portal-nodata">
<view class="u-flex-col" style="align-items: center;">
<u-image width="280rpx" height="280rpx" :src="emptyImg"></u-image>
<text class="u-m-t-20" style="color: #909399;">当前内容无法在APP上显示请前往PC门户查看</text>
</view>
</view>
</template>
</mescroll-body>
</template>
<view class="portal-v" style="padding-top: 20rpx;" v-else>
<view class="portal-box">
<defaultPortal></defaultPortal>
</view>
</view>
<u-popup v-model="showSelect" mode="bottom" class="select-box" height="600rpx" @close="closePopup">
<view class="search-box">
<u-search :placeholder="$t('app.apply.pleaseKeyword')" v-model="keyword" height="72"
:show-action="false" bg-color="#f0f2f6" shape="square" search-icon-color="#909399" />
</view>
<view v-for="(item,index) in columnList" :key="index" class="select-item" @click="selectPortal(item,index)">
<text class="u-m-r-12 u-font-30"
:class="[item.icon,{'currentItem':item.isDefault || item.id === item.appPortalId }]" />
<text class="item-text sysName"
:class="{'currentItem':item.isDefault || item.id === item.appPortalId}">{{item.fullName}}</text>
<u-icon name="checkbox-mark " class="currentItem"
v-if="item.isDefault || item.id === item.appPortalId"></u-icon>
</view>
</u-popup>
<!-- #endif -->
<!-- #ifdef MP -->
<view>
<web-view :src="mpPortalUrl"></web-view>
</view>
<!-- #endif -->
</view>
</template>
<script>
var wv; //计划创建的webview
import {
PortalList,
SetPortal,
auth
} from '@/api/portal/portal'
// #ifndef MP
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js"
import IndexMixin from './mixin'
import portalItem from '@/pages/portal/components/index.vue'
import defaultPortal from '@/pages/portal/components/defaultPortal.vue'
// #endif
import emptyImg from '@/static/image/defPortal.png'
import {
useUserStore
} from '@/store/modules/user'
import {
refreshCurrentPage
} from '@/utils/refreshCurrent'
export default {
// #ifndef MP
mixins: [MescrollMixin, IndexMixin],
// #endif
components: {
// #ifndef MP
portalItem,
defaultPortal
// #endif
},
data() {
return {
keyword: '',
showWebView: true,
emptyImg: emptyImg,
rightIcon: '',
key: +new Date(),
formData: [],
portalTitle: '门户',
showSelect: false,
upOption: {
page: {
num: 0,
size: 50,
time: null
},
empty: {
use: false,
},
textNoMore: this.$t('app.apply.noMoreData'),
},
portalList: [],
id: '',
userInfo: {},
downOption: {
use: true,
auto: true
},
authConfig: {},
token: '',
mpPortalUrl: '',
timer: null,
type: 0
};
},
onShow() {
this.token = uni.getStorageSync('token')
this.mpPortalUrl = this.define.baseURL + '/pages/portal/mpPortal/index?token=' + this.token
this.userInfo = uni.getStorageSync('userInfo') || {}
this.showSelect = false
if (!this.userInfo.appPortalId) return
// #ifndef MP
this.getPortalList()
this.$nextTick(() => {
this.mescroll.resetUpScroll();
this.portalList = []
})
// #endif
// #ifdef APP
this.rightIcon = 'scan'
// #endif
},
onReady() {
// #ifdef APP
this.setWebview()
// #endif
},
onLoad(e) {
// #ifndef MP
this.token = uni.getStorageSync('token')
// #endif
},
computed: {
isPortalListValid() {
return Array.isArray(this.portalList) && this.portalList.length;
},
columnList() {
return this.portalList.filter((o) => (o.fullName && o.fullName.match(this.keyword)))
}
},
methods: {
setWebview() {
if (this.authConfig.linkType == 1) {
var currentWebview = this.$scope
.$getAppWebview() //此对象相当于html5plus里的plus.webview.currentWebview()。在uni-app里vue页面直接使用plus.webview.currentWebview()无效
let height = 0;
uni.getSystemInfo({
//成功获取的回调函数,返回值为系统信息
success: (sysinfo) => {
height = sysinfo.windowHeight - 50; //自行修改,自己需要的高度 此处如底部有其他内容,可以直接---(-50)这种
},
complete: () => {}
});
this.$nextTick(() => {
setTimeout(() => {
wv = currentWebview.children()[0]
wv.setStyle({
top: 80,
height,
scalable: true
})
}, 500); //如果是页面初始化调用时,需要延时一下
})
}
},
upCallback(keyword) {
auth(this.userInfo.appPortalId).then(res => {
this.authConfig = res.data || {}
let data = JSON.parse(res.data.formData) || {};
this.formData = data.layout ? JSON.parse(JSON.stringify(data.layout)) : [];
this.handelFormData(data);
if (data.refresh.autoRefresh) {
this.timer && clearInterval(this.timer);
this.timer = setInterval(() => {
uni.$emit('proRefresh')
}, data.refresh.autoRefreshTime * 60000)
}
this.mescroll.endSuccess(this.formData.length);
this.key = +new Date()
// #ifdef APP
this.setWebview()
// #endif
}).catch(() => {
this.formData = []
this.mescroll.endSuccess(0);
this.mescroll.endErr();
this.key = +new Date()
})
},
handelFormData(data) {
const loop = (list) => {
list.forEach(o => {
o.allRefresh = data.refresh
o.show = false
if (o.visibility && o.visibility.length && o.visibility.includes('app')) o.show =
true
if (o.children && o.children.length) loop(o.children)
})
this.key = +new Date()
}
loop(this.formData)
this.dataList = this.formData.filter(o => o.show)
if (this.dataList.length < 1) {
this.formData = this.dataList
this.mescroll.endSuccess(this.dataList.length);
}
},
isJSON(str) {
try {
var obj = JSON.parse(str);
if (typeof obj == 'object' && obj) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
},
scan() {
if (!this.userInfo.appPortalId) return
uni.scanCode({
success: res => {
let url = ""
if (this.isJSON(res.result.trim())) {
const result = JSON.parse(res.result.trim())
if (result.t === 'ADP') {
let config = {
isPreview: 1,
moduleId: result.id,
previewType: result.previewType
}
url = '/pages/apply/dynamicModel/index?config=' + this.jnpf.base64.encode(JSON
.stringify(config))
}
if (result.t === 'DFD') {
url = '/pages/apply/dynamicModel/scanForm?config=' + JSON.stringify(result)
}
if (result.t === 'WFP') {
url = '/pages/workFlow/scanForm/index?config=' + JSON.stringify(result)
}
if (result.t === 'report') {
userInfo = uni.getStorageSync('userInfo') || {}
const appCode = userInfo.systemCode
let url_ =
`${this.report}/preview.html?id=${result.id}&token=${this.token}&appCode=${appCode}&page=1&from=menu`
url = '/pages/apply/externalLink/index?url=' + encodeURIComponent(url_) +
'&fullName= ' + result.fullName
}
if (result.t === 'portal') {
url = '/pages/portal/scanPortal/index?id=' + result.id
}
if (result.t === 'login') {
url = '/pages/login/scanLogin?id=' + result.id
}
} else {
url = '/pages/my/scanResult/index?result=' + res.result
}
uni.navigateTo({
url,
fail: (err) => {
this.$u.toast("暂无此页面")
}
})
}
});
},
/* 获取门户列表 */
getPortalList() {
PortalList().then(res => {
let list = res.data.list || [];
this.portalList = list
list.map(o => {
this.portalList.forEach(o => {
o.appPortalId = this.userInfo.appPortalId
if (o.id === o.appPortalId) this.portalTitle = o.fullName
})
})
})
},
closePopup() {
// #ifdef APP
this.setWebview()
uni.$emit('showVideo', true)
this.showWebView = true
// #endif
},
showSelectBox() {
this.showSelect = !this.showSelect;
// #ifndef MP
this.getPortalList();
// #endif
// #ifdef APP
uni.$emit('showVideo', false);
this.showWebView = false;
this.setWebview();
// #endif
},
selectPortal(item, index) {
SetPortal(item.id).then(res => {
this.portalTitle = this.portalList[index].fullName;
this.userInfo.appPortalId = item.id;
// #ifndef MP
this.mescroll.triggerDownScroll();
// #endif
this.showSelectBox(false);
uni.setStorageSync('userInfo', this.userInfo);
});
},
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
}
.index-v {
.portal-v {
padding: 0 20rpx 20rpx 20rpx;
.portal-box {
width: 100%;
height: 100%;
.htabs {
.u-scroll-box {
height: 80rpx;
.u-tab-item {
border-right: 1px solid #e4e7ed;
&::before {
content: "";
}
}
}
}
.card-v {
&.u-card {
margin: 0rpx !important;
padding: 0rpx !important;
}
}
}
}
.nav {
z-index: 99999;
::v-deep .uni-navbar__content {
z-index: 99999;
}
::v-deep .uni-navbar__header-container {
justify-content: center;
}
.nav-left {
max-width: 100%;
display: flex;
align-items: center;
.nav-left-text {
font-weight: 700;
font-size: 32rpx;
flex: 1;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.portal-select {
background-color: #fff;
height: 80rpx;
padding-left: 20rpx;
line-height: 80rpx;
.portal-select-inner {
width: 200rpx;
height: 100%;
.portal-select-text {
color: #303133;
}
}
.right-icons {
font-weight: 700;
margin-top: 2px;
margin-left: 4px;
transition-duration: 0.3s;
color: #606266 !important;
}
.select-right-icons {
transform: rotate(-180deg);
}
}
.select-box {
overflow-y: scroll;
.search-box {
height: 112rpx;
width: 100%;
padding: 20rpx 20rpx;
z-index: 10000;
background: #fff;
&::after {
content: " ";
position: absolute;
left: 2%;
top: 62px;
box-sizing: border-box;
width: 96%;
height: 1px;
transform: scale(1, .3);
border: 0 solid #e4e7ed;
z-index: 2;
border-bottom-width: 1px;
}
}
.currentItem {
color: #02a7f0;
}
.select-item {
height: 88rpx;
display: flex;
align-items: center;
padding: 0 20rpx;
font-size: 30rpx;
color: #303133;
text-align: left;
position: relative;
&::after {
content: " ";
position: absolute;
left: 2%;
bottom: 0;
box-sizing: border-box;
width: 96%;
height: 1px;
transform: scale(1, .3);
border: 0 solid #e4e7ed;
z-index: 2;
border-bottom-width: 1px;
}
.sysName {
flex: 1;
overflow: auto;
min-width: 0;
font-size: 28rpx;
}
}
}
.popup {
position: absolute;
top: 244rpx;
z-index: 99999;
width: 70%;
height: 200px;
border: 1px solid #ccc;
background-color: #fff;
left: 283rpx;
border-radius: 4rpx;
transform: translate(-50%, -50%) scale(0);
animation: popup-animation 0.4s ease-in-out forwards;
}
.uni-select--mask {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 2;
}
@keyframes popup-animation {
from {
transform: translate(-50%, -50%) scale(0);
opacity: 0;
}
to {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
}
.nav {
z-index: 99999;
::v-deep .uni-navbar__content {
z-index: 99999;
}
::v-deep .uni-navbar__header-container {
justify-content: center;
}
.nav-left {
max-width: 100%;
display: flex;
align-items: center;
.nav-left-text {
font-weight: 700;
font-size: 29rpx;
flex: 1;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
.portal-nodata {
position: absolute;
top: 450rpx;
width: 100%;
text-align: center;
z-index: 100;
background-color: #f0f2f6;
}
</style>

362
pages/index/index.vue Normal file
View File

@@ -0,0 +1,362 @@
<template>
<view class="index_v">
<u-sticky>
<view class="head-tabs u-flex">
<view class="head-tabs-item" @click="openPage('/pages/workFlow/flowTodo/index','approve')">
<text class="icon-ym icon-ym-flowTodo-app u-m-r-4 icon-style" />
<text class="u-font-24 head-tabs-name">审批中心</text>
<u-badge type="error" :count="count" :absolute="true" :offset="offset" />
</view>
<view class="head-tabs-item" @click="openPage('/pages/workFlow/entrustAgent/index','entrust')">
<text class="icon-ym icon-ym-flowDone-app u-m-r-4 icon-style" />
<text class="u-font-24 head-tabs-name">委托代理</text>
</view>
<view class="head-tabs-item" @click="openPage('/pages/workFlow/schedule/index','schedule')">
<text class="icon-ym icon-ym-flowDone-app u-m-r-4 icon-style" />
<text class="u-font-24 head-tabs-name">日程</text>
</view>
<view class="head-tabs-item" @click="openPage('/pages/workFlow/document/index','document')">
<text class="icon-ym icon-ym-flowCopy-app u-m-r-4 icon-style" />
<text class="u-font-24 head-tabs-name">文档</text>
</view>
</view>
</u-sticky>
<CommonPane :flowList="homeData.favoritesFlowList || []" :menuList="homeData.favoritesMenuList || []"
type="collect" @launch="launch" v-if="homeData.favoritesEnable" @openPage="openPage" @addApp="addApp"
:showAdd="true" :flowEnabled="homeData.flowEnabled" />
<CommonPane title="最近使用" :flowList="homeData.latelyUseFlowList || []" type="use"
:menuList="homeData.latelyUseMenuList || []" @launch="launch" v-if="homeData.latelyUseEnable"
@openPage="openPage" :flowEnabled="homeData.flowEnabled" />
<CommonPane title="最近常用" :flowList="homeData.commonUseFlowList || []" type="common"
:menuList="homeData.commonUseMenuList || []" @launch="launch" v-if="homeData.commonUseEnable"
@openPage="openPage" :flowEnabled="homeData.flowEnabled" />
<PasswordPopup @submit="dataFormSubmit" :passwordShow="passwordShow" :formData="baseForm"></PasswordPopup>
</view>
</template>
<script>
import {
getFlowTodoCount
} from "@/api/workFlow/flowEngine";
import {
getHomeData
} from "@/api/index/index";
import {
useUserStore
} from '@/store/modules/user'
import {
useChatStore
} from '@/store/modules/chat'
import {
updatePassword,
updatePasswordMessage
} from '@/api/common.js'
import chat from '@/libs/chat'
import CommonPane from '@/components/CommonPane'
import PasswordPopup from './components/PasswordPopup'
import {
useLocale
} from '@/locale/useLocale';
const chatStore = useChatStore()
export default {
components: {
CommonPane,
PasswordPopup
},
data() {
return {
homeData: [],
count: 0,
offset: [20, -10],
menuList: [],
passwordShow: false,
baseForm: {
passwordStrengthLimit: 0,
passwordLengthMin: false,
passwordLengthMinNumber: 0,
containsNumbers: false,
includeLowercaseLetters: false,
includeUppercaseLetters: false,
containsCharacters: false,
mandatoryModificationOfInitialPassword: 0,
},
userInfo: {}
};
},
onLoad() {
const chatStore = useChatStore()
if (!chatStore.getSocket) chat && chat.initSocket()
const userStore = useUserStore()
userStore.getCurrentUser().then(() => {
this.getSystemName()
}).catch(() => {
setTimeout(() => {
userStore.resetToken()
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/index'
})
}, 500)
}, 1000)
})
const {
changeLocale
} = useLocale();
changeLocale(uni.getLocale())
},
onShow(e) {
this.init()
},
computed: {
baseURL() {
return this.define.baseURL;
},
token() {
return uni.getStorageSync('token')
},
report() {
return this.define.report;
},
pcURL() {
return this.define.pcURL;
}
},
methods: {
init() {
this.getHomeData()
this.getFlowCount()
this.getSystemConfig()
},
// 获取系统配置
getSystemConfig() {
updatePasswordMessage();
this.userInfo = uni.getStorageSync('userInfo') || {}
const config = uni.getStorageSync('sysConfigInfo') || {};
this.$nextTick(() => {
this.baseForm.passwordStrengthLimit = config.passwordStrengthLimit
this.baseForm.passwordLengthMin = config.passwordLengthMin
this.baseForm.passwordLengthMinNumber = config.passwordLengthMinNumber
this.baseForm.containsNumbers = config.containsNumbers
this.baseForm.includeLowercaseLetters = config.includeLowercaseLetters
this.baseForm.containsCharacters = config.containsCharacters
this.baseForm.mandatoryModificationOfInitialPassword = config
.mandatoryModificationOfInitialPassword
if (this.userInfo.changePasswordDate == null && config
.mandatoryModificationOfInitialPassword == 1)
this.passwordShow = true;
})
},
dataFormSubmit(query) {
updatePassword(query).then((res) => {
const userStore = useUserStore()
userStore.logout().then(() => {
uni.reLaunch({
url: "/pages/login/index",
});
});
})
},
//获取并设置应用名称
getSystemName() {
const userInfo = uni.getStorageSync("userInfo");
this.menuList = uni.getStorageSync("menuList");
uni.setNavigationBarTitle({
title: userInfo.systemName
})
},
launch(item) {
if (item.tabType == 'flow') return this.JumpFlow(item)
if (item.tabType == 'menu') return this.JumpApply(item)
},
JumpApply(item) {
if (item.type == 1) {
getChildList(item.id).then(res => {
this.listChild = res.data || []
this.handleProperty(this.listChild)
this.$nextTick(() => {
uni.navigateTo({
url: "/pages/apply/catalog/index?config=" +
this.jnpf.base64.encode(JSON.stringify(this.listChild[0])),
fail: (err) => {
this.$u.toast("暂无此页面");
},
});
})
})
return;
}
let url = ''
// 2-页面 11-回传表单
if (item.type == 2 || item.type == 11) {
if (!item.pageAddress) {
this.$u.toast("暂无此页面");
return;
}
url = item.pageAddress + "?menuId=" + item.id + "&fullName=" + item.fullName
}
// 3-在线表单 9-流程
if (item.type == 3 || item.type == 9) {
if (!item.moduleId) {
this.$u.toast("暂无此页面");
return;
}
url = "/pages/apply/dynamicModel/index?config=" + this.jnpf.base64.encode(JSON.stringify(item))
}
// 外链
if (item.type == 7) {
if (!item.pageAddress) {
this.$u.toast("暂无此页面");
return;
}
url = "/pages/apply/externalLink/index?url=" + encodeURIComponent(item.pageAddress) + "&fullName=" +
item.fullName + "&type=" + item.type
}
// 报表(原)
if (item.type == 5) {
if (!item.moduleId) {
this.$u.toast("暂无此页面");
return;
}
userInfo = uni.getStorageSync('userInfo') || {}
const appCode = userInfo.systemCode
const urlPre = encodeURIComponent(
`${this.report}/preview.html?id=${item.moduleId}&token=${this.token}&appCode=${appCode}&page=1&from=menu`
)
url = "/pages/apply/externalLink/index?url=" + urlPre + "&fullName=" + item.fullName + "&type=" +
item.type
}
// 报表
if (item.type == 10) {
if (!item.moduleId) {
this.$u.toast("暂无此页面");
return;
}
const urlPre = encodeURIComponent(
`${this.pcURL}/reportPreview?id=${item.moduleId}&token=${this.token}&from=app`
);
url = "/pages/apply/externalLink/index?url=" + urlPre + "&fullName=" + item.fullName + "&type=" +
item.type
}
// 门户
if (item.type == 8) {
if (!item.moduleId) {
this.$u.toast("暂无此页面");
return;
}
url = "/pages/portal/scanPortal/index?id=" + item.moduleId + "&portalType=1&fullName=" +
item.fullName
}
if (!url) return;
uni.navigateTo({
url,
fail: () => {
this.$u.toast("暂无此页面");
},
});
},
handleProperty(list) {
const loop = (par) => {
par.map(o => {
if (o?.propertyJson) {
let propertyJson = JSON.parse(o.propertyJson);
this.$set(o, "iconBackground", propertyJson.iconBackgroundColor || "");
this.$set(o, "moduleId", propertyJson.moduleId || "");
}
if (o?.children && o?.children?.length) loop(o.children)
})
}
loop(list)
},
JumpFlow(item) {
const config = {
id: "",
flowId: item.id,
opType: "-1",
isFlow: 1
};
uni.navigateTo({
url: "/pages/workFlow/flowBefore/index?config=" +
this.jnpf.base64.encode(JSON.stringify(config))
});
},
//获取审批中心待办条数
getFlowCount() {
getFlowTodoCount().then((res) => {
this.count = res.data.flowTodo || 0;
})
},
getHomeData() {
getHomeData().then((res) => {
for (let i = 0; i < res.data.length; i++) {
const e = res.data[i];
this.homeData[`${e.code}Enable`] = e.enable;
this.homeData['flowEnabled'] = e.flowEnabled
this.handleProperty(e.appList)
if (['commonUse', 'favorites', 'latelyUse'].includes(e.code)) {
this.homeData[`${e.code}MenuList`] = e.appList || [];
this.homeData[`${e.code}FlowList`] = e.flowList || [];
}
}
}).catch(() => {});
},
//更多按钮
openPage(path, type) {
if (type === 'approve') {
let workFlowList = this.menuList.filter(o => o.enCode === 'workFlow')
if (!workFlowList[0]?.children?.length) return this.$u.toast('暂无权限')
}
if (!path) return;
uni.navigateTo({
url: path,
});
},
//添加按钮
addApp(path) {
if (!path) return;
uni.navigateTo({
url: path,
});
},
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
padding-bottom: 20rpx;
}
.index_v {
.head-tabs {
background-color: #fff;
width: 100%;
height: 120rpx;
justify-content: space-between;
padding: 0 20rpx;
.head-tabs-item {
display: flex;
justify-content: center;
font-size: 28rpx;
color: #303133;
flex-shrink: 0;
position: relative;
align-items: center;
height: 120rpx;
.icon-style {
font-size: 42rpx;
color: #666666;
}
.head-tabs-name {
color: #303133;
font-family: PingFang SC;
margin-left: 6rpx;
}
}
}
}
</style>

464
pages/index/message.vue Normal file
View File

@@ -0,0 +1,464 @@
<template>
<view class="message-v">
<view class="nav-bar-box">
<uni-nav-bar :fixed="true" :statusBar="true" :border="false" @clickLeft="back" height="44px">
<block #default>
<view class="u-flex slot-wrap">
<view class="title">{{$t('app.tabBar.message')}}</view>
<view class="nav-icon u-m-l-10" @click="readAll">
<text class="icon-ym icon-ym-clean" />
</view>
</view>
</block>
</uni-nav-bar>
</view>
<view class="notice-warp" :style="{'top':topSearch,'height':noticeWarpH+'px'}">
<view class="search-box ">
<u-search :placeholder="$t('app.apply.pleaseKeyword')" v-model="keyword" height="72"
:show-action="false" @change="search" bg-color="#f0f2f6" shape="square">
</u-search>
</view>
<view class="sticky-box-tabs">
<view class="tabs-box">
<u-tabs class="u-tab-box" :list="tablist" :current="current" @change="tabChange" :offset="offset">
</u-tabs>
</view>
<view class="status-box">
<view class="status-icon" @click="showAction = true">
<uni-icons type="bottom" size="16" color="#3C3C3C"></uni-icons>
</view>
</view>
</view>
</view>
<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
:up="upOption" :bottombar="false" :top="mescrollTop">
<view class="message-list">
<view class="u-flex message-item u-border-bottom " v-for="(item, i) in list" :key="i"
@click="detail(item)">
<view class="message-item-img message-item-icon u-flex u-row-center"
:class="{'message-item-icon-flow':item.type == 2,'message-notice-icon':item.type == 3,'message-schedule':item.type == 4}">
<text class="icon-ym icon-ym-xitong" v-if="item.type == 1" />
<text class="icon-ym icon-ym-generator-notice" v-else-if="item.type == 3" />
<text class="icon-ym icon-ym-portal-schedule" v-else-if="item.type == 4" />
<text class="icon-ym icon-ym-generator-flow" v-else />
<text class="redDot" v-if="!item.isRead"></text>
</view>
<view class="message-item-txt">
<view class="message-item-title u-flex">
<text class="title u-line-1">{{item.title}}</text>
</view>
<view class="u-flex u-row-between message-item-cell">
<text>{{item.releaseUser}}</text>
<text
class="u-font-24">{{item.releaseTime?$u.timeFormat(item.releaseTime, 'mm-dd hh:MM'):''}}</text>
</view>
</view>
</view>
</view>
</mescroll-body>
<u-action-sheet :list="statusOptions" v-model="showAction" @click="handleClick"></u-action-sheet>
</view>
</template>
<script>
import {
getMessageList,
getMessageDetail,
checkInfo,
getUnReadMsgNum,
MessageAllRead
} from '@/api/message.js'
import resources from '@/libs/resources.js'
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
import IndexMixin from "@/pages/index/mixin.js";
import {
useChatStore
} from '@/store/modules/chat'
const chatStore = useChatStore()
export default {
mixins: [MescrollMixin, IndexMixin],
data() {
return {
mescrollTop: 326,
topSearch: '80px',
appTopHeight: 0,
offset: [5, 8],
downOption: {
use: true,
auto: true
},
upOption: {
page: {
num: 0,
size: 20,
time: null
},
empty: {
use: true,
icon: resources.message.nodata,
tip: this.$t('common.noData'),
fixed: false,
top: "640rpx",
},
textNoMore: this.$t('app.apply.noMoreData'),
},
keyword: '',
type: "",
list: [],
current: 0,
tablist: [{
name: '全部',
count: 0
}, {
name: '系统',
count: 0
}, {
name: '流程',
count: 0
}, {
name: '公告',
count: 0
}, {
name: '日程',
count: 0
}],
status: '未读',
isRead: 0,
statusOptions: [{
text: '全部'
}, {
text: '未读'
}, {
text: '已读'
}],
showAction: false,
noticeWarpH: 0
}
},
onLoad(option) {
this.getUnReadMsgNum()
},
mounted() {
this.getContentHeight()
},
methods: {
back() {
uni.navigateBack()
},
upCallback(page) {
let query = {
currentPage: page.num,
pageSize: page.size,
keyword: this.keyword,
type: this.type,
isRead: this.isRead
}
getMessageList(query, {
load: page.num == 1
}).then(res => {
this.mescroll.endSuccess(res.data.list.length);
if (page.num == 1) this.list = [];
const list = res.data.list;
this.list = this.list.concat(list);
}).catch(() => {
this.mescroll.endErr();
})
},
getUnReadMsgNum() {
getUnReadMsgNum().then(res => {
const data = res.data
for (var i = 0; i < this.tablist.length; i++) {
const item = this.tablist[i]
if (item.name == '全部') item.count = data.unReadNum
if (item.name == '系统') item.count = data.unReadSystemMsg
if (item.name == '流程') item.count = data.unReadMsg
if (item.name == '公告') item.count = data.unReadNotice
if (item.name == '日程') item.count = data.unReadSchedule
}
chatStore.setMsgInfoNum(Number(data.unReadNum))
})
},
search() {
this.searchTimer && clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.list = [];
this.mescroll.resetUpScroll();
}, 300)
},
tabChange(e) {
this.current = e
if (e == 0) this.type = ''
if (e == 1) this.type = 3
if (e == 2) this.type = 2
if (e == 3) this.type = 1
if (e == 4) this.type = 4
this.list = [];
this.mescroll.resetUpScroll();
},
handleClick(index) {
if (index == 0) {
this.status = '全部'
this.isRead = ''
} else if (index == 1) {
this.status = '未读'
this.isRead = 0
} else {
this.status = '已读'
this.isRead = 1
}
this.list = [];
this.mescroll.resetUpScroll();
},
async getContentHeight() {
const windowHeight = this.$u.sys().windowHeight;
// 获取元素尺寸
const [navBarHeight, noticeWarpRect, searchBoxRect, stickyBoxTabsRect] = await Promise.all([
this.$uGetRect('.nav-bar-box'),
this.$uGetRect('.notice-warp'),
this.$uGetRect('.search-box'),
this.$uGetRect('.sticky-box-tabs')
]);
// 计算高度
const appTopHeight = navBarHeight.height;
const searchBoxHeight = searchBoxRect.height;
const stickyBoxTabsHeight = stickyBoxTabsRect.height;
// 设置组件数据
this.topSearch = `${appTopHeight}px`;
this.appTopHeight = appTopHeight;
this.noticeWarpH = searchBoxHeight + stickyBoxTabsHeight;
this.mescrollTop = appTopHeight + searchBoxHeight + stickyBoxTabsHeight + 10;
},
readAll() {
const query = {
keyword: this.keyword,
type: this.type,
isRead: this.isRead
}
MessageAllRead(query).then(res => {
if (this.isRead === 0) {
this.list = [];
this.mescroll.resetUpScroll();
} else {
for (let i = 0; i < this.list.length; i++) {
this.$set(this.list[i], 'isRead', '1')
}
}
this.getUnReadMsgNum()
uni.showToast({
title: res.msg,
icon: 'none'
});
})
},
detail(item) {
if (item.type == '1' || item.type == '3') {
if (!item.isRead) {
item.isRead = 1
chatStore.setMsgInfoNum()
uni.$on('initUnReadMsgNum', () => {
this.getUnReadMsgNum()
})
}
uni.navigateTo({
url: '/pages/message/messageDetail/index?id=' + item.id
});
} else {
getMessageDetail(item.id).then(res => {
if (!item.isRead) {
item.isRead = 1
chatStore.setMsgInfoNum()
this.$nextTick(() => {
this.getUnReadMsgNum()
})
}
let data = res.data || {};
let bodyText = data.bodyText ? JSON.parse(data.bodyText) : {};
if (item.type == 4) {
if (bodyText.type == 3) return
let groupId = bodyText.groupId || ''
uni.navigateTo({
url: `/pages/workFlow/schedule/detail?groupId=${groupId}&id=${bodyText.id}`
});
return
}
let config = {
id: data.id,
flowId: bodyText.flowId,
opType: bodyText.opType,
taskId: bodyText.taskId,
operatorId: bodyText.operatorId,
}
if (item.flowType == 1) {
checkInfo(config.operatorId || config.taskId, config.opType).then(res => {
config.opType = res.data.opType;
setTimeout(() => {
uni.navigateTo({
url: '/pages/workFlow/flowBefore/index?config=' +
this.jnpf.base64.encode(JSON.stringify(config))
});
}, 300)
})
} else {
if (bodyText.type == 0) return
let url = '/pages/workFlow/entrustAgent/index'
let i = bodyText.type == 1 ? 0 : bodyText.type == 2 ? 1 : bodyText.type == 3 ? 2 : 3
setTimeout(() => {
uni.navigateTo({
url: url + '?index=' + i
});
}, 300)
}
})
}
}
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
}
.message-v {
height: 100%;
:deep(.u-border-bottom):after {
border-bottom-width: 0px
}
.slot-wrap {
.title {
font-size: 32rpx;
font-weight: bold;
}
.nav-icon {
width: 44rpx;
height: 44rpx;
background: rgb(240, 242, 246);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
text {
font-size: 32rpx;
}
}
}
.message-schedule {
background-color: #77f !important;
}
.sticky-box {
height: 100%;
position: sticky;
z-index: 100;
}
.sticky-box-tabs {
width: 100%;
display: flex;
flex-direction: row;
background-color: #fff;
align-items: center;
.tabs-box {
width: 90%;
}
.status-box {
width: 10%;
text-align: center;
padding: 28rpx 18rpx;
.status-title {
flex-shrink: 0;
color: #999;
font-size: 28rpx;
}
.status-icon {
width: 100%;
align-items: center;
font-size: 24rpx;
}
.status-input {
flex: 1;
}
}
}
.u-tab-box {}
.message-list {
padding: 0 20rpx;
background-color: #fff;
.message-item {
height: 132rpx;
.message-item-icon-flow {
background-color: #33CC51 !important;
}
.message-notice-icon {
background-color: #e09f0c !important;
}
.message-item-img {
width: 96rpx;
height: 96rpx;
margin-right: 16rpx;
flex-shrink: 0;
border-radius: 50%;
background-color: #3B87F7;
position: relative;
.icon-ym {
color: #fff;
font-size: 50rpx;
}
.redDot {
height: 16rpx;
width: 16rpx;
border-radius: 50%;
background: #FE5146;
display: inline-block;
// margin-right: 6rpx;
flex-shrink: 0;
position: absolute;
right: 2rpx;
top: 2rpx;
}
}
.message-item-txt {
width: calc(100% - 112rpx);
.message-item-title {
line-height: 46rpx;
margin-bottom: 6rpx;
.title {
font-size: 28rpx;
}
}
.message-item-cell {
color: #909399;
font-size: 24rpx;
}
}
}
}
}
</style>

24
pages/index/mixin.js Normal file
View File

@@ -0,0 +1,24 @@
import {
useChatStore
} from '@/store/modules/chat'
export default {
onShow() {
this.setTabBarBadge()
},
methods: {
setTabBarBadge() {
const chatStore = useChatStore()
const badgeNum = chatStore.getBadgeNum || 0
if (badgeNum) {
uni.setTabBarBadge({
index: 2,
text: badgeNum > 99 ? '99+' : badgeNum.toString()
});
} else {
uni.removeTabBarBadge({
index: 2
});
}
}
}
}

376
pages/index/my.vue Normal file
View File

@@ -0,0 +1,376 @@
<template>
<view class="my-v" v-if="loading">
<view class="u-flex user-box">
<view class="u-m-r-10">
<u-avatar size="127" @click='chooseAvatar' :src='avatarSrc'></u-avatar>
</view>
<view class="u-flex-1 f-right" @click="personalPage('/pages/my/personalData/index')">
<view class="u-font-36 u-m-l-16">{{baseInfo.realName}}</view>
<view class="u-m-l-10 u-p-10">
<u-icon name="arrow-right" color="#969799" size="28"></u-icon>
</view>
</view>
</view>
<view class="u-m-t-20 my-group-box" v-for="(group, index) in cellGroups" :key="index">
<view class="my-group-box-inner">
<u-cell-group :border="false" class="cell-group">
<view v-for="(item, idx) in group.items" :key="idx">
<u-cell-item :title="$t(item.title)" @click="openPage(item.page, item.param)"
:title-style="titleStyle" :border-bottom="item.borderBottom"
v-if="item.title!='app.my.scanCode'">
<template #icon>
<text :class="['icon-ym', item.icon, 'u-m-r-16', 'u-font-32', 'my-list']"
:style="{ color: item.color }" />
</template>
</u-cell-item>
<!-- #ifndef H5 -->
<u-cell-item :title="$t(item.title)" @click="scanCode()" :title-style="titleStyle"
:border-bottom="item.borderBottom" v-if="item.title=='app.my.scanCode'">
<template #icon>
<text :class="['icon-ym', item.icon, 'u-m-r-16', 'u-font-32', 'my-list']"
:style="{ color: item.color }" />
</template>
</u-cell-item>
<!-- #endif -->
</view>
</u-cell-group>
</view>
</view>
<view class="u-p-t-20">
<view class="logout-cell" hover-class="u-cell-hover" @click="logout">{{$t('app.my.logout')}}</view>
</view>
</view>
</template>
<script>
import IndexMixin from './mixin.js'
import {
UpdateAvatar,
UserSettingInfo,
setMajor
} from '@/api/common'
import chat from '@/libs/chat.js'
import {
useUserStore
} from '@/store/modules/user'
import {
useChatStore
} from '@/store/modules/chat'
export default {
mixins: [IndexMixin],
data() {
return {
titleStyle: {
color: '#606266'
},
userInfo: '',
avatarSrc: '',
baseInfo: {},
loading: false,
cellGroups: [{
items: [{
title: 'app.my.organization',
page: '/pages/my/organization/index',
param: 'position',
icon: 'icon-ym-zuzhi',
color: '#6071F5',
borderBottom: true
},
{
title: 'app.my.switchIdentity',
page: '/pages/my/identity/index',
param: 'standing',
icon: 'icon-ym-position1',
color: '#F4A02F',
borderBottom: true
},
{
title: 'app.my.changeSystem',
page: '/pages/my/changeSystem/index',
icon: 'icon-ym-header-sys-toggle',
color: '#3686F2',
borderBottom: false
},
],
},
{
items: [{
title: 'app.my.personalSetting',
page: '/pages/my/personalSetting/index',
icon: 'icon-ym-shezhi',
color: '#F46E1B',
borderBottom: true
},
{
title: 'app.my.accountSecurity',
page: '/pages/my/accountSecurity/index',
icon: 'icon-ym-secure',
color: '#26C6A1',
borderBottom: true
},
{
title: 'app.my.contacts',
page: '/pages/my/contacts/index',
icon: 'icon-ym-contacts',
color: '#6071F5',
borderBottom: true
},
{
title: 'app.my.chat',
page: '/pages/message/chat/index',
icon: 'icon-ym-chat',
color: '#4CBF2A',
borderBottom: false
},
],
},
{
items: [{
title: 'app.my.scanCode',
page: '',
icon: 'icon-ym-scanCode1',
color: '#F7AA41',
borderBottom: true
},
{
title: 'app.my.setting',
page: '/pages/my/settings/index',
icon: 'icon-ym-route-appMenu',
color: '#5944FC',
borderBottom: false
},
],
},
],
}
},
computed: {
baseURL() {
return this.define.comUploadUrl
},
baseURL2() {
return this.define.baseURL
},
token() {
return uni.getStorageSync('token')
},
report() {
return this.define.report
}
},
onLoad() {
const chatStore = useChatStore()
if (!chatStore.getSocket) chat.initSocket()
},
onShow() {
UserSettingInfo().then(res => {
this.baseInfo = res.data || {}
this.avatarSrc = this.baseURL2 + this.baseInfo.avatar
this.loading = true
})
uni.setNavigationBarTitle({
title: "我的"
})
},
methods: {
chooseAvatar() {
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
success: (res) => {
// #ifdef H5
let isAccept = new RegExp('image/*').test(res.tempFiles[0].type)
if (!isAccept) return this.$u.toast(`请上传图片`)
// #endif
let tempFilePaths = res.tempFilePaths[0]
uni.uploadFile({
url: this.baseURL + 'userAvatar',
filePath: tempFilePaths,
name: 'file',
header: {
'Authorization': this.token
},
success: (uploadFileRes) => {
let data = JSON.parse(uploadFileRes.data)
if (data.code === 200) {
UpdateAvatar(data.data.name).then(res => {
this.$u.toast('头像更换成功')
this.avatarSrc = this.baseURL2 + data.data.url
})
} else {
this.$u.toast(data.msg)
}
},
fail: (err) => {
this.$u.toast('头像更换失败')
}
});
}
});
},
openPage(path, type) {
if (!path) return
let url = !!type ? path + '?majorType=' + type : path
uni.navigateTo({
url: url
})
},
personalPage(path) {
if (!path) return;
const neededFields = [
'realName', 'nation', 'gender', 'nativePlace', 'certificatesType',
'certificatesNumber', 'education', 'birthday', 'telePhone', 'landline',
'urgentContacts', 'urgentTelePhone', 'postalAddress', 'signature'
];
const baseInfo = neededFields.reduce((obj, key) => {
if (this.baseInfo[key] !== undefined) {
obj[key] = this.baseInfo[key];
}
return obj;
}, {});
uni.navigateTo({
url: `${path}?baseInfo=${JSON.stringify(baseInfo)}`
});
},
isJSON(str) {
try {
var obj = JSON.parse(str);
if (typeof obj == 'object' && obj) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
},
logout() {
uni.showModal({
title: '提示',
content: '确定退出当前账号吗?',
success: res => {
if (res.confirm) {
const userStore = useUserStore()
userStore.logout().then(() => {
uni.closeSocket()
uni.reLaunch({
url: '/pages/login/index'
})
})
this.removeAccount()
}
}
})
},
removeAccount() {
let model = uni.getStorageSync('rememberAccount')
if (!model.remember) {
model.account = ''
model.password = ''
model.remember = false
uni.setStorageSync('rememberAccount', model)
}
},
scanCode() {
uni.scanCode({
success: res => {
let url = ""
if (this.isJSON(res.result.trim())) {
const result = JSON.parse(res.result.trim())
if (result.t === 'ADP') {
let config = {
isPreview: 1,
moduleId: result.id,
previewType: result.previewType
}
url = '/pages/apply/dynamicModel/index?config=' + this.jnpf.base64.encode(JSON
.stringify(config))
}
if (result.t === 'DFD') {
url = '/pages/apply/dynamicModel/scanForm?config=' + JSON.stringify(result)
}
if (result.t === 'WFP') {
url = '/pages/workFlow/scanForm/index?config=' + JSON.stringify(result)
}
if (result.t === 'report') {
userInfo = uni.getStorageSync('userInfo') || {}
const appCode = userInfo.systemCode
let url_ =
`${this.report}/preview.html?id=${result.id}&token=${this.token}&appCode=${appCode}&page=1&from=menu`
url = '/pages/apply/externalLink/index?url=' + encodeURIComponent(url_) +
'&fullName= ' + result.fullName
}
if (result.t === 'portal') {
url = '/pages/portal/scanPortal/index?id=' + result.id
}
if (result.t === 'login') {
url = '/pages/login/scanLogin?id=' + result.id
}
} else {
url = '/pages/my/scanResult/index?result=' + res.result
}
uni.navigateTo({
url,
fail: (err) => {
this.$u.toast("暂无此页面")
}
})
}
});
}
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
}
.my-v {
:deep(.u-cell) {
height: 112rpx;
padding: 0;
}
.my-group-box {
.my-group-box-inner {
background-color: #fff;
.cell-group {
/* #ifndef MP-WEIXIN */
padding: 0 20rpx;
/* #endif */
// padding: 0 20rpx;
}
}
}
.user-box {
background-color: #fff;
padding: 0 10rpx 10rpx 20rpx;
/* #ifdef MP-WEIXIN */
padding: 0 20rpx 20rpx 20rpx !important;
/* #endif */
}
.logout-cell {
text-align: center;
font-size: 32rpx;
height: 108rpx;
background-color: #fff;
color: #D82828;
line-height: 98rpx;
font-family: PingFang SC;
}
.f-right {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
text-align: center;
}
}
</style>