| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- <template>
- <view class="container">
- <view class="header">
- <text class="title">测评方案审核</text>
- <view class="header-actions">
- <picker :range="['全部', '待审核(generated)', '已审核(reviewed)', '已激活(active)', '已完成(completed)']" @change="onStatusFilter">
- <view class="filter-btn">
- <text>{{ statusFilterText }}</text>
- </view>
- </picker>
- </view>
- </view>
- <view class="family-section" v-for="group in planGroups" :key="group.familyId">
- <view class="family-header">
- <text class="family-name">家庭 ID: {{ group.familyId }}</text>
- <text class="plan-count">{{ group.plans.length }} 个方案</text>
- </view>
- <view class="plan-card" v-for="plan in group.plans" :key="plan.id" @click="goDetail(plan)">
- <view class="plan-header">
- <text class="plan-title">{{ plan.name || '测评方案' }}</text>
- <text class="plan-status" :class="'status-' + plan.status">{{ statusLabel(plan.status) }}</text>
- </view>
- <view class="plan-meta">
- <text>孩子ID: {{ plan.childId }}</text>
- <text>{{ formatDate(plan.createdAt) }}</text>
- </view>
- <view class="plan-desc" v-if="plan.remark">{{ plan.remark }}</view>
- <view class="plan-actions">
- <view class="action-btn primary" v-if="plan.status === 'generated'" @click.stop="handleReview(plan, true)">
- <text>批准</text>
- </view>
- <view class="action-btn danger" v-if="plan.status === 'generated'" @click.stop="handleReview(plan, false)">
- <text>驳回</text>
- </view>
- <view class="action-btn success" v-if="plan.status === 'reviewed'" @click.stop="handleActivate(plan)">
- <text>激活</text>
- </view>
- </view>
- </view>
- </view>
- <view class="empty" v-if="planGroups.length === 0">
- <text>暂无方案</text>
- </view>
- <uni-popup ref="reviewPopup" type="dialog">
- <uni-popup-dialog title="审核意见" :content="reviewComment" @confirm="submitReview" @close="reviewComment = ''">
- <input class="review-input" v-model="reviewComment" placeholder="输入审核意见(可选)" />
- </uni-popup-dialog>
- </uni-popup>
- </view>
- </template>
- <script>
- import { listFamilyPlans, reviewPlan, activatePlan, getGuidePlanDetail } from '@/utils/api'
- export default {
- data() {
- return {
- plans: [],
- statusFilter: '',
- statusFilterText: '全部状态',
- currentPlan: null,
- reviewApproved: false,
- reviewComment: '',
- families: []
- }
- },
- computed: {
- planGroups() {
- const groups = {}
- for (const p of this.plans) {
- const key = p.familyId
- if (!groups[key]) groups[key] = { familyId: key, plans: [] }
- groups[key].plans.push(p)
- }
- return Object.values(groups)
- }
- },
- onShow() {
- this.loadPlans()
- },
- methods: {
- async loadPlans() {
- try {
- const res = await listFamilyPlans({ status: this.statusFilter || null })
- if (res.code === 200) this.plans = res.data || []
- } catch (e) { /* ignore */ }
- },
- onStatusFilter(e) {
- const labels = ['全部', '待审核(generated)', '已审核(reviewed)', '已激活(active)', '已完成(completed)']
- const vals = ['', 'generated', 'reviewed', 'active', 'completed']
- const idx = e.detail.value
- this.statusFilterText = labels[idx]
- this.statusFilter = vals[idx]
- this.loadPlans()
- },
- statusLabel(s) {
- const map = { generated: '待审核', reviewed: '已审核', active: '已激活', completed: '已完成', rejected: '已驳回' }
- return map[s] || s
- },
- formatDate(d) {
- if (!d) return ''
- const date = new Date(d)
- return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate()
- },
- goDetail(plan) {
- uni.navigateTo({ url: '/pages/guide/plans/detail?planId=' + plan.id })
- },
- handleReview(plan, approved) {
- this.currentPlan = plan
- this.reviewApproved = approved
- this.reviewComment = ''
- if (!approved) {
- uni.showModal({
- title: '驳回方案',
- content: '确定驳回该方案吗?',
- success: (res) => {
- if (res.confirm) this.submitReview()
- }
- })
- } else {
- this.submitReview()
- }
- },
- async submitReview() {
- if (!this.currentPlan) return
- try {
- const res = await reviewPlan({
- planId: this.currentPlan.id,
- approved: this.reviewApproved,
- comment: this.reviewComment
- })
- if (res.code === 200) {
- uni.showToast({ title: '操作成功' })
- this.loadPlans()
- }
- } catch (e) { /* ignore */ }
- },
- handleActivate(plan) {
- uni.showModal({
- title: '激活方案',
- content: '激活后将生成任务,确定激活?',
- success: async (res) => {
- if (!res.confirm) return
- try {
- const r = await activatePlan({ planId: plan.id })
- if (r.code === 200) {
- uni.showToast({ title: '方案已激活' })
- this.loadPlans()
- }
- } catch (e) { /* ignore */ }
- }
- })
- }
- }
- }
- </script>
- <style scoped>
- .container { min-height: 100vh; background: #f5f5f5; padding: 30rpx; }
- .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 30rpx; }
- .title { font-size: 36rpx; font-weight: bold; }
- .filter-btn { padding: 10rpx 20rpx; background: #fff; border-radius: 8rpx; font-size: 26rpx; }
- .family-section { margin-bottom: 30rpx; }
- .family-header { display: flex; justify-content: space-between; padding: 20rpx; background: #e8f4fd; border-radius: 12rpx 12rpx 0 0; }
- .family-name { font-weight: bold; font-size: 28rpx; }
- .plan-count { font-size: 24rpx; color: #999; }
- .plan-card { background: #fff; padding: 24rpx; border-bottom: 2rpx solid #f0f0f0; }
- .plan-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16rpx; }
- .plan-title { font-size: 30rpx; font-weight: bold; }
- .plan-status { font-size: 24rpx; padding: 4rpx 12rpx; border-radius: 6rpx; }
- .status-generated { background: #fff3cd; color: #856404; }
- .status-reviewed { background: #d1ecf1; color: #0c5460; }
- .status-active { background: #d4edda; color: #155724; }
- .status-completed { background: #e2e3e5; color: #383d41; }
- .status-rejected { background: #f8d7da; color: #721c24; }
- .plan-meta { font-size: 24rpx; color: #999; display: flex; justify-content: space-between; margin-bottom: 12rpx; }
- .plan-desc { font-size: 26rpx; color: #666; margin-bottom: 16rpx; }
- .plan-actions { display: flex; gap: 20rpx; }
- .action-btn { padding: 10rpx 24rpx; border-radius: 8rpx; font-size: 26rpx; }
- .action-btn.primary { background: #4A9BD7; color: #fff; }
- .action-btn.danger { background: #e74c3c; color: #fff; }
- .action-btn.success { background: #27ae60; color: #fff; }
- .empty { text-align: center; padding: 100rpx 0; color: #999; font-size: 30rpx; }
- .review-input { border: 2rpx solid #ddd; border-radius: 8rpx; padding: 16rpx; margin-top: 20rpx; }
- </style>
|