wujingjing
2025-03-21 197e3949af92e687e9e06bd2daf539b6b665d06d
微信绑定
已修改5个文件
已添加1个文件
487 ■■■■■ 文件已修改
customer_list/yw/static/config/route.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/login/UserMenuData.ts 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/hooks/useLogin.ts 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/navBars/breadcrumb/user.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/global.ts 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/project/yw/systemManage/personalCenter/PersonalCenter.vue 425 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
customer_list/yw/static/config/route.js
@@ -106,6 +106,13 @@
        component: '/project/yw/systemManage/replaceWordMgr/ReplaceWordMgr.vue',
    },
    {
        name: 'PersonalCenter',
        isKeepAlive: true,
        isAffix: false,
        path: '/personalCenter',
        component: '/project/yw/systemManage/personalCenter/PersonalCenter.vue',
    },
    {
        name: 'AmisEditor',
        isKeepAlive: true,
        isAffix: false,
src/api/login/UserMenuData.ts
@@ -456,6 +456,23 @@
                SortCode: 2,
                Description: '',
            },
            {
                Children: [],
                ID: '1135',
                ParentID: '3',
                Type: 2,
                Name: '个人中心',
                Path: '/personalCenter',
                Permission: '',
                Icon: 'ywifont ywicon-wode',
                IsIframe: false,
                OutLink: '',
                IsHide: true,
                Weight: 0,
                SortCode: 2,
                Description: '',
            },
            {
                Children: [],
                ID: '333-2',
src/hooks/useLogin.ts
@@ -69,8 +69,7 @@
        const currentTime = formatAxis(new Date());
        Local.set(accessSessionKey, res.hswatersession);
        await useUserInfo().setUserInfos({
            userName: res.name,
            phoneNumber: res.phone,
            ...(res ?? {}),
            photo: profileMan,
        }); //缓存用户信息
        // state.loading.signIn = true;
src/layout/navBars/breadcrumb/user.vue
@@ -63,7 +63,7 @@
        <el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
            <span class="layout-navbars-breadcrumb-user-link">
                <img :src="userInfos.photo" class="layout-navbars-breadcrumb-user-link-photo mr5" />
                {{ userInfos.userName === '' ? 'common' : userInfos.userName }}
                {{ userInfos.name === '' ? 'common' : userInfos.name }}
                <el-icon class="el-icon--right">
                    <ele-ArrowDown />
                </el-icon>
@@ -75,7 +75,7 @@
                    <el-dropdown-item command="/404">{{ $t('message.user.dropdown3') }}</el-dropdown-item>
                    <el-dropdown-item command="/401">{{ $t('message.user.dropdown4') }}</el-dropdown-item> -->
                    <!-- <el-dropdown-item command="/auth/personalCenter">{{ $t('message.user.dropdown2') }}</el-dropdown-item> -->
                    <el-dropdown-item command="/personalCenter">{{ $t('message.user.dropdown2') }}</el-dropdown-item>
                    <el-dropdown-item divided command="logOut">{{ $t('message.user.dropdown5') }}</el-dropdown-item>
                </el-dropdown-menu>
            </template>
src/utils/global.ts
@@ -56,7 +56,7 @@
            value: res?.json_msg ?? '登录失败,请检查是否已绑定微信',
        });
        window.location.href = SERVE_URL;
        // window.location.href = SERVE_URL;
    }
};
@@ -65,23 +65,42 @@
    const res = await userBindingWechat({
        weixin_code: wxcode,
        user_name: userInfo.userName,
        user_name: userInfo.name,
    });
    const reloadPage = () => {
        const url = window.location.href;
        const [baseUrl, hash] = url.split('#/');
        const [path, search] = hash.split('?');
        if (search) {
            const searchParams = new URLSearchParams(search);
            searchParams.delete('isWxLogin');
            searchParams.delete('wxcode');
            searchParams.delete('wxstate');
            const newSearch = searchParams.toString();
            const newUrl = `${baseUrl}#/${path}${newSearch ? '?' + newSearch : ''}`;
            window.history.replaceState({}, '', newUrl);
            window.location.reload();
        }
    };
    if (res?.json_ok) {
        ElMessage.success('绑定成功');
        const userInfo = Local.get(userInfoKey);
        Local.set(userInfoKey, {
            ...userInfo,
            isBindWechat: true,
            wechatNickname: res.json_url,
            /** @description éšä¾¿è®¾ç½®ä¸€ä¸ªå€¼ï¼Œè¡¨ç¤ºå·²ç»ç»‘定了 */
            weixin_openid: 'sdfdf',
            weixin_nickname: res.json_url,
        });
        setTimeout(() => {
            window.location.href = SERVE_URL;
            reloadPage();
        }, 700);
    } else {
        ElMessage.error(res?.json_msg ?? '绑定失败');
        setTimeout(() => {
            window.location.href = SERVE_URL;
            reloadPage();
        }, 2000);
    }
};
src/views/project/yw/systemManage/personalCenter/PersonalCenter.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,425 @@
<template>
    <div class="sys-userCenter-container h100">
        <el-row :gutter="8" style="width: 100%" class="h100">
            <el-col :span="8" :xs="24" class="h100">
                <el-card shadow="hover" class="h100">
                    <div class="account-center-avatarHolder">
                        <el-avatar
                            :size="100"
                            :src="userInfos.photo"
                            @click="openCropperDialog"
                            v-loading="state.avatarLoading"
                            element-loading-spinner="el-icon-Upload"
                            element-loading-background="rgba(0, 0, 0, 0.2)"
                            @mouseenter="mouseEnterAvatar"
                            @mouseleave="mouseLeaveAvatar"
                        />
                        <div class="username">{{ userInfos.realName }}</div>
                    </div>
                    <div class="account-center-org" style="display: flex; flex-direction: column; align-items: center">
                        <div>
                            <p class="user-simple-info">
                                <!-- ç”¨æˆ·å -->
                                <el-icon size="17" class="mr10"><ele-User /></el-icon> <span>{{ userInfos?.name ?? '---' }}</span>
                            </p>
                            <!-- å…¬å¸å -->
                            <!-- <p class="user-simple-info">
                                <el-icon size="17"><ele-Briefcase /></el-icon> <span>{{ userInfos.SoftWare?.Project?.Corpration?.Name ?? '---' }}</span>
                            </p> -->
                            <p class="user-simple-info" v-if="userInfos?.weixin_nikename">
                                <!-- è´¦æˆ·ç±»åž‹å -->
                                <span class="ywifont ywicon-weixin mr10"></span>
                                <span>{{ userInfos?.weixin_nikename ?? '---' }}</span>
                            </p>
                        </div>
                    </div>
                </el-card>
            </el-col>
            <el-col :span="16" :xs="24" class="h100" v-loading="state.loading">
                <el-card shadow="hover" class="h100">
                    <el-tabs>
                        <el-tab-pane label="基础信息">
                            <el-form :model="state.ruleFormBase" ref="ruleFormBaseRef" label-width="80px">
                                <el-row :gutter="35">
                                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
                                        <el-form-item label="用户ID">
                                            <el-input :model-value="userInfos?.name" placeholder="用户ID" />
                                        </el-form-item>
                                    </el-col>
                                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
                                        <el-form-item label="姓名">
                                            <el-input :model-value="userInfos?.real_name" placeholder="姓名" />
                                        </el-form-item>
                                    </el-col>
                                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
                                        <el-form-item label="性别">
                                            <el-input :model-value="userInfos?.sex" placeholder="性别" />
                                        </el-form-item>
                                    </el-col>
                                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
                                        <el-form-item label="部门">
                                            <el-input :model-value="userInfos?.part" placeholder="部门" />
                                        </el-form-item>
                                    </el-col>
                                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
                                        <el-form-item label="电话">
                                            <el-input :model-value="userInfos?.phone" placeholder="电话" />
                                        </el-form-item>
                                    </el-col>
                                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
                                        <el-form-item label="邮箱">
                                            <el-input :model-value="userInfos?.email" placeholder="邮箱" />
                                        </el-form-item>
                                    </el-col>
                                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
                                        <el-form-item label="微信昵称">
                                            <el-input :model-value="userInfos?.weixin_nikename" placeholder="微信昵称" />
                                        </el-form-item>
                                    </el-col>
                                    <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
                                        <el-form-item label="微信绑定" prop="wechat">
                                            <div id="wechat-bind">
                                                <div v-if="bindingCodeShow" class="flex flex-col items-center justify-center">
                                                    <iframe
                                                        ref="wechatQrRef"
                                                        sandbox="allow-top-navigation allow-scripts"
                                                        style="width: 200px; height: 170px; overflow: hidden"
                                                        frameborder="0"
                                                    ></iframe>
                                                    <span>使用微信扫一扫绑定</span>
                                                </div>
                                                <template v-else>
                                                    <div v-if="userInformationInfo.weixin_openid" class="flex items-center">
                                                        <span>已绑定</span>
                                                        <el-button link type="primary" @click="openWechatLogin">重新绑定</el-button>
                                                    </div>
                                                    <div v-else class="flex items-center">
                                                        <span>未绑定</span>
                                                        <el-button link type="primary" @click="openWechatLogin">点击绑定</el-button>
                                                    </div>
                                                </template>
                                            </div>
                                        </el-form-item>
                                    </el-col>
                                </el-row>
                            </el-form>
                        </el-tab-pane>
                        <el-tab-pane label="修改密码">
                            <el-form ref="ruleFormPasswordRef" :model="state.ruleFormPassword" label-width="80px">
                                <el-form-item
                                    label="新密码"
                                    prop="passwordNew"
                                    :rules="[{ required: true, message: '新密码不能为空', trigger: 'blur' }]"
                                >
                                    <el-input
                                        v-model="state.ruleFormPassword.passwordNew"
                                        :type="state.showPasswdNew ? 'text' : 'password'"
                                        autocomplete="off"
                                    >
                                        <template #suffix>
                                            <i
                                                class="iconfont el-input__icon login-content-password"
                                                :class="state.showPasswdNew ? 'icon-yincangmima' : 'icon-xianshimima'"
                                                @click="state.showPasswdNew = !state.showPasswdNew"
                                            >
                                            </i>
                                        </template>
                                    </el-input>
                                </el-form-item>
                                <el-form-item
                                    label="确认密码"
                                    prop="passwordNew2"
                                    :rules="[{ validator: validatePassword, required: true, trigger: 'blur' }]"
                                >
                                    <el-input :type="state.showPassWdNew2 ? 'text' : 'password'" v-model="state.passwordNew2" autocomplete="off">
                                        <template #suffix>
                                            <i
                                                class="iconfont el-input__icon login-content-password"
                                                :class="state.showPassWdNew2 ? 'icon-yincangmima' : 'icon-xianshimima'"
                                                @click="state.showPassWdNew2 = !state.showPassWdNew2"
                                            >
                                            </i>
                                        </template>
                                    </el-input>
                                </el-form-item>
                                <el-form-item>
                                    <el-button icon="ele-Refresh" @click="resetPassword">重 ç½®</el-button>
                                    <el-button icon="ele-SuccessFilled" type="primary" @click="submitPassword">ç¡® å®š</el-button>
                                </el-form-item>
                            </el-form>
                        </el-tab-pane>
                    </el-tabs>
                </el-card>
            </el-col>
        </el-row>
    </div>
</template>
<script lang="ts" setup name="sysUserCenter">
import { onMounted, watch, reactive, ref, nextTick } from 'vue';
import { storeToRefs } from 'pinia';
import { ElForm, ElMessage, ElMessageBox, genFileId } from 'element-plus';
import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
import { useUserInfo } from '/@/stores/userInfo';
// import { base64ToFile } from '/@/utils/base64Conver';
// import CropperDialog from '/@/components/cropper/index.vue';
import { ADMIN_TYPE_MAP } from '/@/views/types';
import { clearAccessTokens, userInfoKey } from '/@/utils/request';
import { ResetSystemLoginPwd, UpdateSystemLoginPwd } from '/@/api/auth/userManage';
import { SERVE_URL } from '/@/constants';
import { Local } from '/@/utils/storage';
const stores = useUserInfo();
const { userInfos } = storeToRefs(stores);
const uploadSignRef = ref<UploadInstance>();
//const uploadAvatarRef = ref<UploadInstance>();
const ruleFormBaseRef = ref<InstanceType<typeof ElForm>>();
const ruleFormPasswordRef = ref<InstanceType<typeof ElForm>>();
const state = reactive({
    loading: false,
    avatarLoading: false,
    signDialogVisible: false,
    ruleFormBase: {},
    ruleFormPassword: {} as any,
    showPasswdNew: false,
    showPassWdNew2: false,
    signFileList: [] as any,
    passwordNew2: '',
    cropperTitle: '',
});
const userInformationInfo = ref({}) as any;
onMounted(async () => {
    state.loading = true;
    userInformationInfo.value = Local.get(userInfoKey);
    // var res = await getAPI(SysUserApi).apiSysUserBaseInfoGet();
    // state.ruleFormBase = res.data.result ?? { account: '' };
    state.loading = false;
});
// ä¸Šä¼ å¤´åƒå›¾ç‰‡
const uploadCropperImg = async (e: any) => {
    // var res = await getAPI(SysFileApi).apiSysFileUploadAvatarPostForm(e.img);
    // userInfos.value.avatar = res.data.result?.filePath + '/' + res.data.result?.name;
};
// èŽ·å¾—ç”µå­ç­¾åæ–‡ä»¶åˆ—è¡¨
const handleChangeSignFile = (_file: any, fileList: []) => {
    state.signFileList = fileList;
};
// å¯†ç éªŒè¯
const validatePassword = (_rule: any, value: any, callback: any) => {
    if (!state.ruleFormPassword.passwordNew) {
        callback('请先输入新密码!');
    } else if (state.passwordNew2 != state.ruleFormPassword.passwordNew) {
        callback(new Error('两次密码不一致!'));
    } else {
        callback();
    }
};
// å¯†ç é‡ç½®
const resetPassword = () => {
    ElMessageBox.confirm('确定重置当前用户密码?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
    })
        .then(async () => {
            resetSystemLoginPwd();
        })
        .catch(() => {});
};
const confirmReLogin = () => {
    // é€€å‡ºç³»ç»Ÿ
    ElMessageBox.confirm('密码已修改,是否重新登录系统?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
    })
        .then(async () => {
            clearAccessTokens();
        })
        .catch(() => {});
};
const resetSystemLoginPwd = async () => {
    const res = await ResetSystemLoginPwd({
        ID: userInfos.value?.User?.LoginAccount?.ID,
    });
    if (res?.Code === 0) {
        if (res.Data) {
            confirmReLogin();
        } else {
            ElMessage.error('重置密码失败');
        }
    } else {
        ElMessage.error('重置密码失败' + (res?.Message ? `,${JSON.stringify(res.Message)}` : ''));
    }
};
// å¯†ç æäº¤
const submitPassword = () => {
    ruleFormPasswordRef.value?.validate(async (valid: boolean) => {
        if (!valid) return;
        ElMessageBox.confirm('确定修改当前账户密码?', '提示', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning',
        })
            .then(async () => {
                const res = await UpdateSystemLoginPwd({
                    ID: userInfos.value?.User?.LoginAccount?.ID,
                    LoginPwd: state.passwordNew2,
                });
                if (res?.Code === 0) {
                    if (res.Data) {
                        confirmReLogin();
                    } else {
                        ElMessage.error('修改密码失败');
                    }
                } else {
                    ElMessage.error('修改密码失败' + (res?.Message ? `,${JSON.stringify(res.Message)}` : ''));
                }
            })
            .catch(() => {});
    });
};
const bindingCodeShow = ref(false);
const wechatQrRef = ref<HTMLIFrameElement>();
const openWechatLogin = async () => {
    bindingCodeShow.value = true;
    await nextTick();
    if (!wechatQrRef.value) return;
    const url = `${SERVE_URL}JJJHHH/home?isWxLogin=N`;
    const appid = 'wx4ea2dca37170074c';
    const state = (new Date().getTime() / 1000).toString();
    const base64 = btoa(`
    .impowerBox .title {display:none;}
    .impowerBox .status.status_browser  p:nth-of-type(2){
        display: none;
    }
    .impowerBox .qrcode {
    width: 160px;
    margin-top:0;
    }
    .info{
        display: none;
    }
    .impowerBox .status{
    }
    #tpl_for_iframe{
        height:100%;
        overflow: hidden;
            display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
    }
    `);
    const wechatAuthUrl = `https://open.weixin.qq.com/connect/qrconnect?appid=${appid}&redirect_uri=${encodeURIComponent(
        `http://apiv3.xpump.net/User/wxUserLoginCB.html?from=wi&url=${url}`
    )}&response_type=code&scope=snsapi_login&state=${state}&href=data:text/css;base64,${base64}#wechat_redirect`;
    wechatQrRef.value.src = wechatAuthUrl;
};
// æ‰“开裁剪弹窗
const openCropperDialog = () => {
    // state.cropperTitle = '更换头像';
    // cropperDialogRef.value?.openDialog(userInfos.value.avatar);
};
// é¼ æ ‡è¿›å…¥å’Œç¦»å¼€å¤´åƒæ—¶
const mouseEnterAvatar = () => {
    state.avatarLoading = true;
};
const mouseLeaveAvatar = () => {
    state.avatarLoading = false;
};
// å¯¼å‡ºå¯¹è±¡
defineExpose({ handleChangeSignFile });
</script>
<style lang="scss" scoped>
.login-content-password {
    display: inline-block;
    width: 20px;
    cursor: pointer;
    &:hover {
        color: #909399;
    }
}
.user-simple-info {
    display: flex;
    align-items: flex-start;
}
.account-center-avatarHolder {
    text-align: center;
    margin-bottom: 24px;
    .username {
        font-size: 20px;
        line-height: 28px;
        font-weight: 500;
        margin-bottom: 4px;
    }
}
.account-center-org {
    margin-bottom: 8px;
    position: relative;
    p {
        margin-top: 10px;
    }
    span {
        // padding-left: 17px;
    }
}
.avatar {
    margin: 0 auto;
    width: 104px;
    height: 104px;
    margin-bottom: 20px;
    border-radius: 50%;
    overflow: hidden;
    img {
        height: 100%;
        width: 100%;
    }
}
.image-signature {
    margin-top: 20px;
    margin-bottom: 10px;
    width: 100%;
    height: 150px;
    background-color: #fff;
    text-align: center;
    vertical-align: middle;
    border: solid 1px var(--el-border-color);
}
</style>