|
|
@@ -1,576 +1,577 @@
|
|
|
-<template>
|
|
|
- <div class="page-container">
|
|
|
- <el-card>
|
|
|
- <div slot="header">
|
|
|
- <span>心愿管理</span>
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- size="small"
|
|
|
- style="float: right"
|
|
|
- @click="handleRefresh"
|
|
|
- :loading="loading"
|
|
|
- >
|
|
|
- 刷新
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
-
|
|
|
- <el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
|
|
- <!-- 待处理列表 -->
|
|
|
- <el-tab-pane label="待处理" name="pending">
|
|
|
- <!-- 统计卡片 -->
|
|
|
- <el-row :gutter="20" style="margin-bottom: 20px">
|
|
|
- <el-col :span="6">
|
|
|
- <el-card shadow="hover" class="stat-card draft">
|
|
|
- <div class="stat-num">{{ draftCount }}</div>
|
|
|
- <div class="stat-label">待定价</div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-card shadow="hover" class="stat-card pending">
|
|
|
- <div class="stat-num">{{ pendingCount }}</div>
|
|
|
- <div class="stat-label">待审批</div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-card shadow="hover" class="stat-card approved">
|
|
|
- <div class="stat-num">{{ approvedCount }}</div>
|
|
|
- <div class="stat-label">待兑现</div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-card shadow="hover" class="stat-card exchanging">
|
|
|
- <div class="stat-num">{{ exchangingCount }}</div>
|
|
|
- <div class="stat-label">待收货</div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
-
|
|
|
- <!-- 待定价列表 -->
|
|
|
- <div v-if="draftWishes.length > 0" class="wish-section">
|
|
|
- <div class="section-title">📝 待定价</div>
|
|
|
- <el-table :data="draftWishes" border v-loading="loading" style="width: 100%">
|
|
|
- <el-table-column prop="id" label="ID" width="80"></el-table-column>
|
|
|
- <el-table-column prop="childName" label="孩子" width="100"></el-table-column>
|
|
|
- <el-table-column prop="title" label="心愿名称" min-width="200"></el-table-column>
|
|
|
- <el-table-column prop="category" label="分类" width="100"></el-table-column>
|
|
|
- <el-table-column prop="createdAt" label="创建时间" width="180">
|
|
|
- <template slot-scope="scope">
|
|
|
- {{ formatDate(scope.row.createdAt) }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="操作" width="200" fixed="right">
|
|
|
- <template slot-scope="{ row }">
|
|
|
- <el-button size="mini" type="primary" @click="showPriceDialog(row)">定价</el-button>
|
|
|
- <el-dropdown trigger="hover" @command="(cmd) => handleActionCmd(row, cmd)">
|
|
|
- <el-button size="mini">
|
|
|
- 更多<i class="el-icon-arrow-down el-icon--right"></i>
|
|
|
- </el-button>
|
|
|
- <el-dropdown-menu slot="dropdown">
|
|
|
- <el-dropdown-item command="reject" icon="el-icon-close">拒绝</el-dropdown-item>
|
|
|
- </el-dropdown-menu>
|
|
|
- </el-dropdown>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 待审批兑换列表 -->
|
|
|
- <div v-if="pendingExchangeWishes.length > 0" class="wish-section">
|
|
|
- <div class="section-title">⏳ 待审批兑换</div>
|
|
|
- <el-table :data="pendingExchangeWishes" border v-loading="loading" style="width: 100%">
|
|
|
- <el-table-column prop="id" label="ID" width="80"></el-table-column>
|
|
|
- <el-table-column prop="childName" label="孩子" width="100"></el-table-column>
|
|
|
- <el-table-column prop="title" label="心愿名称" min-width="200"></el-table-column>
|
|
|
- <el-table-column prop="pointsRequired" label="所需积分" width="120">
|
|
|
- <template slot-scope="scope">
|
|
|
- <el-tag type="warning">{{ scope.row.pointsRequired }}</el-tag>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="exchangeRequestedAt" label="申请时间" width="180">
|
|
|
- <template slot-scope="scope">
|
|
|
- {{ formatDate(scope.row.exchangeRequestedAt) }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="操作" width="200" fixed="right">
|
|
|
- <template slot-scope="{ row }">
|
|
|
- <el-button size="mini" type="primary" @click="handleApproveExchange(row, true)">批准</el-button>
|
|
|
- <el-dropdown trigger="hover" @command="(cmd) => handleActionCmd(row, cmd)">
|
|
|
- <el-button size="mini">
|
|
|
- 更多<i class="el-icon-arrow-down el-icon--right"></i>
|
|
|
- </el-button>
|
|
|
- <el-dropdown-menu slot="dropdown">
|
|
|
- <el-dropdown-item command="rejectExchange" icon="el-icon-close">拒绝</el-dropdown-item>
|
|
|
- </el-dropdown-menu>
|
|
|
- </el-dropdown>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 待兑现列表 -->
|
|
|
- <div v-if="approvedWishes.length > 0" class="wish-section">
|
|
|
- <div class="section-title">📦 待兑现</div>
|
|
|
- <el-table :data="approvedWishes" border v-loading="loading" style="width: 100%">
|
|
|
- <el-table-column prop="id" label="ID" width="80"></el-table-column>
|
|
|
- <el-table-column prop="childName" label="孩子" width="100"></el-table-column>
|
|
|
- <el-table-column prop="title" label="心愿名称" min-width="200"></el-table-column>
|
|
|
- <el-table-column prop="pointsRequired" label="已扣积分" width="120">
|
|
|
- <template slot-scope="scope">
|
|
|
- <el-tag type="success">{{ scope.row.pointsRequired }}</el-tag>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="exchangeApprovedAt" label="批准时间" width="180">
|
|
|
- <template slot-scope="scope">
|
|
|
- {{ formatDate(scope.row.exchangeApprovedAt) }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="操作" width="120" fixed="right">
|
|
|
- <template slot-scope="{ row }">
|
|
|
- <el-button size="mini" @click="handleFulfill(row)">已购买</el-button>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 待收货列表 -->
|
|
|
- <div v-if="exchangingWishes.length > 0" class="wish-section">
|
|
|
- <div class="section-title">🎁 待收货确认</div>
|
|
|
- <el-table :data="exchangingWishes" border v-loading="loading" style="width: 100%">
|
|
|
- <el-table-column prop="id" label="ID" width="80"></el-table-column>
|
|
|
- <el-table-column prop="childName" label="孩子" width="100"></el-table-column>
|
|
|
- <el-table-column prop="title" label="心愿名称" min-width="200"></el-table-column>
|
|
|
- <el-table-column prop="fulfilledAt" label="购买时间" width="180">
|
|
|
- <template slot-scope="scope">
|
|
|
- {{ formatDate(scope.row.fulfilledAt) }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="状态" width="100">
|
|
|
- <template slot-scope="scope">
|
|
|
- <el-tag type="info">等待确认</el-tag>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 空状态 -->
|
|
|
- <el-empty v-if="pendingWishes.length === 0" description="暂无待处理心愿"></el-empty>
|
|
|
- </el-tab-pane>
|
|
|
-
|
|
|
- <!-- 全部心愿 -->
|
|
|
- <el-tab-pane label="全部心愿" name="all">
|
|
|
- <!-- 搜索筛选 -->
|
|
|
- <el-form :inline="true" :model="searchForm" style="margin-bottom: 20px">
|
|
|
- <el-form-item label="家庭">
|
|
|
- <el-input
|
|
|
- v-model="searchForm.familyName"
|
|
|
- placeholder="点击选择家庭"
|
|
|
- readonly
|
|
|
- @click.native="showFamilySelector"
|
|
|
- >
|
|
|
- <el-button
|
|
|
- slot="append"
|
|
|
- icon="el-icon-close"
|
|
|
- v-if="searchForm.familyId"
|
|
|
- @click.stop="clearFamily"
|
|
|
- style="padding: 8px"
|
|
|
- ></el-button>
|
|
|
- <i slot="suffix" class="el-input__icon el-icon-arrow-down" @click.stop="showFamilySelector"></i>
|
|
|
- </el-input>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="状态">
|
|
|
- <el-select v-model="searchForm.status" placeholder="全部状态" clearable @change="handleSearch">
|
|
|
- <el-option label="待定价" value="draft"></el-option>
|
|
|
- <el-option label="积攒中" value="active"></el-option>
|
|
|
- <el-option label="待审批" value="pending_exchange"></el-option>
|
|
|
- <el-option label="已批准" value="approved"></el-option>
|
|
|
- <el-option label="待收货" value="exchanging"></el-option>
|
|
|
- <el-option label="已完成" value="fulfilled"></el-option>
|
|
|
- <el-option label="已拒绝" value="rejected"></el-option>
|
|
|
- <el-option label="已取消" value="cancelled"></el-option>
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item>
|
|
|
- <el-button type="primary" @click="handleSearch">搜索</el-button>
|
|
|
- <el-button @click="handleReset">重置</el-button>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
-
|
|
|
- <!-- 家庭选择对话框 -->
|
|
|
- <el-dialog title="选择家庭" :visible.sync="familyDialogVisible" width="600px">
|
|
|
- <el-table :data="familyList" border v-loading="familyLoading" @row-click="selectFamily" style="cursor: pointer">
|
|
|
- <el-table-column prop="id" label="ID" width="80"></el-table-column>
|
|
|
- <el-table-column prop="name" label="家庭名称" min-width="150"></el-table-column>
|
|
|
- <el-table-column prop="inviteCode" label="邀请码" width="120"></el-table-column>
|
|
|
- </el-table>
|
|
|
- <el-pagination
|
|
|
- style="margin-top: 15px; text-align: right"
|
|
|
- @current-change="loadFamilies"
|
|
|
- :current-page="familyPagination.current"
|
|
|
- :page-size="familyPagination.size"
|
|
|
- :total="familyPagination.total"
|
|
|
- layout="total, prev, pager, next"
|
|
|
- ></el-pagination>
|
|
|
- </el-dialog>
|
|
|
-
|
|
|
- <el-table :data="tableData" border v-loading="loading" style="width: 100%">
|
|
|
- <el-table-column prop="id" label="ID" width="80"></el-table-column>
|
|
|
- <el-table-column prop="childName" label="孩子" width="100"></el-table-column>
|
|
|
- <el-table-column prop="title" label="心愿名称" min-width="200"></el-table-column>
|
|
|
- <el-table-column prop="pointsRequired" label="所需积分" width="120">
|
|
|
- <template slot-scope="scope">
|
|
|
- <el-tag v-if="scope.row.pointsRequired" type="warning">{{ scope.row.pointsRequired }}</el-tag>
|
|
|
- <span v-else class="no-price">未定价</span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="category" label="分类" width="100"></el-table-column>
|
|
|
- <el-table-column prop="status" label="状态" width="100">
|
|
|
- <template slot-scope="scope">
|
|
|
- <el-tag :type="getStatusType(scope.row.status)" size="small">
|
|
|
- {{ scope.row.statusText }}
|
|
|
- </el-tag>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="creatorType" label="创建者" width="80">
|
|
|
- <template slot-scope="scope">
|
|
|
- <el-tag :type="scope.row.creatorType === 'parent' ? 'primary' : 'success'" size="small">
|
|
|
- {{ scope.row.creatorType === 'parent' ? '家长' : '孩子' }}
|
|
|
- </el-tag>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="createdAt" label="创建时间" width="180">
|
|
|
- <template slot-scope="scope">
|
|
|
- {{ formatDate(scope.row.createdAt) }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
-
|
|
|
- <el-pagination
|
|
|
- style="margin-top: 20px; text-align: right"
|
|
|
- @size-change="handleSizeChange"
|
|
|
- @current-change="handleCurrentChange"
|
|
|
- :current-page="pagination.current"
|
|
|
- :page-sizes="[10, 20, 50, 100]"
|
|
|
- :page-size="pagination.size"
|
|
|
- :total="pagination.total"
|
|
|
- layout="total, sizes, prev, pager, next, jumper"
|
|
|
- ></el-pagination>
|
|
|
- </el-tab-pane>
|
|
|
- </el-tabs>
|
|
|
- </el-card>
|
|
|
-
|
|
|
- <!-- 定价对话框 -->
|
|
|
- <el-dialog title="设置积分" :visible.sync="priceDialogVisible" width="400px">
|
|
|
- <el-form label-width="100px">
|
|
|
- <el-form-item label="心愿名称">
|
|
|
- <span>{{ currentWish && currentWish.title }}</span>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="所需积分">
|
|
|
- <el-input-number v-model="priceValue" :min="1" :max="10000"></el-input-number>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- <div slot="footer">
|
|
|
- <el-button @click="priceDialogVisible = false">取消</el-button>
|
|
|
- <el-button type="primary" @click="submitPrice" :loading="submitting">确定</el-button>
|
|
|
- </div>
|
|
|
- </el-dialog>
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script>
|
|
|
-import {
|
|
|
- getWishesList,
|
|
|
- getPendingWishes,
|
|
|
- setWishPrice,
|
|
|
- rejectWish,
|
|
|
- approveWishExchange,
|
|
|
- markWishFulfilled,
|
|
|
- getFamilyList
|
|
|
-} from '@/api/admin'
|
|
|
-
|
|
|
-export default {
|
|
|
- name: 'Wishes',
|
|
|
- data() {
|
|
|
- return {
|
|
|
- activeTab: 'pending',
|
|
|
- loading: false,
|
|
|
- submitting: false,
|
|
|
- tableData: [],
|
|
|
- pendingWishes: [],
|
|
|
- searchForm: {
|
|
|
- status: '',
|
|
|
- familyId: '',
|
|
|
- familyName: ''
|
|
|
- },
|
|
|
- pagination: {
|
|
|
- current: 1,
|
|
|
- size: 10,
|
|
|
- total: 0
|
|
|
- },
|
|
|
- // 家庭选择对话框
|
|
|
- familyDialogVisible: false,
|
|
|
- familyLoading: false,
|
|
|
- familyList: [],
|
|
|
- familyPagination: {
|
|
|
- current: 1,
|
|
|
- size: 10,
|
|
|
- total: 0
|
|
|
- },
|
|
|
- // 定价对话框
|
|
|
- priceDialogVisible: false,
|
|
|
- currentWish: null,
|
|
|
- priceValue: 100
|
|
|
- }
|
|
|
- },
|
|
|
- computed: {
|
|
|
- draftWishes() {
|
|
|
- return this.pendingWishes.filter(w => w.status === 'draft')
|
|
|
- },
|
|
|
- pendingExchangeWishes() {
|
|
|
- return this.pendingWishes.filter(w => w.status === 'pending_exchange')
|
|
|
- },
|
|
|
- approvedWishes() {
|
|
|
- return this.pendingWishes.filter(w => w.status === 'approved')
|
|
|
- },
|
|
|
- exchangingWishes() {
|
|
|
- return this.pendingWishes.filter(w => w.status === 'exchanging')
|
|
|
- },
|
|
|
- draftCount() { return this.draftWishes.length },
|
|
|
- pendingCount() { return this.pendingExchangeWishes.length },
|
|
|
- approvedCount() { return this.approvedWishes.length },
|
|
|
- exchangingCount() { return this.exchangingWishes.length }
|
|
|
- },
|
|
|
- mounted() {
|
|
|
- this.loadPendingWishes()
|
|
|
- },
|
|
|
- methods: {
|
|
|
- handleActionCmd(row, cmd) {
|
|
|
- switch (cmd) {
|
|
|
- case 'price': this.showPriceDialog(row); break;
|
|
|
- case 'reject': this.handleReject(row); break;
|
|
|
- case 'approve': this.handleApproveExchange(row, true); break;
|
|
|
- case 'rejectExchange': this.handleApproveExchange(row, false); break;
|
|
|
- case 'fulfill': this.handleFulfill(row); break;
|
|
|
- }
|
|
|
- },
|
|
|
- async loadPendingWishes() {
|
|
|
- this.loading = true
|
|
|
- try {
|
|
|
- // 获取当前用户家庭ID
|
|
|
- const familyId = localStorage.getItem('familyId')
|
|
|
- if (familyId) {
|
|
|
- const res = await getPendingWishes(familyId)
|
|
|
- this.pendingWishes = res.data || []
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- this.$message.error(error.message || '加载待处理列表失败')
|
|
|
- } finally {
|
|
|
- this.loading = false
|
|
|
- }
|
|
|
- },
|
|
|
- async loadWishes() {
|
|
|
- this.loading = true
|
|
|
- try {
|
|
|
- const params = {
|
|
|
- page: this.pagination.current,
|
|
|
- size: this.pagination.size,
|
|
|
- familyId: this.searchForm.familyId || undefined,
|
|
|
- status: this.searchForm.status || undefined
|
|
|
- }
|
|
|
- const res = await getWishesList(params)
|
|
|
- this.tableData = res.data.records || res.data || []
|
|
|
- this.pagination.total = res.data.total || this.tableData.length
|
|
|
- } catch (error) {
|
|
|
- this.$message.error(error.message || '加载心愿列表失败')
|
|
|
- } finally {
|
|
|
- this.loading = false
|
|
|
- }
|
|
|
- },
|
|
|
- async loadFamilies(page = 1) {
|
|
|
- this.familyLoading = true
|
|
|
- try {
|
|
|
- const res = await getFamilyList({ page, size: this.familyPagination.size })
|
|
|
- this.familyList = res.data.records || res.data || []
|
|
|
- this.familyPagination.total = res.data.total || this.familyList.length
|
|
|
- this.familyPagination.current = page
|
|
|
- } catch (error) {
|
|
|
- this.$message.error(error.message || '加载家庭列表失败')
|
|
|
- } finally {
|
|
|
- this.familyLoading = false
|
|
|
- }
|
|
|
- },
|
|
|
- selectFamily(row) {
|
|
|
- this.searchForm.familyId = row.id
|
|
|
- this.searchForm.familyName = row.name
|
|
|
- this.familyDialogVisible = false
|
|
|
- this.handleSearch()
|
|
|
- },
|
|
|
- showFamilySelector() {
|
|
|
- this.familyDialogVisible = true
|
|
|
- this.loadFamilies()
|
|
|
- },
|
|
|
- clearFamily() {
|
|
|
- this.searchForm.familyId = ''
|
|
|
- this.searchForm.familyName = ''
|
|
|
- this.handleSearch()
|
|
|
- },
|
|
|
- handleTabClick(tab) {
|
|
|
- if (tab.name === 'all' && this.tableData.length === 0) {
|
|
|
- this.loadWishes()
|
|
|
- }
|
|
|
- },
|
|
|
- handleSearch() {
|
|
|
- this.pagination.current = 1
|
|
|
- this.loadWishes()
|
|
|
- },
|
|
|
- handleReset() {
|
|
|
- this.searchForm = { status: '', familyId: '', familyName: '' }
|
|
|
- this.handleSearch()
|
|
|
- },
|
|
|
- handleRefresh() {
|
|
|
- if (this.activeTab === 'pending') {
|
|
|
- this.loadPendingWishes()
|
|
|
- } else {
|
|
|
- this.loadWishes()
|
|
|
- }
|
|
|
- },
|
|
|
- handleSizeChange(val) {
|
|
|
- this.pagination.size = val
|
|
|
- this.loadWishes()
|
|
|
- },
|
|
|
- handleCurrentChange(val) {
|
|
|
- this.pagination.current = val
|
|
|
- this.loadWishes()
|
|
|
- },
|
|
|
- showPriceDialog(wish) {
|
|
|
- this.currentWish = wish
|
|
|
- this.priceValue = wish.pointsRequired || 100
|
|
|
- this.priceDialogVisible = true
|
|
|
- },
|
|
|
- async submitPrice() {
|
|
|
- if (!this.priceValue || this.priceValue <= 0) {
|
|
|
- this.$message.warning('请输入有效积分')
|
|
|
- return
|
|
|
- }
|
|
|
- this.submitting = true
|
|
|
- try {
|
|
|
- await setWishPrice(this.currentWish.id, this.priceValue)
|
|
|
- this.$message.success('定价成功')
|
|
|
- this.priceDialogVisible = false
|
|
|
- this.loadPendingWishes()
|
|
|
- } catch (error) {
|
|
|
- this.$message.error(error.message || '定价失败')
|
|
|
- } finally {
|
|
|
- this.submitting = false
|
|
|
- }
|
|
|
- },
|
|
|
- async handleReject(row) {
|
|
|
- this.$confirm(`确定要拒绝 "${row.title}" 吗?`, '提示', {
|
|
|
- confirmButtonText: '确定',
|
|
|
- cancelButtonText: '取消',
|
|
|
- type: 'warning'
|
|
|
- }).then(async () => {
|
|
|
- try {
|
|
|
- await rejectWish(row.id, '不符合要求')
|
|
|
- this.$message.success('已拒绝')
|
|
|
- this.loadPendingWishes()
|
|
|
- } catch (error) {
|
|
|
- this.$message.error(error.message || '操作失败')
|
|
|
- }
|
|
|
- }).catch(() => {})
|
|
|
- },
|
|
|
- async handleApproveExchange(row, approved) {
|
|
|
- const action = approved ? '批准' : '拒绝'
|
|
|
- this.$confirm(`确定要${action} "${row.title}" 的兑换申请吗?`, '提示', {
|
|
|
- confirmButtonText: '确定',
|
|
|
- cancelButtonText: '取消',
|
|
|
- type: 'warning'
|
|
|
- }).then(async () => {
|
|
|
- try {
|
|
|
- await approveWishExchange(row.id, approved)
|
|
|
- this.$message.success(`已${action}`)
|
|
|
- this.loadPendingWishes()
|
|
|
- } catch (error) {
|
|
|
- this.$message.error(error.message || '操作失败')
|
|
|
- }
|
|
|
- }).catch(() => {})
|
|
|
- },
|
|
|
- async handleFulfill(row) {
|
|
|
- this.$confirm(`确定已购买 "${row.title}" 吗?`, '提示', {
|
|
|
- confirmButtonText: '确定',
|
|
|
- cancelButtonText: '取消',
|
|
|
- type: 'warning'
|
|
|
- }).then(async () => {
|
|
|
- try {
|
|
|
- await markWishFulfilled(row.id)
|
|
|
- this.$message.success('已标记')
|
|
|
- this.loadPendingWishes()
|
|
|
- } catch (error) {
|
|
|
- this.$message.error(error.message || '操作失败')
|
|
|
- }
|
|
|
- }).catch(() => {})
|
|
|
- },
|
|
|
- getStatusType(status) {
|
|
|
- const types = {
|
|
|
- draft: 'info',
|
|
|
- active: 'success',
|
|
|
- pending_exchange: 'warning',
|
|
|
- approved: 'primary',
|
|
|
- exchanging: 'primary',
|
|
|
- fulfilled: 'success',
|
|
|
- cancelled: 'info',
|
|
|
- rejected: 'danger'
|
|
|
- }
|
|
|
- return types[status] || 'info'
|
|
|
- },
|
|
|
- formatDate(dateStr) {
|
|
|
- if (!dateStr) return '-'
|
|
|
- const date = new Date(dateStr)
|
|
|
- return date.toLocaleString('zh-CN')
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-</script>
|
|
|
-
|
|
|
-<style scoped>
|
|
|
-.page-container {
|
|
|
- padding: 20px;
|
|
|
-}
|
|
|
-
|
|
|
-.stat-card {
|
|
|
- text-align: center;
|
|
|
- cursor: pointer;
|
|
|
-}
|
|
|
-
|
|
|
-.stat-card.draft { border-top: 3px solid #909399; }
|
|
|
-.stat-card.pending { border-top: 3px solid #E6A23C; }
|
|
|
-.stat-card.approved { border-top: 3px solid #409EFF; }
|
|
|
-.stat-card.exchanging { border-top: 3px solid #409EFF; }
|
|
|
-
|
|
|
-.stat-num {
|
|
|
- font-size: 32px;
|
|
|
- font-weight: bold;
|
|
|
- color: #333;
|
|
|
-}
|
|
|
-
|
|
|
-.stat-label {
|
|
|
- font-size: 14px;
|
|
|
- color: #666;
|
|
|
- margin-top: 5px;
|
|
|
-}
|
|
|
-
|
|
|
-.wish-section {
|
|
|
- margin-bottom: 30px;
|
|
|
-}
|
|
|
-
|
|
|
-.section-title {
|
|
|
- font-size: 16px;
|
|
|
- font-weight: bold;
|
|
|
- margin-bottom: 15px;
|
|
|
- color: #333;
|
|
|
-}
|
|
|
-
|
|
|
-.no-price {
|
|
|
- color: #999;
|
|
|
- font-size: 12px;
|
|
|
-}
|
|
|
+<template>
|
|
|
+ <div class="page-container">
|
|
|
+ <el-card>
|
|
|
+ <div slot="header">
|
|
|
+ <span>心愿管理</span>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ style="float: right"
|
|
|
+ @click="handleRefresh"
|
|
|
+ :loading="loading"
|
|
|
+ >
|
|
|
+ 刷新
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
|
|
+ <!-- 待处理列表 -->
|
|
|
+ <el-tab-pane label="待处理" name="pending">
|
|
|
+ <!-- 统计卡片 -->
|
|
|
+ <el-row :gutter="20" style="margin-bottom: 20px">
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card shadow="hover" class="stat-card draft">
|
|
|
+ <div class="stat-num">{{ draftCount }}</div>
|
|
|
+ <div class="stat-label">待定价</div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card shadow="hover" class="stat-card pending">
|
|
|
+ <div class="stat-num">{{ pendingCount }}</div>
|
|
|
+ <div class="stat-label">待审批</div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card shadow="hover" class="stat-card approved">
|
|
|
+ <div class="stat-num">{{ approvedCount }}</div>
|
|
|
+ <div class="stat-label">待兑现</div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card shadow="hover" class="stat-card exchanging">
|
|
|
+ <div class="stat-num">{{ exchangingCount }}</div>
|
|
|
+ <div class="stat-label">待收货</div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 待定价列表 -->
|
|
|
+ <div v-if="draftWishes.length > 0" class="wish-section">
|
|
|
+ <div class="section-title">📝 待定价</div>
|
|
|
+ <div style="overflow:auto;max-height:calc(100vh - 300px);">
|
|
|
+<el-table style="max-height:calc(100vh - 300px);" :data="draftWishes" border v-loading="loading" style="width: 100%">
|
|
|
+ <el-table-column prop="id" label="ID" width="80"></el-table-column>
|
|
|
+ <el-table-column prop="childName" label="孩子" width="100"></el-table-column>
|
|
|
+ <el-table-column prop="title" label="心愿名称" min-width="200"></el-table-column>
|
|
|
+ <el-table-column prop="category" label="分类" width="100"></el-table-column>
|
|
|
+ <el-table-column prop="createdAt" label="创建时间" width="180">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ formatDate(scope.row.createdAt) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="200" fixed="right">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-button size="mini" type="primary" @click="showPriceDialog(row)">定价</el-button>
|
|
|
+ <el-dropdown trigger="hover" @command="(cmd) => handleActionCmd(row, cmd)">
|
|
|
+ <el-button size="mini">
|
|
|
+ 更多<i class="el-icon-arrow-down el-icon--right"></i>
|
|
|
+ </el-button>
|
|
|
+ <el-dropdown-menu slot="dropdown">
|
|
|
+ <el-dropdown-item command="reject" icon="el-icon-close">拒绝</el-dropdown-item>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </el-dropdown>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 待审批兑换列表 -->
|
|
|
+ <div v-if="pendingExchangeWishes.length > 0" class="wish-section">
|
|
|
+ <div class="section-title">⏳ 待审批兑换</div>
|
|
|
+ <el-table :data="pendingExchangeWishes" border v-loading="loading" style="width: 100%">
|
|
|
+ <el-table-column prop="id" label="ID" width="80"></el-table-column>
|
|
|
+ <el-table-column prop="childName" label="孩子" width="100"></el-table-column>
|
|
|
+ <el-table-column prop="title" label="心愿名称" min-width="200"></el-table-column>
|
|
|
+ <el-table-column prop="pointsRequired" label="所需积分" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag type="warning">{{ scope.row.pointsRequired }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="exchangeRequestedAt" label="申请时间" width="180">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ formatDate(scope.row.exchangeRequestedAt) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="200" fixed="right">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-button size="mini" type="primary" @click="handleApproveExchange(row, true)">批准</el-button>
|
|
|
+ <el-dropdown trigger="hover" @command="(cmd) => handleActionCmd(row, cmd)">
|
|
|
+ <el-button size="mini">
|
|
|
+ 更多<i class="el-icon-arrow-down el-icon--right"></i>
|
|
|
+ </el-button>
|
|
|
+ <el-dropdown-menu slot="dropdown">
|
|
|
+ <el-dropdown-item command="rejectExchange" icon="el-icon-close">拒绝</el-dropdown-item>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </el-dropdown>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 待兑现列表 -->
|
|
|
+ <div v-if="approvedWishes.length > 0" class="wish-section">
|
|
|
+ <div class="section-title">📦 待兑现</div>
|
|
|
+ <el-table :data="approvedWishes" border v-loading="loading" style="width: 100%">
|
|
|
+ <el-table-column prop="id" label="ID" width="80"></el-table-column>
|
|
|
+ <el-table-column prop="childName" label="孩子" width="100"></el-table-column>
|
|
|
+ <el-table-column prop="title" label="心愿名称" min-width="200"></el-table-column>
|
|
|
+ <el-table-column prop="pointsRequired" label="已扣积分" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag type="success">{{ scope.row.pointsRequired }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="exchangeApprovedAt" label="批准时间" width="180">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ formatDate(scope.row.exchangeApprovedAt) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="120" fixed="right">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-button size="mini" @click="handleFulfill(row)">已购买</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 待收货列表 -->
|
|
|
+ <div v-if="exchangingWishes.length > 0" class="wish-section">
|
|
|
+ <div class="section-title">🎁 待收货确认</div>
|
|
|
+ <el-table :data="exchangingWishes" border v-loading="loading" style="width: 100%">
|
|
|
+ <el-table-column prop="id" label="ID" width="80"></el-table-column>
|
|
|
+ <el-table-column prop="childName" label="孩子" width="100"></el-table-column>
|
|
|
+ <el-table-column prop="title" label="心愿名称" min-width="200"></el-table-column>
|
|
|
+ <el-table-column prop="fulfilledAt" label="购买时间" width="180">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ formatDate(scope.row.fulfilledAt) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="状态" width="100">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag type="info">等待确认</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 空状态 -->
|
|
|
+ <el-empty v-if="pendingWishes.length === 0" description="暂无待处理心愿"></el-empty>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 全部心愿 -->
|
|
|
+ <el-tab-pane label="全部心愿" name="all">
|
|
|
+ <!-- 搜索筛选 -->
|
|
|
+ <el-form :inline="true" :model="searchForm" style="margin-bottom: 20px">
|
|
|
+ <el-form-item label="家庭">
|
|
|
+ <el-input
|
|
|
+ v-model="searchForm.familyName"
|
|
|
+ placeholder="点击选择家庭"
|
|
|
+ readonly
|
|
|
+ @click.native="showFamilySelector"
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ slot="append"
|
|
|
+ icon="el-icon-close"
|
|
|
+ v-if="searchForm.familyId"
|
|
|
+ @click.stop="clearFamily"
|
|
|
+ style="padding: 8px"
|
|
|
+ ></el-button>
|
|
|
+ <i slot="suffix" class="el-input__icon el-icon-arrow-down" @click.stop="showFamilySelector"></i>
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="状态">
|
|
|
+ <el-select v-model="searchForm.status" placeholder="全部状态" clearable @change="handleSearch">
|
|
|
+ <el-option label="待定价" value="draft"></el-option>
|
|
|
+ <el-option label="积攒中" value="active"></el-option>
|
|
|
+ <el-option label="待审批" value="pending_exchange"></el-option>
|
|
|
+ <el-option label="已批准" value="approved"></el-option>
|
|
|
+ <el-option label="待收货" value="exchanging"></el-option>
|
|
|
+ <el-option label="已完成" value="fulfilled"></el-option>
|
|
|
+ <el-option label="已拒绝" value="rejected"></el-option>
|
|
|
+ <el-option label="已取消" value="cancelled"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" @click="handleSearch">搜索</el-button>
|
|
|
+ <el-button @click="handleReset">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <!-- 家庭选择对话框 -->
|
|
|
+ <el-dialog title="选择家庭" :visible.sync="familyDialogVisible" width="600px">
|
|
|
+ <el-table :data="familyList" border v-loading="familyLoading" @row-click="selectFamily" style="cursor: pointer">
|
|
|
+ <el-table-column prop="id" label="ID" width="80"></el-table-column>
|
|
|
+ <el-table-column prop="name" label="家庭名称" min-width="150"></el-table-column>
|
|
|
+ <el-table-column prop="inviteCode" label="邀请码" width="120"></el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <el-pagination
|
|
|
+ style="margin-top: 15px; text-align: right"
|
|
|
+ @current-change="loadFamilies"
|
|
|
+ :current-page="familyPagination.current"
|
|
|
+ :page-size="familyPagination.size"
|
|
|
+ :total="familyPagination.total"
|
|
|
+ layout="total, prev, pager, next"
|
|
|
+ ></el-pagination>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <el-table :data="tableData" border v-loading="loading" style="width: 100%">
|
|
|
+ <el-table-column prop="id" label="ID" width="80"></el-table-column>
|
|
|
+ <el-table-column prop="childName" label="孩子" width="100"></el-table-column>
|
|
|
+ <el-table-column prop="title" label="心愿名称" min-width="200"></el-table-column>
|
|
|
+ <el-table-column prop="pointsRequired" label="所需积分" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag v-if="scope.row.pointsRequired" type="warning">{{ scope.row.pointsRequired }}</el-tag>
|
|
|
+ <span v-else class="no-price">未定价</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="category" label="分类" width="100"></el-table-column>
|
|
|
+ <el-table-column prop="status" label="状态" width="100">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag :type="getStatusType(scope.row.status)" size="small">
|
|
|
+ {{ scope.row.statusText }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="creatorType" label="创建者" width="80">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag :type="scope.row.creatorType === 'parent' ? 'primary' : 'success'" size="small">
|
|
|
+ {{ scope.row.creatorType === 'parent' ? '家长' : '孩子' }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="createdAt" label="创建时间" width="180">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ formatDate(scope.row.createdAt) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <el-pagination
|
|
|
+ style="margin-top: 20px; text-align: right"
|
|
|
+ @size-change="handleSizeChange"
|
|
|
+ @current-change="handleCurrentChange"
|
|
|
+ :current-page="pagination.current"
|
|
|
+ :page-sizes="[10, 20, 50, 100]"
|
|
|
+ :page-size="pagination.size"
|
|
|
+ :total="pagination.total"
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ ></el-pagination>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 定价对话框 -->
|
|
|
+ <el-dialog title="设置积分" :visible.sync="priceDialogVisible" width="400px">
|
|
|
+ <el-form label-width="100px">
|
|
|
+ <el-form-item label="心愿名称">
|
|
|
+ <span>{{ currentWish && currentWish.title }}</span>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="所需积分">
|
|
|
+ <el-input-number v-model="priceValue" :min="1" :max="10000"></el-input-number>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer">
|
|
|
+ <el-button @click="priceDialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="submitPrice" :loading="submitting">确定</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import {
|
|
|
+ getWishesList,
|
|
|
+ getPendingWishes,
|
|
|
+ setWishPrice,
|
|
|
+ rejectWish,
|
|
|
+ approveWishExchange,
|
|
|
+ markWishFulfilled,
|
|
|
+ getFamilyList
|
|
|
+} from '@/api/admin'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'Wishes',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ activeTab: 'pending',
|
|
|
+ loading: false,
|
|
|
+ submitting: false,
|
|
|
+ tableData: [],
|
|
|
+ pendingWishes: [],
|
|
|
+ searchForm: {
|
|
|
+ status: '',
|
|
|
+ familyId: '',
|
|
|
+ familyName: ''
|
|
|
+ },
|
|
|
+ pagination: {
|
|
|
+ current: 1,
|
|
|
+ size: 10,
|
|
|
+ total: 0
|
|
|
+ },
|
|
|
+ // 家庭选择对话框
|
|
|
+ familyDialogVisible: false,
|
|
|
+ familyLoading: false,
|
|
|
+ familyList: [],
|
|
|
+ familyPagination: {
|
|
|
+ current: 1,
|
|
|
+ size: 10,
|
|
|
+ total: 0
|
|
|
+ },
|
|
|
+ // 定价对话框
|
|
|
+ priceDialogVisible: false,
|
|
|
+ currentWish: null,
|
|
|
+ priceValue: 100
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ draftWishes() {
|
|
|
+ return this.pendingWishes.filter(w => w.status === 'draft')
|
|
|
+ },
|
|
|
+ pendingExchangeWishes() {
|
|
|
+ return this.pendingWishes.filter(w => w.status === 'pending_exchange')
|
|
|
+ },
|
|
|
+ approvedWishes() {
|
|
|
+ return this.pendingWishes.filter(w => w.status === 'approved')
|
|
|
+ },
|
|
|
+ exchangingWishes() {
|
|
|
+ return this.pendingWishes.filter(w => w.status === 'exchanging')
|
|
|
+ },
|
|
|
+ draftCount() { return this.draftWishes.length },
|
|
|
+ pendingCount() { return this.pendingExchangeWishes.length },
|
|
|
+ approvedCount() { return this.approvedWishes.length },
|
|
|
+ exchangingCount() { return this.exchangingWishes.length }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.loadPendingWishes()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ handleActionCmd(row, cmd) {
|
|
|
+ switch (cmd) {
|
|
|
+ case 'price': this.showPriceDialog(row); break;
|
|
|
+ case 'reject': this.handleReject(row); break;
|
|
|
+ case 'approve': this.handleApproveExchange(row, true); break;
|
|
|
+ case 'rejectExchange': this.handleApproveExchange(row, false); break;
|
|
|
+ case 'fulfill': this.handleFulfill(row); break;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async loadPendingWishes() {
|
|
|
+ this.loading = true
|
|
|
+ try {
|
|
|
+ // 获取当前用户家庭ID
|
|
|
+ const familyId = localStorage.getItem('familyId')
|
|
|
+ if (familyId) {
|
|
|
+ const res = await getPendingWishes(familyId)
|
|
|
+ this.pendingWishes = res.data || []
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(error.message || '加载待处理列表失败')
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async loadWishes() {
|
|
|
+ this.loading = true
|
|
|
+ try {
|
|
|
+ const params = {
|
|
|
+ page: this.pagination.current,
|
|
|
+ size: this.pagination.size,
|
|
|
+ familyId: this.searchForm.familyId || undefined,
|
|
|
+ status: this.searchForm.status || undefined
|
|
|
+ }
|
|
|
+ const res = await getWishesList(params)
|
|
|
+ this.tableData = res.data.records || res.data || []
|
|
|
+ this.pagination.total = res.data.total || this.tableData.length
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(error.message || '加载心愿列表失败')
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async loadFamilies(page = 1) {
|
|
|
+ this.familyLoading = true
|
|
|
+ try {
|
|
|
+ const res = await getFamilyList({ page, size: this.familyPagination.size })
|
|
|
+ this.familyList = res.data.records || res.data || []
|
|
|
+ this.familyPagination.total = res.data.total || this.familyList.length
|
|
|
+ this.familyPagination.current = page
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(error.message || '加载家庭列表失败')
|
|
|
+ } finally {
|
|
|
+ this.familyLoading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ selectFamily(row) {
|
|
|
+ this.searchForm.familyId = row.id
|
|
|
+ this.searchForm.familyName = row.name
|
|
|
+ this.familyDialogVisible = false
|
|
|
+ this.handleSearch()
|
|
|
+ },
|
|
|
+ showFamilySelector() {
|
|
|
+ this.familyDialogVisible = true
|
|
|
+ this.loadFamilies()
|
|
|
+ },
|
|
|
+ clearFamily() {
|
|
|
+ this.searchForm.familyId = ''
|
|
|
+ this.searchForm.familyName = ''
|
|
|
+ this.handleSearch()
|
|
|
+ },
|
|
|
+ handleTabClick(tab) {
|
|
|
+ if (tab.name === 'all' && this.tableData.length === 0) {
|
|
|
+ this.loadWishes()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleSearch() {
|
|
|
+ this.pagination.current = 1
|
|
|
+ this.loadWishes()
|
|
|
+ },
|
|
|
+ handleReset() {
|
|
|
+ this.searchForm = { status: '', familyId: '', familyName: '' }
|
|
|
+ this.handleSearch()
|
|
|
+ },
|
|
|
+ handleRefresh() {
|
|
|
+ if (this.activeTab === 'pending') {
|
|
|
+ this.loadPendingWishes()
|
|
|
+ } else {
|
|
|
+ this.loadWishes()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleSizeChange(val) {
|
|
|
+ this.pagination.size = val
|
|
|
+ this.loadWishes()
|
|
|
+ },
|
|
|
+ handleCurrentChange(val) {
|
|
|
+ this.pagination.current = val
|
|
|
+ this.loadWishes()
|
|
|
+ },
|
|
|
+ showPriceDialog(wish) {
|
|
|
+ this.currentWish = wish
|
|
|
+ this.priceValue = wish.pointsRequired || 100
|
|
|
+ this.priceDialogVisible = true
|
|
|
+ },
|
|
|
+ async submitPrice() {
|
|
|
+ if (!this.priceValue || this.priceValue <= 0) {
|
|
|
+ this.$message.warning('请输入有效积分')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.submitting = true
|
|
|
+ try {
|
|
|
+ await setWishPrice(this.currentWish.id, this.priceValue)
|
|
|
+ this.$message.success('定价成功')
|
|
|
+ this.priceDialogVisible = false
|
|
|
+ this.loadPendingWishes()
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(error.message || '定价失败')
|
|
|
+ } finally {
|
|
|
+ this.submitting = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async handleReject(row) {
|
|
|
+ this.$confirm(`确定要拒绝 "${row.title}" 吗?`, '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(async () => {
|
|
|
+ try {
|
|
|
+ await rejectWish(row.id, '不符合要求')
|
|
|
+ this.$message.success('已拒绝')
|
|
|
+ this.loadPendingWishes()
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(error.message || '操作失败')
|
|
|
+ }
|
|
|
+ }).catch(() => {})
|
|
|
+ },
|
|
|
+ async handleApproveExchange(row, approved) {
|
|
|
+ const action = approved ? '批准' : '拒绝'
|
|
|
+ this.$confirm(`确定要${action} "${row.title}" 的兑换申请吗?`, '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(async () => {
|
|
|
+ try {
|
|
|
+ await approveWishExchange(row.id, approved)
|
|
|
+ this.$message.success(`已${action}`)
|
|
|
+ this.loadPendingWishes()
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(error.message || '操作失败')
|
|
|
+ }
|
|
|
+ }).catch(() => {})
|
|
|
+ },
|
|
|
+ async handleFulfill(row) {
|
|
|
+ this.$confirm(`确定已购买 "${row.title}" 吗?`, '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(async () => {
|
|
|
+ try {
|
|
|
+ await markWishFulfilled(row.id)
|
|
|
+ this.$message.success('已标记')
|
|
|
+ this.loadPendingWishes()
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(error.message || '操作失败')
|
|
|
+ }
|
|
|
+ }).catch(() => {})
|
|
|
+ },
|
|
|
+ getStatusType(status) {
|
|
|
+ const types = {
|
|
|
+ draft: 'info',
|
|
|
+ active: 'success',
|
|
|
+ pending_exchange: 'warning',
|
|
|
+ approved: 'primary',
|
|
|
+ exchanging: 'primary',
|
|
|
+ fulfilled: 'success',
|
|
|
+ cancelled: 'info',
|
|
|
+ rejected: 'danger'
|
|
|
+ }
|
|
|
+ return types[status] || 'info'
|
|
|
+ },
|
|
|
+ formatDate(dateStr) {
|
|
|
+ if (!dateStr) return '-'
|
|
|
+ const date = new Date(dateStr)
|
|
|
+ return date.toLocaleString('zh-CN')
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.page-container {
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card {
|
|
|
+ text-align: center;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card.draft { border-top: 3px solid #909399; }
|
|
|
+.stat-card.pending { border-top: 3px solid #E6A23C; }
|
|
|
+.stat-card.approved { border-top: 3px solid #409EFF; }
|
|
|
+.stat-card.exchanging { border-top: 3px solid #409EFF; }
|
|
|
+
|
|
|
+.stat-num {
|
|
|
+ font-size: 32px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+ margin-top: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.wish-section {
|
|
|
+ margin-bottom: 30px;
|
|
|
+}
|
|
|
+
|
|
|
+.section-title {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-bottom: 15px;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.no-price {
|
|
|
+ color: #999;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
</style>
|