wip:个人中心&代理用户详情页初版
This commit is contained in:
parent
0e195a5434
commit
3a4f3f1df1
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { request } from '@/api';
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
export async function getUserInfoApi() {
|
||||||
|
return request.get('/user/getUserInfo');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改语言类型
|
||||||
|
export async function editLangTypeApi(lang_type: number) {
|
||||||
|
return request.post('/user/editLangType', { lang_type });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定手机号
|
||||||
|
export async function bindMobileApi(data: { mobile: string; code: string }) {
|
||||||
|
return request.post('/user/bindMobile', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定邮箱
|
||||||
|
export async function bindEmailApi(data: { email: string; code: string }) {
|
||||||
|
return request.post('/user/bindEmail', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改用户名
|
||||||
|
export async function editUsernameApi(data: { user_id: number; username: string }) {
|
||||||
|
return request.post('/user/editUser', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 短信验证码修改密码
|
||||||
|
// 验证码修改密码(统一接口)
|
||||||
|
export async function updatePwdByCaptchaApi(data: {
|
||||||
|
reg_type: number;
|
||||||
|
email?: string;
|
||||||
|
mobile?: string;
|
||||||
|
code: string;
|
||||||
|
login_password1: string;
|
||||||
|
login_password2: string;
|
||||||
|
}) {
|
||||||
|
return request.post('/user/editLoginPass', data);
|
||||||
|
}
|
||||||
|
|
@ -176,6 +176,7 @@ return {
|
||||||
sideMenu,
|
sideMenu,
|
||||||
// 方法(箭头函数)
|
// 方法(箭头函数)
|
||||||
login,
|
login,
|
||||||
|
setUserInfo,
|
||||||
register,
|
register,
|
||||||
logout,
|
logout,
|
||||||
forgotPassword,
|
forgotPassword,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,322 @@
|
||||||
|
<template>
|
||||||
|
<el-drawer v-model="innerVisible" :title="drawerTitle" direction="rtl" size="80%" header-align="center">
|
||||||
|
<div class="drawer-content">
|
||||||
|
<!-- 顶部客户信息卡片 -->
|
||||||
|
<div class="user-top" v-loading="loading">
|
||||||
|
<div class="left-card">
|
||||||
|
|
||||||
|
<div class="avatar-wrapper">
|
||||||
|
<el-icon class="avatar-icon">
|
||||||
|
<UserFilled />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="left-text">
|
||||||
|
<p>{{ userInfo.name }}</p>
|
||||||
|
<p>ID:{{ userInfo.id }}</p>
|
||||||
|
<p>{{ userInfo.role }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="right-card">
|
||||||
|
<div class="item">
|
||||||
|
<div class="item-label">
|
||||||
|
<el-icon>
|
||||||
|
<Phone />
|
||||||
|
</el-icon>
|
||||||
|
<span>手机号</span>
|
||||||
|
</div>
|
||||||
|
<div class="item-value">{{ userInfo.phone || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="item-label">
|
||||||
|
<el-icon>
|
||||||
|
<CreditCard />
|
||||||
|
</el-icon>
|
||||||
|
<span>账户余额</span>
|
||||||
|
</div>
|
||||||
|
<div class="item-value text-highlight">{{ userInfo.balance || '0' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="item-label">
|
||||||
|
<el-icon>
|
||||||
|
<Money />
|
||||||
|
</el-icon>
|
||||||
|
<span>存款金额</span>
|
||||||
|
</div>
|
||||||
|
<div class="item-value text-highlight">{{ userInfo.deposit || '0' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="item-label">
|
||||||
|
<el-icon>
|
||||||
|
<Message />
|
||||||
|
</el-icon>
|
||||||
|
<span>邮箱</span>
|
||||||
|
</div>
|
||||||
|
<div class="item-value">{{ userInfo.email || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="item-label">
|
||||||
|
<el-icon>
|
||||||
|
<Clock />
|
||||||
|
</el-icon>
|
||||||
|
<span>注册时间</span>
|
||||||
|
</div>
|
||||||
|
<div class="item-value">{{ userInfo.registerTime || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="item-label">
|
||||||
|
<el-icon>
|
||||||
|
<Wallet />
|
||||||
|
</el-icon>
|
||||||
|
<span>历史交易金额</span>
|
||||||
|
</div>
|
||||||
|
<div class="item-value">{{ userInfo.hisMeny || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 账户表格 -->
|
||||||
|
<div class="table-container" v-loading="tableLoading">
|
||||||
|
<CustomerListTable :table-data="tableData" :columns="tableColumnsOptions" row-key="tradeAccount"
|
||||||
|
:default-sort="{ prop: 'addTime', order: 'descending' }">
|
||||||
|
</CustomerListTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch, computed } from 'vue'
|
||||||
|
import { ElAvatar, ElIcon, ElDrawer, ElButton, ElMessage } from 'element-plus'
|
||||||
|
import { UserFilled, Phone, Message, Money, Wallet, Clock, CreditCard } from '@element-plus/icons-vue'
|
||||||
|
import CustomerListTable from '@/components/common/Table.vue'
|
||||||
|
import { getCustomerDetailApi } from '@/api/modules/agent/customerDetail'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
const roleMap: { [key: number]: string } = {
|
||||||
|
1: '平台',
|
||||||
|
2: '承包商',
|
||||||
|
3: '特殊做市商',
|
||||||
|
4: '普通做市商',
|
||||||
|
5: '代理',
|
||||||
|
6: '用户',
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接收父组件传值
|
||||||
|
const props = defineProps<{
|
||||||
|
drawerVisible: boolean
|
||||||
|
queryId: string | number
|
||||||
|
drawerTitle: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// 抛出事件
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:drawerVisible': [value: boolean]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// =============== 修复核心:computed 中转 v-model ===============
|
||||||
|
const innerVisible = computed({
|
||||||
|
get() {
|
||||||
|
return props.drawerVisible
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
emit('update:drawerVisible', val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 状态
|
||||||
|
const loading = ref(false)
|
||||||
|
const tableLoading = ref(false)
|
||||||
|
|
||||||
|
// 客户信息
|
||||||
|
const userInfo = ref({
|
||||||
|
name: '',
|
||||||
|
id: '',
|
||||||
|
role: '',
|
||||||
|
phone: '',
|
||||||
|
email: '',
|
||||||
|
deposit: '',
|
||||||
|
balance: '',
|
||||||
|
registerTime: '',
|
||||||
|
hisMeny: 122,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表格
|
||||||
|
const tableData = ref()
|
||||||
|
const tableColumnsOptions = ref([
|
||||||
|
{ label: '交易账号', prop: 'tradeAccount' },
|
||||||
|
{ label: '账户名称', prop: 'accountName' },
|
||||||
|
{ label: '余额', prop: 'balance', sortable: true },
|
||||||
|
{ label: '交易杠杆', prop: 'leverage' },
|
||||||
|
{ label: '历史交易量', prop: 'tradeVolume' },
|
||||||
|
{ label: '添加时间', prop: 'addTime', width: 180 }
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
// ===================== 加载详情数据(替换你的接口) =====================
|
||||||
|
const loadDetailData = async (id: string | number) => {
|
||||||
|
loading.value = true
|
||||||
|
tableLoading.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 这里替换成你的真实客户详情接口
|
||||||
|
getCustomerDetailApi({ user_id: id }).then((res) => {
|
||||||
|
console.log('res:', res);
|
||||||
|
const { accounts, user } = res;
|
||||||
|
userInfo.value = {
|
||||||
|
name: user.email || user.mobile,
|
||||||
|
id: user.id,
|
||||||
|
role: roleMap[user.role_id],
|
||||||
|
phone: user.mobile || '-',
|
||||||
|
email: user.email || '-',
|
||||||
|
balance: user.capital_amount,
|
||||||
|
deposit: user.total_recharge_amount,
|
||||||
|
registerTime: dayjs(user.created_at).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
|
||||||
|
hisMeny: user.history_transfer_amount,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这里替换成你的真实账户列表接口
|
||||||
|
tableData.value = accounts.map((item: any) => ({
|
||||||
|
tradeAccount: item.account_id,
|
||||||
|
accountName: item.account_name,
|
||||||
|
balance: item.wallets_trade.amount,
|
||||||
|
leverage: item.wallets_trade.lever,
|
||||||
|
tradeVolume: item.collect_data,
|
||||||
|
addTime: dayjs(item.created_at).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
ElMessage.error('加载详情失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
tableLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== 监听 ID 自动加载数据 =====================
|
||||||
|
watch(
|
||||||
|
() => props.queryId,
|
||||||
|
async (id) => {
|
||||||
|
if (!id) return
|
||||||
|
await loadDetailData(id)
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
// 账户操作
|
||||||
|
const handleAccount = (row: any) => {
|
||||||
|
ElMessage.success('操作账户:' + row.tradeAccount)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.drawer-content {
|
||||||
|
padding: 0px 32px 28px 32px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-top {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px 32px;
|
||||||
|
background: #fafbfc;
|
||||||
|
border-radius: 16px;
|
||||||
|
border: 1px solid #f0f2f5;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||||
|
min-width: 300px;
|
||||||
|
|
||||||
|
.avatar-wrapper {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #f2f3f5;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.avatar-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #1d2129;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-text p {
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-card {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 28px 36px;
|
||||||
|
padding: 24px 32px;
|
||||||
|
background: #fafbfc;
|
||||||
|
border-radius: 16px;
|
||||||
|
border: 1px solid #f0f2f5;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #86909c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-label :deep(.el-icon) {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #86909c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-value {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #1d2129;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-highlight {
|
||||||
|
color: #1677ff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
@reset="handleReset"
|
@reset="handleReset"
|
||||||
></CustomerListSearch>
|
></CustomerListSearch>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<CustomerListTable
|
<CustomerListTable
|
||||||
:table-data="tableTree"
|
:table-data="tableTree"
|
||||||
|
|
@ -38,11 +39,13 @@
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<template #action="{ row }">
|
<template #action="{ row }">
|
||||||
<el-button link type="primary" size="small" @click="handleDetail(row)" v-if="row.userId">
|
<!-- 👇 这里改成打开抽屉,不再跳转路由 -->
|
||||||
|
<el-button link type="primary" size="small" @click="openDetailDrawer(row)" v-if="row.userId">
|
||||||
查看详情
|
查看详情
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</CustomerListTable>
|
</CustomerListTable>
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<CustomerListPagination
|
<CustomerListPagination
|
||||||
v-model="currentPage"
|
v-model="currentPage"
|
||||||
|
|
@ -50,6 +53,7 @@
|
||||||
:total="total"
|
:total="total"
|
||||||
@pagination-change="handlePaginationChange"
|
@pagination-change="handlePaginationChange"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- dialog -->
|
<!-- dialog -->
|
||||||
<CustomerListDialog
|
<CustomerListDialog
|
||||||
:visible="dialogVisible"
|
:visible="dialogVisible"
|
||||||
|
|
@ -88,7 +92,14 @@
|
||||||
v-if="dialogType === 'editCommissionRatio'"
|
v-if="dialogType === 'editCommissionRatio'"
|
||||||
></CustomerListForm>
|
></CustomerListForm>
|
||||||
</CustomerListDialog>
|
</CustomerListDialog>
|
||||||
<!-- dialog表格 -->
|
|
||||||
|
<!-- ===================== 【新增】客户详情抽屉 ===================== -->
|
||||||
|
<CustomerDetailDrawer
|
||||||
|
v-model:drawer-visible="detailDrawerVisible"
|
||||||
|
:query-id="detailQueryId"
|
||||||
|
:drawer-title="detailDrawerTitle"
|
||||||
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -99,6 +110,10 @@ import CustomerListTable from '@/components/common/Table.vue'
|
||||||
import CustomerListPagination from '@/components/common/Pagination.vue'
|
import CustomerListPagination from '@/components/common/Pagination.vue'
|
||||||
import CustomerListDialog from '@/components/common/Dialog.vue'
|
import CustomerListDialog from '@/components/common/Dialog.vue'
|
||||||
import CustomerListForm from '@/components/common/Form.vue'
|
import CustomerListForm from '@/components/common/Form.vue'
|
||||||
|
|
||||||
|
// 👇 引入同目录的抽屉组件(default.vue)
|
||||||
|
import CustomerDetailDrawer from './default.vue'
|
||||||
|
|
||||||
// 配置以及表单验证
|
// 配置以及表单验证
|
||||||
import { search, tableColumns, marginRatioTableColumns, commissionRatioFormItem } from './options'
|
import { search, tableColumns, marginRatioTableColumns, commissionRatioFormItem } from './options'
|
||||||
|
|
||||||
|
|
@ -112,6 +127,7 @@ import {
|
||||||
// 公用方法
|
// 公用方法
|
||||||
import { getStorage } from '@/utils/storage'
|
import { getStorage } from '@/utils/storage'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 获取用户id
|
* @description: 获取用户id
|
||||||
*/
|
*/
|
||||||
|
|
@ -151,6 +167,11 @@ const dialogTitle = ref<string>('')
|
||||||
const dialogType = ref<string>('')
|
const dialogType = ref<string>('')
|
||||||
const dialogFullscreen = ref(false)
|
const dialogFullscreen = ref(false)
|
||||||
|
|
||||||
|
// ===================== 【新增】详情抽屉状态 =====================
|
||||||
|
const detailDrawerVisible = ref(false)
|
||||||
|
const detailQueryId = ref('') // 传给抽屉的查询ID
|
||||||
|
const detailDrawerTitle = ref('') // 传给抽屉的标题
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 定义点差表格数据
|
* @description: 定义点差表格数据
|
||||||
*/
|
*/
|
||||||
|
|
@ -309,9 +330,18 @@ const handleReset = () => {
|
||||||
/**
|
/**
|
||||||
* @description: 定义表格方法
|
* @description: 定义表格方法
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// ===================== 【新增】打开详情抽屉(把ID传过去) =====================
|
||||||
|
const openDetailDrawer = (row: any) => {
|
||||||
|
detailQueryId.value = row.userId
|
||||||
|
detailDrawerTitle.value = `${row.accountName} - 客户详情`
|
||||||
|
detailDrawerVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 👇 旧的路由跳转可以删掉或保留
|
||||||
const handleDetail = (row: any) => {
|
const handleDetail = (row: any) => {
|
||||||
console.log('查看详情', row)
|
console.log('查看详情(旧路由)', row)
|
||||||
router.push(`/agent/customerDetail/${row.userId}`)
|
// router.push(`/agent/customerDetail/${row.userId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改点差
|
// 修改点差
|
||||||
|
|
@ -443,15 +473,13 @@ const handleCancel = () => {
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* @description: 定义弹窗表格方法
|
|
||||||
*/
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
:deep(.placeholder-row) {
|
:deep(.placeholder-row) {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #909399;
|
color: #909399;
|
||||||
/* 灰色,和Element空状态颜色一致 */
|
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
</style>
|
||||||
|
|
@ -111,6 +111,7 @@
|
||||||
placeholder="请输入验证码"
|
placeholder="请输入验证码"
|
||||||
>
|
>
|
||||||
</el-input>
|
</el-input>
|
||||||
|
|
||||||
<el-button type="primary" @click="handleSendCode" :disabled="sendState.isCounting">{{
|
<el-button type="primary" @click="handleSendCode" :disabled="sendState.isCounting">{{
|
||||||
sendState.isCounting ? `${sendState.countdown}s后重新获取` : '发送验证码'
|
sendState.isCounting ? `${sendState.countdown}s后重新获取` : '发送验证码'
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,552 @@
|
||||||
|
<template>
|
||||||
|
<div class="personal-center">
|
||||||
|
<div class="page-header">
|
||||||
|
<h2>账户信息</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<el-card class="card avatar-card">
|
||||||
|
<div class="avatar-box">
|
||||||
|
<div class="avatar-wrapper">
|
||||||
|
<img v-if="userInfo.avatar" :src="userInfo.avatar" class="avatar-image" alt="用户头像" />
|
||||||
|
<el-icon v-else class="avatar-icon">
|
||||||
|
<UserFilled />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-text">
|
||||||
|
<div class="username-row">
|
||||||
|
<span class="username-text">{{ userInfo.username }}</span>
|
||||||
|
<el-button link type="primary" size="small" @click="showEditDialog('username')">修改</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="login-time">最后登录:{{ userInfo.last_login }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="card info-card">
|
||||||
|
<div class="info-list">
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="label">邮箱</span>
|
||||||
|
<span class="value">{{ userInfo.email || '未设置' }}</span>
|
||||||
|
<template v-if="!userInfo.email">
|
||||||
|
<el-button link type="primary" @click="showEditDialog('email')">去绑定</el-button>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-tag type="success" size="small">已绑定</el-tag>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="label">手机号</span>
|
||||||
|
<span class="value">{{ userInfo.mobile || '未绑定' }}</span>
|
||||||
|
<el-button link type="primary" @click="showEditDialog('mobile')">
|
||||||
|
{{ userInfo.mobile ? '重新绑定' : '绑定' }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="label">密码</span>
|
||||||
|
<el-button link type="primary" @click="showPasswordDialog">修改</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="label">语言</span>
|
||||||
|
<span class="value">选择您偏好的通讯语言</span>
|
||||||
|
<el-select v-model="userInfo.lang_type" @change="handleLanguageChange" style="width: 130px" size="small">
|
||||||
|
<el-option label="简体中文" :value="1" />
|
||||||
|
<el-option label="English" :value="2" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 编辑弹窗 -->
|
||||||
|
<Dialog
|
||||||
|
:visible="dialogVisible"
|
||||||
|
:title="dialogTitle"
|
||||||
|
width="500px"
|
||||||
|
@confirm="handleEditConfirm"
|
||||||
|
@cancel="handleEditDialogReset"
|
||||||
|
@close="handleEditDialogReset"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="editFormRef"
|
||||||
|
:model="editForm"
|
||||||
|
:rules="getEditRules()"
|
||||||
|
label-width="80px"
|
||||||
|
label-position="top"
|
||||||
|
>
|
||||||
|
<el-form-item :label="editForm.label" prop="value">
|
||||||
|
<el-input
|
||||||
|
v-if="editField !== 'language'"
|
||||||
|
v-model="editForm.value"
|
||||||
|
:placeholder="`请输入${editForm.label}`"
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<UserCaptcha
|
||||||
|
v-if="['email', 'mobile'].includes(editField)"
|
||||||
|
v-model="editForm.code"
|
||||||
|
@sendCode="handleSendCaptcha"
|
||||||
|
:show-send-button="showSendButton"
|
||||||
|
:send-state="sendState"
|
||||||
|
/>
|
||||||
|
</el-form>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<!-- 密码修改弹窗 → 验证码模式 -->
|
||||||
|
<Dialog
|
||||||
|
:visible="passwordDialogVisible"
|
||||||
|
title="修改密码"
|
||||||
|
width="500px"
|
||||||
|
@confirm="handlePasswordConfirm"
|
||||||
|
@cancel="handlePasswordDialogReset"
|
||||||
|
@close="handlePasswordDialogReset"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="passwordFormRef"
|
||||||
|
:model="passwordForm"
|
||||||
|
:rules="passwordFormRules"
|
||||||
|
label-width="100px"
|
||||||
|
label-position="top"
|
||||||
|
>
|
||||||
|
<!-- 选择验证方式 -->
|
||||||
|
<el-form-item label="验证方式" prop="verifyType">
|
||||||
|
<el-radio-group v-model="passwordForm.verifyType" @change="onVerifyTypeChange">
|
||||||
|
<el-radio label="mobile" :disabled="!userInfo.mobile">手机验证</el-radio>
|
||||||
|
<el-radio label="email" :disabled="!userInfo.email">邮箱验证</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 验证账号 -->
|
||||||
|
<el-form-item label="验证账号">
|
||||||
|
<span style="font-weight:500;color:#333">
|
||||||
|
{{ passwordForm.verifyType === 'mobile' ? userInfo.mobile : userInfo.email }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 验证码 -->
|
||||||
|
<UserCaptcha
|
||||||
|
prop="code"
|
||||||
|
v-model="passwordForm.code"
|
||||||
|
@sendCode="handleSendPwdCaptcha"
|
||||||
|
:show-send-button="showPwdSendButton"
|
||||||
|
:send-state="pwdSendState"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 新密码1 -->
|
||||||
|
<el-form-item label="新密码" prop="login_password1">
|
||||||
|
<el-input
|
||||||
|
v-model="passwordForm.login_password1"
|
||||||
|
type="password"
|
||||||
|
show-password
|
||||||
|
placeholder="6-12位,含大小写+特殊字符"
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 新密码2 -->
|
||||||
|
<el-form-item label="确认密码" prop="login_password2">
|
||||||
|
<el-input
|
||||||
|
v-model="passwordForm.login_password2"
|
||||||
|
type="password"
|
||||||
|
show-password
|
||||||
|
placeholder="请再次输入新密码"
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted, onUnmounted } from 'vue';
|
||||||
|
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
||||||
|
import { UserFilled } from '@element-plus/icons-vue';
|
||||||
|
import Dialog from '@/components/common/Dialog.vue';
|
||||||
|
import UserCaptcha from '@/views/user/components/Captcha.vue';
|
||||||
|
import { useUserStore } from '@/stores/modules/user';
|
||||||
|
import { useLanguageStore } from '@/stores/modules/language';
|
||||||
|
import { sendCaptcha, initCaptchaState, clearCaptchaTimer } from '@/utils/sendCaptcha';
|
||||||
|
import { codeRules } from '@/views/user/config/rules';
|
||||||
|
|
||||||
|
// 接口
|
||||||
|
import {
|
||||||
|
getUserInfoApi,
|
||||||
|
editLangTypeApi,
|
||||||
|
bindMobileApi,
|
||||||
|
bindEmailApi,
|
||||||
|
editUsernameApi,
|
||||||
|
updatePwdByCaptchaApi // 统一修改密码接口
|
||||||
|
} from '@/api/modules/profile/personalCenter';
|
||||||
|
|
||||||
|
const userInfo = reactive({
|
||||||
|
id: 0,
|
||||||
|
username: '',
|
||||||
|
email: '',
|
||||||
|
mobile: '',
|
||||||
|
lang_type: 1,
|
||||||
|
avatar: '',
|
||||||
|
last_login: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const dialogVisible = ref(false);
|
||||||
|
const passwordDialogVisible = ref(false);
|
||||||
|
const editFormRef = ref<FormInstance>();
|
||||||
|
const passwordFormRef = ref<FormInstance>();
|
||||||
|
const editField = ref('');
|
||||||
|
const dialogTitle = ref('');
|
||||||
|
|
||||||
|
const editForm = reactive({ label: '', value: '', code: '' });
|
||||||
|
|
||||||
|
// 密码表单 → 完全按你后端字段命名
|
||||||
|
const passwordForm = reactive({
|
||||||
|
verifyType: 'mobile' as 'mobile' | 'email',
|
||||||
|
code: '',
|
||||||
|
login_password1: '',
|
||||||
|
login_password2: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
// 验证码
|
||||||
|
const showSendButton = ref(true);
|
||||||
|
const sendState = ref({ isCounting: false, countdown: 0 });
|
||||||
|
const phoneCaptchaState = initCaptchaState('phone');
|
||||||
|
const emailCaptchaState = initCaptchaState('email');
|
||||||
|
|
||||||
|
const showPwdSendButton = ref(true);
|
||||||
|
const pwdSendState = ref({ isCounting: false, countdown: 0 });
|
||||||
|
const pwdMobileCaptchaState = initCaptchaState('phone');
|
||||||
|
const pwdEmailCaptchaState = initCaptchaState('email');
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const languageStore = useLanguageStore();
|
||||||
|
|
||||||
|
// 校验规则
|
||||||
|
const getEditRules = (): FormRules => {
|
||||||
|
const rules: FormRules = {};
|
||||||
|
if (['email', 'mobile'].includes(editField.value)) {
|
||||||
|
rules.code = codeRules().code;
|
||||||
|
}
|
||||||
|
switch (editField.value) {
|
||||||
|
case 'email':
|
||||||
|
rules.value = [
|
||||||
|
{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
||||||
|
{ type: 'email', message: '邮箱格式不正确', trigger: 'blur' }
|
||||||
|
];
|
||||||
|
return rules;
|
||||||
|
case 'mobile':
|
||||||
|
rules.value = [
|
||||||
|
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||||
|
{ pattern: /^\+?\d{5,20}$/, message: '手机号格式不正确', trigger: 'blur' }
|
||||||
|
];
|
||||||
|
return rules;
|
||||||
|
case 'username':
|
||||||
|
rules.value = [
|
||||||
|
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||||
|
{ min: 2, max: 20, message: '用户名长度 2-20 位', trigger: 'blur' }
|
||||||
|
];
|
||||||
|
return rules;
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 密码强度校验
|
||||||
|
const passwordFormRules = ref<FormRules>({
|
||||||
|
verifyType: [{ required: true, message: '请选择验证方式', trigger: 'change' }],
|
||||||
|
code: codeRules().code,
|
||||||
|
login_password1: [
|
||||||
|
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||||
|
{ min: 6, max: 12, message: '长度 6-12 位', trigger: 'blur' },
|
||||||
|
{ pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{6,12}$/,
|
||||||
|
message: '必须包含大小写+特殊字符', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
login_password2: [
|
||||||
|
{ required: true, message: '请确认新密码', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value !== passwordForm.login_password1) callback(new Error('两次密码不一致'));
|
||||||
|
callback();
|
||||||
|
}, trigger: 'blur'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
const fetchUserProfile = async () => {
|
||||||
|
const res = await getUserInfoApi();
|
||||||
|
Object.assign(userInfo, res);
|
||||||
|
languageStore.changeLang(res.lang_type === 1 ? 'zh-CN' : 'en-US');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开编辑弹窗
|
||||||
|
const showEditDialog = (field: string) => {
|
||||||
|
editField.value = field;
|
||||||
|
editForm.code = '';
|
||||||
|
switch (field) {
|
||||||
|
case 'email':
|
||||||
|
editForm.label = '邮箱';
|
||||||
|
editForm.value = userInfo.email;
|
||||||
|
dialogTitle.value = '绑定邮箱';
|
||||||
|
break;
|
||||||
|
case 'mobile':
|
||||||
|
editForm.label = '手机号';
|
||||||
|
editForm.value = userInfo.mobile;
|
||||||
|
dialogTitle.value = userInfo.mobile ? '重新绑定手机号' : '绑定手机号';
|
||||||
|
break;
|
||||||
|
case 'username':
|
||||||
|
editForm.label = '用户名';
|
||||||
|
editForm.value = userInfo.username;
|
||||||
|
dialogTitle.value = '修改用户名';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dialogVisible.value = true;
|
||||||
|
updateSendState();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送绑定验证码
|
||||||
|
const handleSendCaptcha = async () => {
|
||||||
|
const type = editField.value === 'email' ? 'email' : 'phone';
|
||||||
|
const receiver = editForm.value?.trim();
|
||||||
|
const currentState = type === 'email' ? emailCaptchaState : phoneCaptchaState;
|
||||||
|
await sendCaptcha(type, receiver, currentState, () => {
|
||||||
|
return userStore.sendCode({
|
||||||
|
reg_type: type === 'email' ? 1 : 2,
|
||||||
|
[type === 'email' ? 'email' : 'mobile']: receiver,
|
||||||
|
type: type === 'email' ? 'transfer_code' : 'bindMobile_code'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
updateSendState();
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSendState = () => {
|
||||||
|
const t = editField.value === 'email' ? emailCaptchaState : phoneCaptchaState;
|
||||||
|
sendState.value = { isCounting: t.isCounting, countdown: t.countdown };
|
||||||
|
};
|
||||||
|
|
||||||
|
// 语言切换
|
||||||
|
const handleLanguageChange = async (val: number) => {
|
||||||
|
await editLangTypeApi(val);
|
||||||
|
userInfo.lang_type = val;
|
||||||
|
languageStore.changeLang(val === 1 ? 'zh-CN' : 'en-US');
|
||||||
|
ElMessage.success('语言切换成功');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交修改
|
||||||
|
const handleEditConfirm = async () => {
|
||||||
|
if (!editFormRef.value) return;
|
||||||
|
await editFormRef.value.validate();
|
||||||
|
try {
|
||||||
|
if (editField.value === 'username') {
|
||||||
|
await editUsernameApi({ username: editForm.value, user_id: userInfo.id });
|
||||||
|
userInfo.username = editForm.value;
|
||||||
|
}
|
||||||
|
if (editField.value === 'mobile') {
|
||||||
|
await bindMobileApi({ mobile: editForm.value, code: editForm.code });
|
||||||
|
userInfo.mobile = editForm.value;
|
||||||
|
}
|
||||||
|
if (editField.value === 'email') {
|
||||||
|
await bindEmailApi({ email: editForm.value, code: editForm.code });
|
||||||
|
userInfo.email = editForm.value;
|
||||||
|
}
|
||||||
|
ElMessage.success('操作成功');
|
||||||
|
dialogVisible.value = false;
|
||||||
|
editFormRef.value.resetFields();
|
||||||
|
} catch (err) {
|
||||||
|
ElMessage.error('操作失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 密码修改逻辑(完全按你接口格式) ====================
|
||||||
|
const showPasswordDialog = () => {
|
||||||
|
if (!userInfo.mobile && !userInfo.email) {
|
||||||
|
ElMessage.warning('请先绑定手机或邮箱');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
passwordForm.verifyType = userInfo.mobile ? 'mobile' : 'email';
|
||||||
|
passwordForm.code = '';
|
||||||
|
passwordForm.login_password1 = '';
|
||||||
|
passwordForm.login_password2 = '';
|
||||||
|
passwordDialogVisible.value = true;
|
||||||
|
updatePwdSendState();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onVerifyTypeChange = () => {
|
||||||
|
passwordForm.code = '';
|
||||||
|
updatePwdSendState();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送修改密码验证码
|
||||||
|
const handleSendPwdCaptcha = async () => {
|
||||||
|
const type = passwordForm.verifyType === 'email' ? 'email' : 'phone';
|
||||||
|
const receiver = passwordForm.verifyType === 'email' ? userInfo.email : userInfo.mobile;
|
||||||
|
const currentState = passwordForm.verifyType === 'email' ? pwdEmailCaptchaState : pwdMobileCaptchaState;
|
||||||
|
|
||||||
|
await sendCaptcha(type, receiver, currentState, () => {
|
||||||
|
return userStore.sendCode({
|
||||||
|
reg_type: passwordForm.verifyType === 'email' ? 1 : 2,
|
||||||
|
[passwordForm.verifyType]: receiver,
|
||||||
|
type: 'resetPass_code'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
updatePwdSendState();
|
||||||
|
};
|
||||||
|
|
||||||
|
const updatePwdSendState = () => {
|
||||||
|
const t = passwordForm.verifyType === 'email' ? pwdEmailCaptchaState : pwdMobileCaptchaState;
|
||||||
|
pwdSendState.value = { isCounting: t.isCounting, countdown: t.countdown };
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交修改密码 → 100% 按你给的参数格式
|
||||||
|
const handlePasswordConfirm = async () => {
|
||||||
|
if (!passwordFormRef.value) return;
|
||||||
|
await passwordFormRef.value.validate();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
reg_type: passwordForm.verifyType === 'email' ? 1 : 2,
|
||||||
|
email: passwordForm.verifyType === 'email' ? userInfo.email : undefined,
|
||||||
|
mobile: passwordForm.verifyType === 'mobile' ? userInfo.mobile : undefined,
|
||||||
|
code: passwordForm.code,
|
||||||
|
login_password1: passwordForm.login_password1,
|
||||||
|
login_password2: passwordForm.login_password2
|
||||||
|
};
|
||||||
|
|
||||||
|
await updatePwdByCaptchaApi(params);
|
||||||
|
ElMessage.success('密码修改成功');
|
||||||
|
passwordDialogVisible.value = false;
|
||||||
|
passwordFormRef.value.resetFields();
|
||||||
|
} catch (err) {
|
||||||
|
ElMessage.error('密码修改失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEditDialogReset = () => {
|
||||||
|
dialogVisible.value = false;
|
||||||
|
editFormRef.value?.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePasswordDialogReset = () => {
|
||||||
|
passwordDialogVisible.value = false;
|
||||||
|
passwordFormRef.value?.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchUserProfile();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearCaptchaTimer(phoneCaptchaState);
|
||||||
|
clearCaptchaTimer(emailCaptchaState);
|
||||||
|
clearCaptchaTimer(pwdMobileCaptchaState);
|
||||||
|
clearCaptchaTimer(pwdEmailCaptchaState);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.personal-center {
|
||||||
|
padding: 24px;
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
h2 {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2329;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-card {
|
||||||
|
.avatar-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
.avatar-wrapper {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #f2f3f5;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
.avatar-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
.avatar-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.info-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
.username-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
.username-text {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2329;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.login-time {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card {
|
||||||
|
.info-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px 0;
|
||||||
|
border-bottom: 1px solid #f2f3f5;
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #606266;
|
||||||
|
min-width: 70px;
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0 12px;
|
||||||
|
color: #303133;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { fileURLToPath, URL } from 'node:url'
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
import { loadEnv, defineConfig, ConfigEnv, ViteConfig } from 'vite'
|
import { loadEnv, defineConfig, ConfigEnv } from 'vite'
|
||||||
|
import type { UserConfig } from 'vite'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||||
|
|
||||||
|
|
@ -11,10 +12,16 @@ import Components from 'unplugin-vue-components/vite'
|
||||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig(({ mode }: ConfigEnv): ViteConfig => {
|
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
||||||
const isProduction = mode === 'production'
|
const isProduction = mode === 'production'
|
||||||
const env = loadEnv(mode, process.cwd())
|
const env = loadEnv(mode, process.cwd())
|
||||||
return {
|
return {
|
||||||
|
esbuild: {
|
||||||
|
drop: isProduction ? ['console', 'debugger'] : [],
|
||||||
|
// minifyIdentifiers: true,
|
||||||
|
minifySyntax: true,
|
||||||
|
minifyWhitespace: true,
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
// 生产环境移除 devTools 插件(减少产物体积)
|
// 生产环境移除 devTools 插件(减少产物体积)
|
||||||
|
|
@ -63,12 +70,6 @@ export default defineConfig(({ mode }: ConfigEnv): ViteConfig => {
|
||||||
|
|
||||||
// 压缩配置
|
// 压缩配置
|
||||||
minify: 'esbuild',
|
minify: 'esbuild',
|
||||||
esbuild: {
|
|
||||||
drop: isProduction ? ['console', 'debugger'] : [],
|
|
||||||
minifyIdentifiers: true,
|
|
||||||
minifySyntax: true,
|
|
||||||
minifyWhitespace: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 大文件警告阈值(默认500KB,可调整)
|
// 大文件警告阈值(默认500KB,可调整)
|
||||||
chunkSizeWarningLimit: 1000,
|
chunkSizeWarningLimit: 1000,
|
||||||
|
|
@ -82,7 +83,6 @@ export default defineConfig(({ mode }: ConfigEnv): ViteConfig => {
|
||||||
server: {
|
server: {
|
||||||
host: true,
|
host: true,
|
||||||
port: 9999,
|
port: 9999,
|
||||||
https: false,
|
|
||||||
open: true,
|
open: true,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue