Initial commit

This commit is contained in:
2025-07-04 16:18:58 +08:00
commit 2cf13f673d
770 changed files with 73394 additions and 0 deletions

View File

@@ -0,0 +1,144 @@
<template>
<view class="page_container">
<custom-nav-bar title="关于我们" />
<view class="logo_area">
<image src="@/static/images/about_logo.png" mode=""></image>
<view>{{ v }}</view>
<info-item
@click="show = true"
class="check"
title="上传调试日志"
content=""
/>
<u-modal showCancelButton :show="show" title="上传日志" @confirm="uploadLog" @cancel="show = false" >
<view class="slot-content">
<u--input
placeholder="日志数量"
border="surround"
v-model="line"
></u--input>
</view>
</u-modal>
</view>
</view>
</template>
<script>
import IMSDK from "openim-uniapp-polyfill";
import { version } from '@/common/config'
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import { PageEvents } from "@/constant";
import InfoItem from "../selfInfo/InfoItem.vue";
export default {
components: {
CustomNavBar,
InfoItem,
},
data() {
return {
show: false,
line: 10000,
version: "",
loading: false,
};
},
computed: {
v(){
return version
}
},
onLoad() {
this.getAppVersion();
uni.$on(PageEvents.CheckForUpdateResp, this.checkRespHandler);
},
onUnload() {
uni.$off(PageEvents.CheckForUpdateResp, this.checkRespHandler);
},
mounted() {
IMSDK.subscribe('uploadLogsProgress', this.uploadHandler);
},
beforeDestroy() {
IMSDK.unsubscribe('uploadLogsProgress', this.uploadHandler);
},
methods: {
uploadLog() {
this.show = false
IMSDK.asyncApi(
'uploadLogs',
IMSDK.uuid(),
{
line: this.line,
ex: ""
}
)
uni.showLoading({
title: '上传中',
mask: true,
});
},
uploadHandler({
data: { current, size },
}) {
console.log('uploadHandler',current,size)
if (current >= size) {
uni.hideLoading();
uni.showToast({
title: "上传成功",
icon: "none",
});
return;
}
},
getAppVersion() {
plus.runtime.getProperty(
plus.runtime.appid,
({ version }) => (this.appVersion = version),
);
},
updateCheck() {
this.loading = true;
uni.$emit(PageEvents.CheckForUpdate, true);
},
checkRespHandler() {
this.loading = false;
},
},
};
</script>
<style lang="scss">
.page_container {
background-color: #f8f8f8;
.logo_area {
display: flex;
flex-direction: column;
align-items: center;
margin: 24rpx 24rpx 0 24rpx;
background: #fff;
border-radius: 6px;
padding: 48rpx 0 16rpx 0;
color: $uni-text-color;
image {
width: 72px;
height: 72px;
margin-bottom: 24rpx;
}
}
.check {
margin-top: 26rpx;
border-top: 1px #e8eaef solid;
padding: 20rpx;
padding-bottom: 0;
width: 90%;
}
.btn_row {
padding: 0 44rpx;
}
}
</style>

View File

@@ -0,0 +1,52 @@
<template>
<view class="page_container">
<custom-nav-bar title="账号设置" />
<view class="info_wrap">
<setting-item @click="toBlockList" title="通讯录黑名单" :border="false" />
</view>
</view>
</template>
<script>
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import MyAvatar from "@/components/MyAvatar/index.vue";
import SettingItem from "@/components/SettingItem/index.vue";
export default {
components: {
CustomNavBar,
MyAvatar,
SettingItem,
},
data() {
return {
};
},
methods: {
toBlockList() {
uni.navigateTo({
url: "/pages/profile/blockList/index",
});
},
},
};
</script>
<style lang="scss" scoped>
.page_container {
background-color: #f8f8f8;
.info_wrap {
background-color: #fff;
margin: 24rpx 24rpx 0 24rpx;
border-radius: 6px;
.qr_icon {
width: 22px;
height: 23px;
}
}
}
</style>

View File

@@ -0,0 +1,117 @@
<template>
<view class="page_container">
<custom-nav-bar title="通讯录黑名单" />
<u-list v-if="blockList.length > 0" class="block_list" height="1">
<u-list-item v-for="item in blockList" :key="item.userID">
<user-item :item="item">
<view @click="tryRemove(item)" class="user_action" slot="action">
移除
</view>
</user-item>
</u-list-item>
</u-list>
<view v-else class="empty">
<image src="@/static/images/block_empty.png"></image>
<text class="empty_text">暂无黑名单</text>
</view>
<!-- <u-empty icon="/static/images/block_empty.png" v-else text="暂无黑名单" iconSize="20" /> -->
<u-modal
width="500rpx"
showCancelButton
:show="showComfirm"
@confirm="confirm"
@cancel="closeModal"
content="确定将用户移除黑名单吗?"
:asyncClose="true"
></u-modal>
</view>
</template>
<script>
import IMSDK from "openim-uniapp-polyfill";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import MyAvatar from "@/components/MyAvatar/index.vue";
import UserItem from "@/components/UserItem/index.vue";
export default {
components: {
CustomNavBar,
MyAvatar,
UserItem,
},
data() {
return {
showComfirm: false,
selectedUser: {},
};
},
computed: {
blockList() {
return this.$store.getters.storeBlackList;
},
},
methods: {
tryRemove(item) {
this.selectedUser = {
...item,
};
this.showComfirm = true;
},
confirm() {
IMSDK.asyncApi(
IMSDK.IMMethods.RemoveBlack,
IMSDK.uuid(),
this.selectedUser.userID,
)
.then(() => uni.$u.toast("移除成功"))
.catch(() => uni.$u.toast("移除失败"))
.finally(() => (this.showComfirm = false));
},
closeModal() {
this.showComfirm = false;
},
},
};
</script>
<style lang="scss" scoped>
.page_container {
@include colBox(false);
height: 100vh;
background-color: #f8f8f8;
.block_list {
flex: 1;
margin-top: 24rpx;
.user_item {
background-color: #fff;
}
.user_action {
position: absolute;
right: 44rpx;
font-size: 28rpx;
color: $uni-color-primary;
}
}
.empty {
@include centerBox();
flex-direction: column;
margin-top: 25vh !important;
&_text {
margin-top: 26rpx;
color: #8e9ab0;
}
image {
width: 237rpx;
height: 244rpx;
}
}
}
</style>

View File

@@ -0,0 +1,238 @@
<template>
<view class="page_container">
<view class="self_info_row"></view>
<view class="info_card">
<my-avatar :src="selfInfo.faceURL" :desc="selfInfo.nickname" size="46" />
<view class="id_row">
<text class="nickname">{{ selfInfo.nickname }}</text>
<view class="id_row_copy" @click="copy">
<text class="id">{{ selfInfo.userID }}</text>
<image
style="width: 32rpx; height: 32rpx"
src="@/static/images/id_copy.png"
mode=""
/>
</view>
</view>
<view class="qr" @click="toSelfQr">
<img src="static/images/self_info_qr.png" alt="" />
<img src="static/images/common_right.png" alt="" />
</view>
</view>
<view class="action_box">
<view
@click="profileMenuClick(item)"
class="profile_menu_item"
v-for="item in profileMenus"
:key="item.idx"
>
<view class="menu_left">
<image style="width: 48rpx; height: 48rpx" :src="item.icon" mode="" />
<text>{{ item.title }}</text>
</view>
<u-icon name="arrow-right" size="16" color="#999"></u-icon>
</view>
</view>
<u-toast ref="uToast"></u-toast>
</view>
</template>
<script>
import IMSDK from "openim-uniapp-polyfill";
import MyAvatar from "@/components/MyAvatar/index.vue";
export default {
components: {
MyAvatar,
},
data() {
return {
profileMenus: [
{
idx: 0,
title: "我的信息",
icon: require("static/images/profile_menu_info.png"),
},
{
idx: 2,
title: "账号设置",
icon: require("static/images/profile_menu_account.png"),
},
{
idx: 3,
title: "关于我们",
icon: require("static/images/profile_menu_about.png"),
},
{
idx: 4,
title: "退出登录",
icon: require("static/images/profile_menu_logout.png"),
},
],
};
},
computed: {
selfInfo() {
return this.$store.getters.storeSelfInfo;
},
},
methods: {
copy() {
uni.setClipboardData({
showToast: false,
data: this.selfInfo.userID,
success: function () {
uni.showToast({
icon: "none",
title: "复制成功",
});
},
});
},
logoutConfirm() {
IMSDK.asyncApi(IMSDK.IMMethods.Logout, IMSDK.uuid())
.then(() => {
uni.removeStorage({
key: "IMToken",
});
uni.removeStorage({
key: "BusinessToken",
});
})
.catch((err) => console.log(err))
.finally(() => {
uni.$u.route("/pages/login/index");
});
},
profileMenuClick({ idx }) {
switch (idx) {
case 0:
uni.navigateTo({
url: "/pages/profile/selfInfo/index",
});
break;
case 1:
uni.navigateTo({
url: "/pages/profile/messageNotification/index",
});
break;
case 2:
uni.navigateTo({
url: "/pages/profile/accountSetting/index",
});
break;
case 3:
uni.navigateTo({
url: "/pages/profile/about/index",
});
break;
case 4:
uni.showModal({
title: "提示",
content: "确定要退出当前账号吗?",
confirmText: "确认",
cancelText: "取消",
success: (res) => {
if (res.confirm) {
this.logoutConfirm();
}
},
});
break;
default:
break;
}
},
toSelfQr() {
uni.navigateTo({
url: `/pages/common/userOrGroupQrCode/index`,
});
},
},
};
</script>
<style lang="scss" scoped>
.page_container {
background-color: #f8f9fa;
.self_info_row {
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
width: 100%;
height: 276rpx;
background-image: url("@/static/images/profile_top_bg.png");
background-repeat: round;
}
.info_card {
width: 640rpx;
height: 196rpx;
border-radius: 6px;
background: #fff;
margin: -120rpx auto 0 auto;
padding: 0 36rpx;
color: #0c1c33;
display: flex;
align-items: center;
.id_row {
@include vCenterBox();
display: flex;
height: 46px;
margin-left: 16rpx;
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
flex: 1;
font-size: 28rpx;
&_copy {
@include vCenterBox();
}
.nickname {
@include nomalEllipsis();
max-width: 400rpx;
font-weight: 500;
font-size: 34rpx;
}
.id {
color: #8e9ab0;
}
}
img {
width: 18px;
height: 18px;
}
}
.action_box {
margin: 24rpx 24rpx 0 24rpx;
background: #fff;
border-radius: 6px;
}
.profile_menu_item {
@include btwBox();
padding: 24rpx 36rpx;
.menu_left {
@include vCenterBox();
color: $uni-text-color;
image {
margin-right: 24rpx;
}
}
}
}
</style>

View File

@@ -0,0 +1,74 @@
<template>
<view @click="clickItem" class="info_item">
<view class="left_label">
<text>{{ title }}</text>
</view>
<view class="right_value">
<slot name="value">
<text class="content">{{ content }}</text>
</slot>
<u-icon
v-if="showArrow"
name="arrow-right"
size="16"
color="#999"
></u-icon>
</view>
<u-loading-icon v-show="loading" class="loading_icon"></u-loading-icon>
</view>
</template>
<script>
export default {
name: "",
props: {
title: String,
content: String,
showArrow: {
type: Boolean,
default: true,
},
loading: {
type: Boolean,
default: false,
},
},
data() {
return {};
},
methods: {
clickItem() {
this.$emit("click");
},
},
};
</script>
<style lang="scss" scoped>
.info_item {
@include btwBox();
height: 82rpx;
padding: 0 44rpx;
color: $uni-text-color;
// border-bottom: 1px solid rgba(153,153,153,0.3);
position: relative;
.right_value {
@include vCenterBox();
.content {
font-size: 28rpx;
color: #999;
}
.u-icon {
margin-left: 12rpx;
}
}
.loading_icon {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
</style>

View File

@@ -0,0 +1,216 @@
<template>
<view class="page_container">
<custom-nav-bar title="个人资料" />
<view class="info_wrap">
<info-item
:loading="loadingState.faceURL"
@click="updateAvatar"
title="头像"
>
<my-avatar
:src="selfInfo.faceURL"
:desc="selfInfo.nickname"
size="30"
slot="value"
/>
</info-item>
<info-item
@click="updateNickname"
title="姓名"
:content="selfInfo.nickname"
/>
<info-item
:loading="loadingState.gender"
@click="updateGender"
title="性别"
:content="getGender"
/>
<info-item
:loading="loadingState.birth"
@click="() => (showDatePicker = true)"
title="生日"
:content="getBirth"
/>
</view>
<view class="info_wrap">
<info-item
:showArrow="false"
title="手机号码"
:content="selfInfo.phoneNumber || '-'"
/>
<info-item
:showArrow="false"
title="邮箱"
:content="selfInfo.email || '-'"
/>
</view>
<u-datetime-picker
:minDate="0"
:maxDate="nowDate"
:show="showDatePicker"
@confirm="confirmDate"
@cancel="() => (showDatePicker = false)"
v-model="selfInfo.birth"
mode="date"
/>
</view>
</template>
<script>
import { businessInfoUpdate } from "@/api/login";
import IMSDK from "openim-uniapp-polyfill";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import MyAvatar from "@/components/MyAvatar/index.vue";
import dayjs from "dayjs";
import InfoItem from "./InfoItem.vue";
import { getPurePath } from "@/util/common";
export default {
components: {
CustomNavBar,
MyAvatar,
InfoItem,
},
data() {
return {
showDatePicker: false,
loadingState: {
faceURL: false,
gender: false,
birth: false,
},
nowDate: Date.now(),
};
},
computed: {
selfInfo() {
return this.$store.getters.storeSelfInfo;
},
getGender() {
if (this.selfInfo.gender === 0) {
return "保密";
}
if (this.selfInfo.gender === 1) {
return "男";
}
return "女";
},
getBirth() {
const birth = this.selfInfo.birth ?? 0;
return dayjs(birth).format("YYYY-MM-DD");
},
},
methods: {
updateNickname() {
uni.navigateTo({
url: `/pages/common/markOrIDPage/index?isSelfNickname=true&sourceInfo=${JSON.stringify(
this.selfInfo,
)}`,
});
},
updateGender() {
uni.showActionSheet({
itemList: ["男", "女"],
success: async ({ tapIndex }) => {
this.loadingState.gender = true;
await this.updateSelfInfo(
{
gender: tapIndex + 1,
},
"gender",
);
},
});
},
updateAvatar() {
uni.chooseImage({
count: 1,
sizeType: ["compressed"],
success: async ({ tempFilePaths }) => {
const path = tempFilePaths[0];
const nameIdx = path.lastIndexOf("/") + 1;
const typeIdx = path.lastIndexOf(".") + 1;
const fileName = path.slice(nameIdx);
const fileType = path.slice(typeIdx);
this.loadingState.faceURL = true;
const {
data: { url },
} = await IMSDK.asyncApi(IMSDK.IMMethods.UploadFile, IMSDK.uuid(), {
filepath: getPurePath(tempFilePaths[0]),
name: fileName,
contentType: fileType,
uuid: IMSDK.uuid(),
});
console.log(url);
this.updateSelfInfo(
{
faceURL: url,
},
"faceURL",
);
this.loadingState.faceURL = false;
},
});
},
toQrCode() {
uni.navigateTo({
url: `/pages/common/userOrGroupQrCode/index`,
});
},
copyID() {
uni.setClipboardData({
data: this.selfInfo.userID,
success: () => {
uni.hideToast();
this.$nextTick(() => {
uni.$u.toast("复制成功");
});
},
});
},
async updateSelfInfo(data, key) {
try {
await businessInfoUpdate({
userID: this.selfInfo.userID,
...data,
});
await this.$store.dispatch("user/updateBusinessInfo");
uni.$u.toast("修改成功");
} catch (e) {
console.log(e);
uni.$u.toast("修改失败");
}
this.loadingState[key] = false;
},
confirmDate({ value }) {
this.loadingState.birth = true;
this.updateSelfInfo(
{
birth: value,
},
"birth",
);
this.showDatePicker = false;
},
},
};
</script>
<style lang="scss" scoped>
.page_container {
background-color: #f8f8f8;
.info_wrap {
margin: 24rpx 24rpx 0 24rpx;
background: #fff;
border-radius: 6px;
.qr_icon {
width: 22px;
height: 23px;
}
}
}
</style>