fix: 批量修复功能问题 - 购物车结算/订单库存积分/搜索/抽奖/个人资料等

This commit is contained in:
2026-05-26 09:27:20 +08:00
parent 760f67c925
commit 012ec873ec
14 changed files with 308 additions and 39 deletions
+2
View File
@@ -35,6 +35,7 @@
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="profile">个人中心</el-dropdown-item>
<el-dropdown-item command="home">返回前台</el-dropdown-item>
<el-dropdown-item divided command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
@@ -94,6 +95,7 @@ const currentPageTitle = computed(() => {
function handleCommand(command: string) {
switch (command) {
case 'profile': router.push('/profile'); break
case 'home': router.push('/'); break
case 'logout': userStore.logout(); cartStore.clearCart(); router.push('/login'); break
}
+1 -1
View File
@@ -153,7 +153,7 @@ function handleCommand(command: string) {
function handleSearch() {
if (searchQuery.value.trim()) {
router.push({ path: '/products', query: { search: searchQuery.value } })
router.push({ path: '/products', query: { keyword: searchQuery.value } })
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
import { createRouter, createWebHistory } from 'vue-router'
import { systemApi } from '../api'
import { systemApi, authApi } from '../api'
const routes = [
{
+69 -16
View File
@@ -7,20 +7,40 @@
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
</svg>
</div>
<h2>{{ $t('auth.forgotPassword') }}</h2>
<h2>{{ step === 1 ? '忘记密码' : '重置密码' }}</h2>
</div>
<el-form :model="form" :rules="rules" ref="formRef" @submit.prevent="handleSubmit">
<el-form-item :label="$t('auth.email')" prop="email">
<el-input v-model="form.email" type="email" />
<el-steps :active="step - 1" simple style="margin-bottom: 24px">
<el-step title="发送验证码" />
<el-step title="重置密码" />
</el-steps>
<el-form v-if="step === 1" :model="emailForm" :rules="emailRules" ref="emailFormRef" @submit.prevent="handleSendCode">
<el-form-item label="邮箱" prop="email">
<el-input v-model="emailForm.email" type="email" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="loading" native-type="submit" class="submit-btn">
{{ $t('auth.resetPassword') }}
</el-button>
<el-button type="primary" :loading="loading" native-type="submit" class="submit-btn">发送验证码</el-button>
</el-form-item>
</el-form>
<el-form v-else :model="resetForm" :rules="resetRules" ref="resetFormRef" @submit.prevent="handleResetPassword">
<el-form-item label="验证码" prop="code">
<el-input v-model="resetForm.code" placeholder="请输入邮箱收到的验证码" />
</el-form-item>
<el-form-item label="新密码" prop="new_password">
<el-input v-model="resetForm.new_password" type="password" show-password placeholder="至少6位" />
</el-form-item>
<el-form-item label="确认密码" prop="confirm_password">
<el-input v-model="resetForm.confirm_password" type="password" show-password />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="loading" native-type="submit" class="submit-btn">重置密码</el-button>
</el-form-item>
</el-form>
<div class="auth-links">
<router-link to="/login">{{ $t('common.login') }}</router-link>
<router-link to="/login">返回登录</router-link>
</div>
</div>
</div>
@@ -28,22 +48,55 @@
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { authApi } from '../../api'
const formRef = ref()
const router = useRouter()
const emailFormRef = ref()
const resetFormRef = ref()
const loading = ref(false)
const form = reactive({ email: '' })
const rules = {
const step = ref(1)
const emailForm = reactive({ email: '' })
const emailRules = {
email: [{ required: true, message: '请输入邮箱', trigger: 'blur' }],
}
async function handleSubmit() {
await formRef.value?.validate()
const resetForm = reactive({ code: '', new_password: '', confirm_password: '' })
const resetRules = {
code: [{ required: true, message: '请输入验证码', trigger: 'blur' }],
new_password: [{ required: true, message: '请输入新密码', trigger: 'blur' }, { min: 6, message: '密码至少6位', trigger: 'blur' }],
confirm_password: [{ required: true, message: '请确认密码', trigger: 'blur' }],
}
async function handleSendCode() {
await emailFormRef.value?.validate()
loading.value = true
try {
await authApi.forgotPassword(form)
ElMessage.success('如果邮箱存在,验证码已发送')
await authApi.forgotPassword(emailForm)
ElMessage.success('验证码已发送到您的邮箱')
step.value = 2
} catch (err: any) {
ElMessage.error(err.response?.data?.error || '发送失败')
} finally {
loading.value = false
}
}
async function handleResetPassword() {
await resetFormRef.value?.validate()
if (resetForm.new_password !== resetForm.confirm_password) {
ElMessage.warning('两次密码不一致')
return
}
loading.value = true
try {
await authApi.resetPassword({ email: emailForm.email, code: resetForm.code, new_password: resetForm.new_password })
ElMessage.success('密码重置成功,请登录')
router.push('/login')
} catch (err: any) {
ElMessage.error(err.response?.data?.error || '重置失败')
} finally {
loading.value = false
}
@@ -65,7 +118,7 @@ async function handleSubmit() {
border-radius: 16px;
padding: 40px;
width: 100%;
max-width: 400px;
max-width: 440px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
}
+2 -1
View File
@@ -418,7 +418,8 @@ async function handleCheckout() {
try {
await orderApi.create({
shipping_address_id: selectedAddressId.value,
payment_method: selectedPayment.value
payment_method: selectedPayment.value,
cart_item_ids: selectedIds.value
})
ElMessage.success('订单创建成功')
await cartStore.fetchCart()
+3 -3
View File
@@ -59,8 +59,8 @@
<p>{{ l.description }}</p>
</div>
<div class="lottery-right">
<span class="lottery-status" :class="l.status === 'active' ? 'active' : ''">
{{ l.status === 'active' ? '进行中' : '已结束' }}
<span class="lottery-status" :class="l.is_active ? 'active' : ''">
{{ l.is_active ? '进行中' : '已结束' }}
</span>
<button class="lottery-btn">参与</button>
</div>
@@ -99,7 +99,7 @@
</div>
<div class="category-info">
<h3>{{ cat.name }}</h3>
<p>{{ cat.product_count || 0 }} 件商品</p>
<p>{{ cat.description || '精选好物' }}</p>
</div>
<el-icon class="category-arrow"><ArrowRight /></el-icon>
</div>
+3 -3
View File
@@ -30,9 +30,9 @@ const route = useRoute()
const lottery = ref<any>(null)
const prizeTypeMap: Record<string, string> = {
product: '商品',
credits: '积分',
cash: '现金',
physical: '实物',
virtual: '虚拟',
credit: '积分',
}
function prizeTypeText(t: string) { return prizeTypeMap[t] || t }
+77 -1
View File
@@ -19,19 +19,95 @@
<el-button @click="$router.push('/orders')">我的订单</el-button>
<el-button v-if="user.role === 'admin'" type="primary" @click="$router.push('/admin')">管理后台</el-button>
<el-button v-if="user.role === 'supplier'" type="primary" @click="$router.push('/supplier')">供应商后台</el-button>
<el-button type="warning" @click="showEditProfile = true">编辑资料</el-button>
<el-button type="danger" @click="showChangePassword = true">修改密码</el-button>
</div>
</div>
<el-dialog v-model="showEditProfile" title="编辑资料" width="400px">
<el-form :model="profileForm" label-width="80px">
<el-form-item label="用户名"><el-input v-model="profileForm.username" /></el-form-item>
<el-form-item label="邮箱"><el-input v-model="profileForm.email" /></el-form-item>
</el-form>
<template #footer>
<el-button @click="showEditProfile = false">取消</el-button>
<el-button type="primary" @click="saveProfile" :loading="saving">保存</el-button>
</template>
</el-dialog>
<el-dialog v-model="showChangePassword" title="修改密码" width="400px">
<el-form :model="passwordForm" label-width="80px">
<el-form-item label="旧密码"><el-input v-model="passwordForm.old_password" type="password" show-password /></el-form-item>
<el-form-item label="新密码"><el-input v-model="passwordForm.new_password" type="password" show-password /></el-form-item>
<el-form-item label="确认密码"><el-input v-model="passwordForm.confirm_password" type="password" show-password /></el-form-item>
</el-form>
<template #footer>
<el-button @click="showChangePassword = false">取消</el-button>
<el-button type="primary" @click="changePassword" :loading="saving">确认修改</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { computed, onMounted } from 'vue'
import { computed, onMounted, ref, reactive, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { useUserStore } from '../../store/user'
import { userApi } from '../../api'
import BackNav from '../../components/BackNav.vue'
const userStore = useUserStore()
const user = computed(() => userStore.user)
const showEditProfile = ref(false)
const showChangePassword = ref(false)
const saving = ref(false)
const profileForm = reactive({ username: '', email: '' })
const passwordForm = reactive({ old_password: '', new_password: '', confirm_password: '' })
watch(() => userStore.user, (u) => {
if (u) {
profileForm.username = u.username || ''
profileForm.email = u.email || ''
}
}, { immediate: true })
async function saveProfile() {
saving.value = true
try {
await userApi.updateProfile(profileForm)
ElMessage.success('资料已更新')
showEditProfile.value = false
await userStore.fetchProfile()
} catch (err: any) {
ElMessage.error(err.response?.data?.error || '更新失败')
} finally {
saving.value = false
}
}
async function changePassword() {
if (passwordForm.new_password !== passwordForm.confirm_password) {
ElMessage.warning('两次密码不一致')
return
}
if (passwordForm.new_password.length < 6) {
ElMessage.warning('密码至少6位')
return
}
saving.value = true
try {
await userApi.changePassword({ old_password: passwordForm.old_password, new_password: passwordForm.new_password })
ElMessage.success('密码已修改')
showChangePassword.value = false
Object.assign(passwordForm, { old_password: '', new_password: '', confirm_password: '' })
} catch (err: any) {
ElMessage.error(err.response?.data?.error || '修改失败')
} finally {
saving.value = false
}
}
const roleMap: Record<string, string> = {
admin: '管理员',
supplier: '供应商',