Files
lilishop-ui/buyer/src/pages/home/memberCenter/AccountSafe.vue
lifenlong 88fc1b0b2a refactor: 四端完成 Options API → script setup + vuex → pinia 迁移
- buyer(95) / seller(169) / im(53) / manager(280) 共 597 个组件全部从 Options API 转换为 <script setup>
- 四端 vuex 全部移除,状态管理统一迁移至 pinia 4(stores/ 目录)
- util.js 等基础设施去除 vm 首参依赖;全局属性($Message/$filters 等)在 script 内改为具名导入
- 被外部 ref 访问的公共组件均已补齐 defineExpose(清单见各端 MIGRATION-VUE3.md)
- 各端 eslint 启用 vue/component-api-style 防止新代码回退 Options API
- 四端 yarn build 全部通过;迁移约定与注意事项已更新至各端 MIGRATION-VUE3.md
2026-10-13 07:06:59 +08:00

402 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="wrapper">
<UserCenterLayout>
<div class="safe-list">
<div class="safe-card">
<div class="safe-icon">
<el-icon :size="22"><Key /></el-icon>
</div>
<div class="safe-body">
<p class="safe-title">登录密码</p>
<p class="safe-desc">互联网账号存在被盗风险,建议您定期更改密码以保护账户安全。</p>
</div>
<el-button @click="modifyPwd">修改密码</el-button>
</div>
<div class="safe-card safe-card-connect">
<div class="safe-card-main">
<div class="safe-icon">
<el-icon :size="22"><Link /></el-icon>
</div>
<div class="safe-body">
<p class="safe-title">第三方账户绑定</p>
<p class="safe-desc">绑定后可使用第三方账号快捷登录。</p>
</div>
<el-button v-if="!isBound(wechatConnect.type)" type="primary" @click="openBindModal(wechatConnect)">绑定</el-button>
<el-button v-else @click="confirmUnbind(wechatConnect)">解绑</el-button>
</div>
<div class="connect-row">
<span class="connect-name">{{ wechatConnect.label }}</span>
<span class="connect-status" :class="{ 'is-bound': isBound(wechatConnect.type) }">
{{ isBound(wechatConnect.type) ? "已绑定" : "未绑定" }}
</span>
<el-skeleton v-if="connectLoading" animated class="connect-skeleton" />
</div>
</div>
</div>
</UserCenterLayout>
<el-dialog
v-model="bindModalVisible"
title="绑定第三方账户"
:close-on-click-modal="false"
@closed="resetBindForm"
>
<el-form label-width="110px">
<el-form-item label="类型">
<el-input v-model="bindForm.typeLabel" disabled />
</el-form-item>
<el-form-item v-if="bindForm.type === 'WECHAT'" label="公众号二维码">
<div class="wechatQrBox">
<div class="wechatQrImgBox">
<el-skeleton fix v-if="qrCodeLoading"></el-skeleton>
<img v-if="qrCodeSrc" class="wechatQrImg" :src="qrCodeSrc" alt="微信公众号二维码" />
<div v-else class="wechatQrEmpty">二维码获取失败,请刷新重试</div>
</div>
<div class="wechatQrActions">
<el-button :loading="qrCodeLoading" @click="fetchWechatQrCode">刷新二维码</el-button>
<el-button type="primary" :loading="bindSubmitLoading" @click="submitBind">我已完成绑定</el-button>
</div>
<div class="wechatQrTip">请使用微信扫码关注/授权,完成后点击“我已完成绑定”检测绑定状态</div>
</div>
</el-form-item>
<el-form-item v-if="bindForm.type !== 'WECHAT'" label="OpenID/UnionID">
<el-input v-model="bindForm.unionID" placeholder="请输入 OpenID/UnionID" />
</el-form-item>
</el-form>
<template v-if="bindForm.type !== 'WECHAT'" #footer>
<el-button @click="bindModalVisible = false">取消</el-button>
<el-button type="primary" :loading="bindSubmitLoading" @click="submitBind">
{{ bindModalOkText }}
</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { computed, nextTick, onMounted, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { Message, Modal } from "@/utils/message";
import { Key, Link } from '@element-plus/icons-vue';
// import { getPwdStatus } from '@/api/account';
import storage from '@/plugins/storage'
import { bindThirdAccount, getThirdAccountBindList, getWechatH5QrCode, unbindThirdAccount } from '@/api/login.js'
defineOptions({ name: 'AccountSafe' });
const router = useRouter();
const pwdStatus = ref('');
const connectLoading = ref(false);
const bindModalVisible = ref(false);
const bindSubmitLoading = ref(false);
const connectBoundTypes = ref([]);
const thirdAccountTypes = ref([
{ type: 'WECHAT', label: '微信' }
]);
const qrCodeLoading = ref(false);
const qrCodeBase64 = ref('');
const bindForm = reactive({
type: '',
typeLabel: '',
unionID: ''
});
const wechatConnect = computed(() => thirdAccountTypes.value[0]);
const qrCodeSrc = computed(() => {
if (!qrCodeBase64.value) return ''
if (qrCodeBase64.value.startsWith('data:image')) return qrCodeBase64.value
return `data:image/png;base64,${qrCodeBase64.value}`
});
const bindModalOkText = computed(() => {
if (bindForm.type === 'WECHAT') return '我已完成绑定'
return '确定'
});
// 设置支付密码
function goModifyPwd () {
router.push({name: 'ModifyPwd', query: { status: 2 }})
}
function modifyPwd () { // 修改密码
router.push({name: 'ModifyPwd', query: { status: 1 }})
}
// 获取密码状态
function getPwdStatus () {}
// function getPwdStatus () {
// getPwdStatus().then(res => {
// if (res) {
// pwdStatus.value = '修改密码'
// } else {
// pwdStatus.value = '设置密码'
// }
// });
// }
function isBound (type) {
return connectBoundTypes.value.includes(type)
}
function refreshConnectBinds () {
connectLoading.value = true
return getThirdAccountBindList()
.then(res => {
if (res && res.success) {
connectBoundTypes.value = Array.isArray(res.result) ? res.result : []
}
})
.finally(() => {
connectLoading.value = false
})
}
function openBindModal (item) {
bindForm.type = item.type
bindForm.typeLabel = item.label
bindForm.unionID = ''
qrCodeBase64.value = ''
bindModalVisible.value = true
if (item.type === 'WECHAT') {
fetchWechatQrCode()
}
}
function resetBindForm () {
bindForm.type = ''
bindForm.typeLabel = ''
bindForm.unionID = ''
qrCodeLoading.value = false
qrCodeBase64.value = ''
bindSubmitLoading.value = false
}
function fetchWechatQrCode () {
if (bindForm.type !== 'WECHAT') return
const rawUserInfo = storage.getItem('userInfo')
const userInfo = rawUserInfo ? JSON.parse(rawUserInfo) : null
const userId = userInfo && userInfo.id ? String(userInfo.id) : ''
if (!userId) {
qrCodeBase64.value = ''
Message.error('未获取到当前用户ID,请重新登录后重试')
return
}
qrCodeLoading.value = true
getWechatH5QrCode({ scene: userId })
.then(res => {
if (res && res.success) {
qrCodeBase64.value = res.result || ''
return
}
qrCodeBase64.value = ''
Message.error((res && res.message) || '二维码获取失败')
})
.catch(() => {
qrCodeBase64.value = ''
})
.finally(() => {
qrCodeLoading.value = false
})
}
function submitBind () {
if (bindSubmitLoading.value) return
if (bindForm.type === 'WECHAT' && !bindForm.unionID) {
bindSubmitLoading.value = true
refreshConnectBinds()
.then(() => {
if (isBound(bindForm.type)) {
Message.success('绑定成功')
bindModalVisible.value = false
resetBindForm()
return
}
Message.warning('暂未检测到绑定,请扫码后重试')
nextTick(() => {
bindModalVisible.value = true
})
})
.finally(() => {
bindSubmitLoading.value = false
})
return
}
if (!bindForm.type) {
Message.error('绑定类型不能为空')
return
}
if (!bindForm.unionID) {
Message.error('OpenID/UnionID 不能为空')
nextTick(() => {
bindModalVisible.value = true
})
return
}
bindSubmitLoading.value = true
bindThirdAccount({ unionID: bindForm.unionID, type: bindForm.type })
.then(() => {
Message.success('绑定成功')
bindModalVisible.value = false
resetBindForm()
refreshConnectBinds()
})
.finally(() => {
bindSubmitLoading.value = false
})
}
function confirmUnbind (item) {
Modal.confirm({
title: '解绑确认',
content: `<p>确定解绑 ${item.label} 吗?</p>`,
onOk: () => {
unbindThirdAccount({ type: item.type }).then(() => {
Message.success('解绑成功')
refreshConnectBinds()
})
}
})
}
onMounted(() => {
getPwdStatus()
refreshConnectBinds()
});
</script>
<style scoped lang="scss">
.safe-list {
display: flex;
flex-direction: column;
gap: 12px;
padding-top: 4px;
}
.safe-card,
.safe-card-main {
display: flex;
align-items: center;
gap: 16px;
}
.safe-card {
padding: 20px;
border: 1px solid #f0f0f0;
border-radius: 8px;
}
.safe-card-connect {
display: block;
padding: 0;
}
.safe-card-main {
padding: 20px 20px 16px;
}
.safe-icon {
flex-shrink: 0;
width: 48px;
height: 48px;
border-radius: 8px;
background: #fff5f4;
color: $theme_color;
display: flex;
align-items: center;
justify-content: center;
}
.safe-body {
flex: 1;
min-width: 0;
}
.safe-title {
margin: 0;
color: #171A26;
font-size: 16px;
font-weight: 600;
line-height: 24px;
}
.safe-desc {
margin: 6px 0 0;
color: #999;
font-size: 13px;
line-height: 20px;
}
.connect-row {
position: relative;
display: flex;
align-items: center;
gap: 10px;
margin: 0 20px 16px 84px;
padding-top: 12px;
border-top: 1px solid #f5f5f5;
}
.connect-name {
color: #171A26;
font-size: 14px;
line-height: 22px;
}
.connect-status {
height: 22px;
padding: 0 8px;
border-radius: 4px;
background: #f5f5f5;
color: #999;
font-size: 12px;
line-height: 22px;
}
.connect-status.is-bound {
background: #f0faf3;
color: #1f9d55;
}
.connect-skeleton {
width: 120px;
height: 22px;
}
.wechatQrBox {
width: 100%;
}
.wechatQrImgBox {
position: relative;
width: 180px;
height: 180px;
border: 1px solid $border_color;
display: flex;
align-items: center;
justify-content: center;
background: #fff;
}
.wechatQrImg {
width: 168px;
height: 168px;
object-fit: contain;
}
.wechatQrEmpty {
color: $light_sub_color;
font-size: 14px;
text-align: center;
padding: 0 8px;
}
.wechatQrActions {
margin-top: 10px;
display: flex;
gap: 10px;
}
.wechatQrTip {
margin-top: 10px;
color: $light_sub_color;
font-size: 14px;
line-height: 1.6;
}
</style>