| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- <template>
- <view class="page">
- <view class="card">
- <text class="title">{{ planTitle }}</text>
- <text class="meta">创建于 {{ formatTime(plan.createdAt) }}</text>
- <text class="meta" v-if="plan.teacherId">审核规划师:{{ plan.teacherId }}</text>
- <text class="meta" v-else>未分配规划师</text>
- </view>
- <view class="card">
- <text class="label">方案正文</text>
- <textarea
- class="editor"
- v-model="planContent"
- :disabled="!editing"
- placeholder="方案正文"
- />
- </view>
- <view class="actions">
- <view class="btn ghost" @click="toggleEdit">{{ editing ? '取消编辑' : '编辑' }}</view>
- <view class="btn ghost" v-if="editing" @click="save">保存</view>
- <view class="btn ok" @click="confirmThen('approve')">通过</view>
- <view class="btn no" @click="confirmThen('reject')">驳回</view>
- </view>
- </view>
- </template>
- <script>
- import {
- updateGuidePlanContent,
- approveGuidePlan,
- rejectGuidePlan,
- getGuidePendingPlans
- } from '@/utils/api.js'
- export default {
- data() {
- return {
- familyId: null,
- planId: null,
- plan: {},
- planContent: '',
- editing: false
- }
- },
- computed: {
- planTitle() {
- // HealthPlan 实体无 title 字段,只能依次回退到 goal / memberName
- return this.plan.goal || this.plan.memberName || '健康方案'
- }
- },
- onLoad(options) {
- this.familyId = options.familyId
- this.planId = options.planId
- this.fetchPlan()
- },
- methods: {
- async fetchPlan() {
- try {
- const res = await getGuidePendingPlans(this.familyId)
- if (res && res.code === 200) {
- const list = res.data || []
- for (let i = 0; i < list.length; i++) {
- if (String(list[i].id) === String(this.planId)) {
- this.plan = list[i]
- this.planContent = list[i].planContent || ''
- return
- }
- }
- uni.showToast({ title: '方案不存在或已被审核', icon: 'none' })
- }
- } catch (e) {
- uni.showToast({ title: '加载失败', icon: 'none' })
- }
- },
- toggleEdit() {
- this.editing = !this.editing
- },
- async save() {
- try {
- const res = await updateGuidePlanContent(this.familyId, this.planId, this.planContent)
- if (res && res.code === 200) {
- uni.showToast({ title: '已保存', icon: 'none' })
- this.editing = false
- } else {
- uni.showToast({ title: (res && res.message) || '保存失败', icon: 'none' })
- }
- } catch (e) {
- uni.showToast({ title: '网络异常', icon: 'none' })
- }
- },
- confirmThen(action) {
- uni.showModal({
- title: action === 'approve' ? '确认通过?' : '确认驳回?',
- content: action === 'approve' ? '通过后将自动生成每日任务' : '驳回后家长需重新提交',
- success: (r) => {
- if (!r.confirm) return
- this.doAction(action, '')
- }
- })
- },
- async doAction(action, comment) {
- try {
- const fn = action === 'approve' ? approveGuidePlan : rejectGuidePlan
- const res = await fn(this.familyId, this.planId, comment)
- if (res && res.code === 200) {
- const d = res.data || {}
- // taskGenerated=false 是「已发布但未生成任务」,属部分成功,必须与失败区分开提示
- if (action === 'approve' && d.taskGenerated === false) {
- uni.showModal({
- title: '部分成功',
- content: d.message || '方案已发布,但未生成任何任务,请检查方案内容',
- showCancel: false
- })
- } else {
- uni.showToast({ title: '操作成功', icon: 'none' })
- }
- setTimeout(() => uni.navigateBack(), 1200)
- } else {
- uni.showToast({ title: (res && res.message) || '操作失败', icon: 'none' })
- }
- } catch (e) {
- uni.showToast({ title: '网络异常', icon: 'none' })
- }
- },
- formatTime(t) {
- if (!t) return '-'
- // 项目统一时间格式:取前19位并把 ISO 的 T 换成空格,避免 toLocaleString 的浏览器差异
- return String(t).substring(0, 19).replace('T', ' ')
- }
- }
- }
- </script>
- <style scoped>
- .page { padding: 24rpx; background: #F5FAFE; min-height: 100vh; }
- .card { background: #fff; border-radius: 16rpx; padding: 24rpx; margin-bottom: 20rpx; }
- .title { font-size: 32rpx; font-weight: 600; color: #1F2D3D; display: block; margin-bottom: 12rpx; }
- .meta { font-size: 24rpx; color: #8A94A6; display: block; margin-bottom: 6rpx; }
- .label { font-size: 26rpx; color: #5A6478; display: block; margin-bottom: 12rpx; }
- .editor { width: 100%; min-height: 400rpx; font-size: 28rpx; color: #1F2D3D; padding: 16rpx; background: #F7F9FC; border-radius: 12rpx; }
- .actions { display: flex; flex-direction: row; flex-wrap: wrap; }
- .btn { flex: 1; text-align: center; padding: 20rpx 0; border-radius: 12rpx; font-size: 28rpx; margin-right: 16rpx; margin-bottom: 16rpx; min-width: 40%; }
- .btn.ghost { background: #fff; color: #4A9BD7; border: 1rpx solid #4A9BD7; }
- .btn.ok { background: #4A9BD7; color: #fff; }
- .btn.no { background: #F2F4F7; color: #5A6478; margin-right: 0; }
- </style>
|