fix: 批量修复功能问题 - 购物车结算/订单库存积分/搜索/抽奖/个人资料等
This commit is contained in:
@@ -27,7 +27,7 @@ func (h *BrandHandler) Create(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
utils.DB.Create(&brand)
|
||||
c.JSON(http.StatusOK, brand)
|
||||
c.JSON(http.StatusOK, gin.H{"data": brand})
|
||||
}
|
||||
|
||||
func (h *BrandHandler) Update(c *gin.Context) {
|
||||
@@ -37,13 +37,32 @@ func (h *BrandHandler) Update(c *gin.Context) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Brand not found"})
|
||||
return
|
||||
}
|
||||
var input models.Brand
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
var req struct {
|
||||
Name *string `json:"name"`
|
||||
Icon *string `json:"icon"`
|
||||
Info *string `json:"info"`
|
||||
SortOrder *int `json:"sort_order"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
utils.DB.Model(&brand).Updates(input)
|
||||
c.JSON(http.StatusOK, brand)
|
||||
updates := make(map[string]interface{})
|
||||
if req.Name != nil {
|
||||
updates["name"] = *req.Name
|
||||
}
|
||||
if req.Icon != nil {
|
||||
updates["icon"] = *req.Icon
|
||||
}
|
||||
if req.Info != nil {
|
||||
updates["info"] = *req.Info
|
||||
}
|
||||
if req.SortOrder != nil {
|
||||
updates["sort_order"] = *req.SortOrder
|
||||
}
|
||||
utils.DB.Model(&brand).Updates(updates)
|
||||
utils.DB.First(&brand, brand.ID)
|
||||
c.JSON(http.StatusOK, gin.H{"data": brand})
|
||||
}
|
||||
|
||||
func (h *BrandHandler) Delete(c *gin.Context) {
|
||||
|
||||
@@ -52,6 +52,31 @@ func (h *LotteryHandler) Register(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if !lottery.IsActive {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Lottery is not active"})
|
||||
return
|
||||
}
|
||||
|
||||
if lottery.TotalQuota != nil && *lottery.TotalQuota > 0 {
|
||||
var participantCount int64
|
||||
utils.DB.Model(&models.LotteryParticipant{}).Where("lottery_id = ?", id).Count(&participantCount)
|
||||
if int(participantCount) >= *lottery.TotalQuota {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Lottery quota is full"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if lottery.DailyQuota != nil && *lottery.DailyQuota > 0 {
|
||||
today := time.Now().Truncate(24 * time.Hour)
|
||||
tomorrow := today.Add(24 * time.Hour)
|
||||
var dailyCount int64
|
||||
utils.DB.Model(&models.LotteryParticipant{}).Where("lottery_id = ? AND registered_at >= ? AND registered_at < ?", id, today, tomorrow).Count(&dailyCount)
|
||||
if int(dailyCount) >= *lottery.DailyQuota {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Daily quota is full"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var existing models.LotteryParticipant
|
||||
if err := utils.DB.Where("lottery_id = ? AND user_id = ?", id, userID).First(&existing).Error; err == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Already registered"})
|
||||
|
||||
@@ -141,7 +141,11 @@ func (h *OrderHandler) Create(c *gin.Context) {
|
||||
}
|
||||
|
||||
var carts []models.Cart
|
||||
utils.DB.Where("user_id = ?", userID).Preload("Product").Find(&carts)
|
||||
cartQuery := utils.DB.Where("user_id = ?", userID).Preload("Product")
|
||||
if len(req.CartItemIDs) > 0 {
|
||||
cartQuery = cartQuery.Where("id IN ?", req.CartItemIDs)
|
||||
}
|
||||
cartQuery.Find(&carts)
|
||||
|
||||
if len(carts) == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Cart is empty"})
|
||||
@@ -154,6 +158,20 @@ func (h *OrderHandler) Create(c *gin.Context) {
|
||||
supplierMap := make(map[uint]bool)
|
||||
|
||||
for _, cart := range carts {
|
||||
if !cart.Product.IsActive {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Product " + cart.Product.Name + " is no longer available"})
|
||||
return
|
||||
}
|
||||
|
||||
if cart.Product.MinPurchase > 0 && cart.Quantity < cart.Product.MinPurchase {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": cart.Product.Name + " minimum purchase is " + strconv.Itoa(cart.Product.MinPurchase)})
|
||||
return
|
||||
}
|
||||
if cart.Product.MaxPurchase != nil && *cart.Product.MaxPurchase > 0 && cart.Quantity > *cart.Product.MaxPurchase {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": cart.Product.Name + " maximum purchase is " + strconv.Itoa(*cart.Product.MaxPurchase)})
|
||||
return
|
||||
}
|
||||
|
||||
if cart.Product.RequireCredit {
|
||||
var user models.User
|
||||
utils.DB.First(&user, userID)
|
||||
@@ -268,7 +286,11 @@ func (h *OrderHandler) Create(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Where("user_id = ?", userID).Delete(&models.Cart{}).Error; err != nil {
|
||||
cartIDs := make([]uint, len(carts))
|
||||
for i, cart := range carts {
|
||||
cartIDs[i] = cart.ID
|
||||
}
|
||||
if err := tx.Where("id IN ? AND user_id = ?", cartIDs, userID).Delete(&models.Cart{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to clear cart"})
|
||||
return
|
||||
@@ -394,7 +416,7 @@ func (h *OrderHandler) ProcessRefund(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
|
||||
var order models.Order
|
||||
if err := utils.DB.First(&order, id).Error; err != nil {
|
||||
if err := utils.DB.Preload("OrderItems").First(&order, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Order not found"})
|
||||
return
|
||||
}
|
||||
@@ -413,11 +435,35 @@ func (h *OrderHandler) ProcessRefund(c *gin.Context) {
|
||||
}
|
||||
|
||||
refundAmount := order.TotalAmount * (1 - feeRate/100)
|
||||
utils.DB.Model(&order).Updates(map[string]interface{}{
|
||||
|
||||
tx := utils.DB.Begin()
|
||||
|
||||
tx.Model(&order).Updates(map[string]interface{}{
|
||||
"refund_status": models.RefundStatusCompleted,
|
||||
"refund_amount": refundAmount,
|
||||
"status": models.OrderStatusRefunded,
|
||||
})
|
||||
|
||||
for _, item := range order.OrderItems {
|
||||
var product models.Product
|
||||
if err := tx.First(&product, item.ProductID).Error; err == nil {
|
||||
if product.RequireCredit {
|
||||
tx.Model(&models.User{}).Where("id = ?", order.UserID).
|
||||
UpdateColumn("purchase_credits", utils.DB.Raw("purchase_credits + ?", product.CreditCost*item.Quantity))
|
||||
}
|
||||
if product.CreditReward > 0 {
|
||||
tx.Model(&models.User{}).Where("id = ?", order.UserID).
|
||||
UpdateColumn("purchase_credits", utils.DB.Raw("purchase_credits - ?", product.CreditReward*item.Quantity))
|
||||
}
|
||||
}
|
||||
|
||||
var inventory models.Inventory
|
||||
if err := tx.Where("product_id = ?", item.ProductID).First(&inventory).Error; err == nil {
|
||||
tx.Model(&inventory).UpdateColumn("quantity", inventory.Quantity+item.Quantity)
|
||||
}
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
} else {
|
||||
utils.DB.Model(&order).Updates(map[string]interface{}{
|
||||
"refund_status": models.RefundStatusRejected,
|
||||
@@ -441,7 +487,7 @@ func (h *OrderHandler) CancelOrder(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
|
||||
var order models.Order
|
||||
if err := utils.DB.Where("id = ? AND user_id = ?", id, userID).First(&order).Error; err != nil {
|
||||
if err := utils.DB.Preload("OrderItems").Where("id = ? AND user_id = ?", id, userID).First(&order).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Order not found"})
|
||||
return
|
||||
}
|
||||
@@ -451,7 +497,29 @@ func (h *OrderHandler) CancelOrder(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
utils.DB.Model(&order).Update("status", models.OrderStatusCancelled)
|
||||
tx := utils.DB.Begin()
|
||||
tx.Model(&order).Update("status", models.OrderStatusCancelled)
|
||||
|
||||
for _, item := range order.OrderItems {
|
||||
var product models.Product
|
||||
if err := tx.First(&product, item.ProductID).Error; err == nil {
|
||||
if product.RequireCredit {
|
||||
tx.Model(&models.User{}).Where("id = ?", order.UserID).
|
||||
UpdateColumn("purchase_credits", utils.DB.Raw("purchase_credits + ?", product.CreditCost*item.Quantity))
|
||||
}
|
||||
if product.CreditReward > 0 {
|
||||
tx.Model(&models.User{}).Where("id = ?", order.UserID).
|
||||
UpdateColumn("purchase_credits", utils.DB.Raw("purchase_credits - ?", product.CreditReward*item.Quantity))
|
||||
}
|
||||
}
|
||||
|
||||
var inventory models.Inventory
|
||||
if err := tx.Where("product_id = ?", item.ProductID).First(&inventory).Error; err == nil {
|
||||
tx.Model(&inventory).UpdateColumn("quantity", inventory.Quantity+item.Quantity)
|
||||
}
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Order cancelled successfully"})
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,14 @@ func (h *CategoryHandler) List(c *gin.Context) {
|
||||
utils.DB.Where("parent_id IS NULL").Order("sort_order ASC, id ASC").Find(&categories)
|
||||
for i := range categories {
|
||||
utils.DB.Where("parent_id = ?", categories[i].ID).Order("sort_order ASC, id ASC").Find(&categories[i].Children)
|
||||
var count int64
|
||||
utils.DB.Table("product_categories").Where("category_id = ?", categories[i].ID).Count(&count)
|
||||
categories[i].ProductCount = count
|
||||
for j := range categories[i].Children {
|
||||
var childCount int64
|
||||
utils.DB.Table("product_categories").Where("category_id = ?", categories[i].Children[j].ID).Count(&childCount)
|
||||
categories[i].Children[j].ProductCount = childCount
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"data": categories})
|
||||
}
|
||||
@@ -121,6 +129,21 @@ func (h *CategoryHandler) Update(c *gin.Context) {
|
||||
|
||||
func (h *CategoryHandler) Delete(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
|
||||
var childCount int64
|
||||
utils.DB.Model(&models.Category{}).Where("parent_id = ?", id).Count(&childCount)
|
||||
if childCount > 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "该分类下有子分类,无法删除"})
|
||||
return
|
||||
}
|
||||
|
||||
var productCount int64
|
||||
utils.DB.Table("product_categories").Where("category_id = ?", id).Count(&productCount)
|
||||
if productCount > 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "该分类下有商品,无法删除"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := utils.DB.Delete(&models.Category{}, id).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete category"})
|
||||
return
|
||||
|
||||
@@ -21,6 +21,7 @@ type Category struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
ProductCount int64 `gorm:"-" json:"product_count"`
|
||||
Children []Category `gorm:"foreignKey:ParentID" json:"children,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,9 @@ type UpdateCartRequest struct {
|
||||
}
|
||||
|
||||
type CreateOrderRequest struct {
|
||||
ShippingAddressID uint `json:"shipping_address_id" binding:"required"`
|
||||
PaymentMethod string `json:"payment_method"`
|
||||
ShippingAddressID uint `json:"shipping_address_id" binding:"required"`
|
||||
PaymentMethod string `json:"payment_method"`
|
||||
CartItemIDs []uint `json:"cart_item_ids"`
|
||||
}
|
||||
|
||||
type RefundRequest struct {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,5 +1,5 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { systemApi } from '../api'
|
||||
import { systemApi, authApi } from '../api'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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: '供应商',
|
||||
|
||||
Reference in New Issue
Block a user