|
|
@@ -1,968 +0,0 @@
|
|
|
-<template>
|
|
|
- <view class="container">
|
|
|
- <!-- ===== 看板模式 ===== -->
|
|
|
- <view class="board-section" v-if="mode === 'board' && boardData">
|
|
|
- <view class="board-header">
|
|
|
- <text class="board-title">{{ boardData.title }}</text>
|
|
|
- <text class="board-progress">{{ boardData.progress }}%</text>
|
|
|
- </view>
|
|
|
- <view class="progress-bar-wrap">
|
|
|
- <view class="progress-bar-fill" :style="{ width: boardData.progress + '%' }"></view>
|
|
|
- </view>
|
|
|
- <view class="board-summary">
|
|
|
- <view class="summary-item">
|
|
|
- <text class="summary-value">{{ boardData.completedItems }}</text>
|
|
|
- <text class="summary-label">已完成</text>
|
|
|
- </view>
|
|
|
- <view class="summary-item">
|
|
|
- <text class="summary-value">{{ boardData.totalItems }}</text>
|
|
|
- <text class="summary-label">总任务</text>
|
|
|
- </view>
|
|
|
- <view class="summary-item">
|
|
|
- <text class="summary-value">{{ boardData.totalItems - boardData.completedItems }}</text>
|
|
|
- <text class="summary-label">进行中</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="board-items" v-if="boardData.itemStatus">
|
|
|
- <view class="board-item" v-for="item in boardData.itemStatus" :key="item.itemId">
|
|
|
- <view class="bi-left">
|
|
|
- <view class="bi-check" :class="item.status === 'completed' ? 'done' : ''">
|
|
|
- <text v-if="item.status === 'completed'">✔</text>
|
|
|
- <text v-else>○</text>
|
|
|
- </view>
|
|
|
- <view class="bi-info">
|
|
|
- <text class="bi-dim">{{ dimensionLabel(item.dimensionKey) }}</text>
|
|
|
- <text class="bi-title">{{ item.taskTitle }}</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <view class="bi-right">
|
|
|
- <text class="bi-status" :class="'bi-' + item.status">{{ itemStatusText(item.status) }}</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- ===== 编辑模式 ===== -->
|
|
|
- <view class="edit-section" v-else>
|
|
|
- <!-- 基本信息 -->
|
|
|
- <view class="section-card">
|
|
|
- <view class="section-title">方案信息</view>
|
|
|
- <view class="form-item">
|
|
|
- <text class="form-label">方案标题</text>
|
|
|
- <input class="form-input" v-model="planForm.title" placeholder="请输入方案标题" :disabled="!editable" />
|
|
|
- </view>
|
|
|
- <view class="form-item">
|
|
|
- <text class="form-label">方案描述</text>
|
|
|
- <textarea class="form-textarea" v-model="planForm.description" placeholder="请输入方案描述" :disabled="!editable" />
|
|
|
- </view>
|
|
|
- <view class="form-item">
|
|
|
- <text class="form-label">状态</text>
|
|
|
- <text class="status-badge" :class="'status-' + planForm.status">{{ statusText(planForm.status) }}</text>
|
|
|
- </view>
|
|
|
- <view class="form-actions" v-if="editable && !isNew">
|
|
|
- <view class="btn-save" @click="savePlan">
|
|
|
- <text>保存</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 训练项目 -->
|
|
|
- <view class="section-card">
|
|
|
- <view class="section-header">
|
|
|
- <text class="section-title">训练项目</text>
|
|
|
- <view class="btn-add-item" v-if="editable" @click="showAddItem">
|
|
|
- <text>+ 添加项目</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="item-list" v-if="items.length > 0">
|
|
|
- <view class="plan-item" v-for="(item, idx) in items" :key="item.id">
|
|
|
- <view class="item-header">
|
|
|
- <view class="item-seq">{{ idx + 1 }}</view>
|
|
|
- <view class="item-dim-tag">{{ dimensionLabel(item.dimensionKey) }}</view>
|
|
|
- <view class="item-status-badge" :class="'item-' + item.status">{{ itemStatusText(item.status) }}</view>
|
|
|
- </view>
|
|
|
- <view class="item-body">
|
|
|
- <view class="item-field">
|
|
|
- <text class="item-field-label">任务标题</text>
|
|
|
- <input v-if="editable" class="item-input" v-model="item.taskTitle" @blur="updateItemField(item)" />
|
|
|
- <text v-else class="item-field-value">{{ item.taskTitle }}</text>
|
|
|
- </view>
|
|
|
- <view class="item-field">
|
|
|
- <text class="item-field-label">任务描述</text>
|
|
|
- <input v-if="editable" class="item-input" v-model="item.taskDesc" @blur="updateItemField(item)" />
|
|
|
- <text v-else class="item-field-value">{{ item.taskDesc }}</text>
|
|
|
- </view>
|
|
|
- <view class="item-field-row">
|
|
|
- <view class="item-field half">
|
|
|
- <text class="item-field-label">积分</text>
|
|
|
- <input v-if="editable" class="item-input" type="number" v-model="item.taskPoints" @blur="updateItemField(item)" />
|
|
|
- <text v-else class="item-field-value">{{ item.taskPoints || 2 }}</text>
|
|
|
- </view>
|
|
|
- <view class="item-field half">
|
|
|
- <text class="item-field-label">截止天数</text>
|
|
|
- <input v-if="editable" class="item-input" type="number" v-model="item.deadlineDays" @blur="updateItemField(item)" />
|
|
|
- <text v-else class="item-field-value">{{ item.deadlineDays || 7 }}天</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <view class="item-field" v-if="item.minigameCode">
|
|
|
- <text class="item-field-label">小游戏</text>
|
|
|
- <text class="item-field-value">{{ item.minigameCode }}</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <view class="item-actions" v-if="editable">
|
|
|
- <view class="item-action-btn delete" @click="deleteItem(item.id, idx)">
|
|
|
- <text>删除</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="empty-items" v-else>
|
|
|
- <text class="empty-items-text">暂无训练项目,点击上方"添加项目"开始配置</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 操作栏 -->
|
|
|
- <view class="action-bar" v-if="editable && !isNew">
|
|
|
- <view class="btn-deploy" @click="handleDeploy" v-if="planForm.status === 'draft'">
|
|
|
- <text>下发训练方案</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 添加项目弹窗 -->
|
|
|
- <view class="modal-mask" v-if="showItemModal" @click="showItemModal = false">
|
|
|
- <view class="modal-card" @click.stop>
|
|
|
- <text class="modal-title">添加训练项目</text>
|
|
|
- <view class="modal-form">
|
|
|
- <view class="form-item">
|
|
|
- <text class="form-label">认知维度</text>
|
|
|
- <picker class="dim-picker" :value="dimPickerIdx" :range="dimensionOptions" range-key="label" @change="onDimChange">
|
|
|
- <text class="picker-text">{{ newItem.dimensionKey ? dimensionLabel(newItem.dimensionKey) : '请选择维度' }}</text>
|
|
|
- </picker>
|
|
|
- </view>
|
|
|
- <view class="form-item">
|
|
|
- <text class="form-label">任务标题</text>
|
|
|
- <input class="form-input" v-model="newItem.taskTitle" placeholder="请输入任务标题" />
|
|
|
- </view>
|
|
|
- <view class="form-item">
|
|
|
- <text class="form-label">任务描述</text>
|
|
|
- <textarea class="form-textarea" v-model="newItem.taskDesc" placeholder="请输入任务描述" />
|
|
|
- </view>
|
|
|
- <view class="form-item">
|
|
|
- <text class="form-label">建议文案</text>
|
|
|
- <textarea class="form-textarea" v-model="newItem.suggestion" placeholder="给家长的建议" />
|
|
|
- </view>
|
|
|
- <view class="form-item-row">
|
|
|
- <view class="form-item half">
|
|
|
- <text class="form-label">积分</text>
|
|
|
- <input class="form-input" type="number" v-model="newItem.taskPoints" />
|
|
|
- </view>
|
|
|
- <view class="form-item half">
|
|
|
- <text class="form-label">截止天数</text>
|
|
|
- <input class="form-input" type="number" v-model="newItem.deadlineDays" />
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <view class="modal-actions">
|
|
|
- <view class="btn-cancel" @click="showItemModal = false"><text>取消</text></view>
|
|
|
- <view class="btn-confirm" @click="addItemConfirm"><text>确认添加</text></view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 新建模式: 选择孩子 -->
|
|
|
- <view class="modal-mask" v-if="showChildModal" @click="showChildModal = false">
|
|
|
- <view class="modal-card" @click.stop>
|
|
|
- <text class="modal-title">选择孩子</text>
|
|
|
- <view class="modal-child-list">
|
|
|
- <view class="modal-child-item" v-for="child in bindedChildren" :key="child.id"
|
|
|
- @click="selectCreateChild(child.id)">
|
|
|
- <text>{{ child.name || '孩子' + child.id }}</text>
|
|
|
- </view>
|
|
|
- <view class="empty-small-text" v-if="bindedChildren.length === 0">暂无可用孩子</view>
|
|
|
- </view>
|
|
|
- <view class="modal-footer">
|
|
|
- <view class="btn-cancel" @click="showChildModal = false"><text>取消</text></view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="loading" v-if="loading"><text class="loading-text">加载中...</text></view>
|
|
|
- </view>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script>
|
|
|
-import { getTrainingPlanDetail, createTrainingPlan, updateTrainingPlan, addTrainingPlanItem, updateTrainingPlanItem, deleteTrainingPlanItem, deployTrainingPlan, getExecutionBoard, getGuideBindedChildren } from '../../../utils/api.js'
|
|
|
-
|
|
|
-export default {
|
|
|
- data() {
|
|
|
- return {
|
|
|
- planId: null,
|
|
|
- isNew: false,
|
|
|
- mode: 'edit',
|
|
|
- planForm: { title: '', description: '', status: 'draft' },
|
|
|
- items: [],
|
|
|
- boardData: null,
|
|
|
- loading: false,
|
|
|
- editable: true,
|
|
|
- showItemModal: false,
|
|
|
- showChildModal: false,
|
|
|
- bindedChildren: [],
|
|
|
- newItem: { dimensionKey: '', taskTitle: '', taskDesc: '', suggestion: '', taskPoints: 2, deadlineDays: 7 },
|
|
|
- dimPickerIdx: 0,
|
|
|
- dimensionOptions: [
|
|
|
- { key: 'perception', label: '感知能力' },
|
|
|
- { key: 'focus', label: '专注力' },
|
|
|
- { key: 'memory', label: '记忆力' },
|
|
|
- { key: 'logic', label: '逻辑思维' },
|
|
|
- { key: 'spatial', label: '空间思维' },
|
|
|
- { key: 'processingSpeed', label: '加工速度' }
|
|
|
- ]
|
|
|
- }
|
|
|
- },
|
|
|
- onLoad(options) {
|
|
|
- this.planId = options.planId ? parseInt(options.planId) : null
|
|
|
- this.mode = options.mode || 'edit'
|
|
|
-
|
|
|
- if (!this.planId || this.planId === 0) {
|
|
|
- this.isNew = true
|
|
|
- this.planForm = { title: '认知训练方案', description: '', status: 'draft' }
|
|
|
- this.showChildModalForNew()
|
|
|
- } else {
|
|
|
- this.loadDetail()
|
|
|
- }
|
|
|
- },
|
|
|
- methods: {
|
|
|
- loadDetail() {
|
|
|
- var self = this
|
|
|
- self.loading = true
|
|
|
- if (self.mode === 'board') {
|
|
|
- getExecutionBoard(self.planId).then(function(res) {
|
|
|
- self.loading = false
|
|
|
- if (res.code === 200) {
|
|
|
- self.boardData = res.data
|
|
|
- }
|
|
|
- }).catch(function() { self.loading = false })
|
|
|
- } else {
|
|
|
- getTrainingPlanDetail(self.planId).then(function(res) {
|
|
|
- self.loading = false
|
|
|
- if (res.code === 200 && res.data) {
|
|
|
- self.planForm = res.data.plan || {}
|
|
|
- self.items = res.data.items || []
|
|
|
- self.editable = self.planForm.status === 'draft'
|
|
|
- }
|
|
|
- }).catch(function() { self.loading = false })
|
|
|
- }
|
|
|
- },
|
|
|
- showChildModalForNew() {
|
|
|
- getGuideBindedChildren().then(function(res) {
|
|
|
- if (res.code === 200 && res.data) {
|
|
|
- var children = []
|
|
|
- var data = res.data
|
|
|
- for (var i = 0; i < data.length; i++) {
|
|
|
- var family = data[i]
|
|
|
- if (family.children) {
|
|
|
- for (var j = 0; j < family.children.length; j++) {
|
|
|
- children.push(family.children[j])
|
|
|
- }
|
|
|
- }
|
|
|
- if (family.members) {
|
|
|
- for (var k = 0; k < family.members.length; k++) {
|
|
|
- var m = family.members[k]
|
|
|
- if (m.childId) children.push({ id: m.childId, name: m.name || m.childName })
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- self.bindedChildren = children
|
|
|
- }
|
|
|
- }).catch(function() {})
|
|
|
- this.showChildModal = true
|
|
|
- },
|
|
|
- selectCreateChild(childId) {
|
|
|
- var self = this
|
|
|
- self.showChildModal = false
|
|
|
- createTrainingPlan(childId, self.planForm.title, self.planForm.description).then(function(res) {
|
|
|
- if (res.code === 200 && res.data) {
|
|
|
- self.planId = res.data
|
|
|
- self.isNew = false
|
|
|
- uni.showToast({ title: '方案已创建', icon: 'success' })
|
|
|
- setTimeout(function() {
|
|
|
- uni.redirectTo({ url: '/pages/guide/training-plans/detail?planId=' + self.planId })
|
|
|
- }, 1000)
|
|
|
- } else {
|
|
|
- uni.showToast({ title: res.message || '创建失败', icon: 'none' })
|
|
|
- }
|
|
|
- }).catch(function() {
|
|
|
- uni.showToast({ title: '网络异常', icon: 'none' })
|
|
|
- })
|
|
|
- },
|
|
|
- savePlan() {
|
|
|
- var self = this
|
|
|
- updateTrainingPlan(self.planId, self.planForm.title, self.planForm.description).then(function(res) {
|
|
|
- if (res.code === 200) {
|
|
|
- uni.showToast({ title: '保存成功', icon: 'success' })
|
|
|
- } else {
|
|
|
- uni.showToast({ title: res.message || '保存失败', icon: 'none' })
|
|
|
- }
|
|
|
- }).catch(function() {
|
|
|
- uni.showToast({ title: '网络异常', icon: 'none' })
|
|
|
- })
|
|
|
- },
|
|
|
- showAddItem() {
|
|
|
- this.newItem = { dimensionKey: '', taskTitle: '', taskDesc: '', suggestion: '', taskPoints: 2, deadlineDays: 7 }
|
|
|
- this.dimPickerIdx = 0
|
|
|
- this.showItemModal = true
|
|
|
- },
|
|
|
- onDimChange(e) {
|
|
|
- this.dimPickerIdx = e.detail.value
|
|
|
- this.newItem.dimensionKey = this.dimensionOptions[this.dimPickerIdx].key
|
|
|
- },
|
|
|
- addItemConfirm() {
|
|
|
- var self = this
|
|
|
- if (!this.newItem.dimensionKey) {
|
|
|
- uni.showToast({ title: '请选择维度', icon: 'none' })
|
|
|
- return
|
|
|
- }
|
|
|
- if (!this.newItem.taskTitle) {
|
|
|
- uni.showToast({ title: '请输入任务标题', icon: 'none' })
|
|
|
- return
|
|
|
- }
|
|
|
- var payload = {
|
|
|
- planId: this.planId,
|
|
|
- dimensionKey: this.newItem.dimensionKey,
|
|
|
- taskTitle: this.newItem.taskTitle,
|
|
|
- taskDesc: this.newItem.taskDesc,
|
|
|
- suggestion: this.newItem.suggestion,
|
|
|
- taskPoints: parseInt(this.newItem.taskPoints) || 2,
|
|
|
- deadlineDays: parseInt(this.newItem.deadlineDays) || 7,
|
|
|
- sequence: this.items.length + 1
|
|
|
- }
|
|
|
- addTrainingPlanItem(payload).then(function(res) {
|
|
|
- if (res.code === 200) {
|
|
|
- self.showItemModal = false
|
|
|
- uni.showToast({ title: '已添加', icon: 'success' })
|
|
|
- self.loadDetail()
|
|
|
- } else {
|
|
|
- uni.showToast({ title: res.message || '添加失败', icon: 'none' })
|
|
|
- }
|
|
|
- }).catch(function() {
|
|
|
- uni.showToast({ title: '网络异常', icon: 'none' })
|
|
|
- })
|
|
|
- },
|
|
|
- updateItemField(item) {
|
|
|
- updateTrainingPlanItem(item).catch(function() {
|
|
|
- uni.showToast({ title: '保存失败', icon: 'none' })
|
|
|
- })
|
|
|
- },
|
|
|
- deleteItem(itemId, idx) {
|
|
|
- var self = this
|
|
|
- if (!itemId) {
|
|
|
- self.items.splice(idx, 1)
|
|
|
- return
|
|
|
- }
|
|
|
- uni.showModal({
|
|
|
- title: '确认删除',
|
|
|
- content: '确定删除此项目?',
|
|
|
- success: function(res) {
|
|
|
- if (res.confirm) {
|
|
|
- deleteTrainingPlanItem(itemId).then(function(r) {
|
|
|
- if (r.code === 200) {
|
|
|
- self.items.splice(idx, 1)
|
|
|
- uni.showToast({ title: '已删除', icon: 'success' })
|
|
|
- }
|
|
|
- }).catch(function() {})
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
- },
|
|
|
- handleDeploy() {
|
|
|
- var self = this
|
|
|
- uni.showModal({
|
|
|
- title: '确认下发',
|
|
|
- content: '将一次性创建所有训练任务,是否继续?',
|
|
|
- success: function(res) {
|
|
|
- if (res.confirm) {
|
|
|
- uni.showLoading({ title: '下发中...' })
|
|
|
- deployTrainingPlan(self.planId).then(function(r) {
|
|
|
- uni.hideLoading()
|
|
|
- if (r.code === 200) {
|
|
|
- uni.showToast({ title: '下发成功', icon: 'success' })
|
|
|
- self.loadDetail()
|
|
|
- } else {
|
|
|
- uni.showToast({ title: r.message || '下发失败', icon: 'none' })
|
|
|
- }
|
|
|
- }).catch(function() {
|
|
|
- uni.hideLoading()
|
|
|
- uni.showToast({ title: '网络异常', icon: 'none' })
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
- },
|
|
|
- dimensionLabel(key) {
|
|
|
- var map = { perception: '感知', focus: '专注', memory: '记忆', logic: '逻辑', spatial: '空间', processingSpeed: '加工速度' }
|
|
|
- return map[key] || key || ''
|
|
|
- },
|
|
|
- statusText(status) {
|
|
|
- var map = { draft: '草稿', published: '已下发', completed: '已完成' }
|
|
|
- return map[status] || status || '草稿'
|
|
|
- },
|
|
|
- itemStatusText(status) {
|
|
|
- var map = { pending: '待完成', completed: '已完成', skipped: '已跳过' }
|
|
|
- return map[status] || status || '待完成'
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-</script>
|
|
|
-
|
|
|
-<style scoped>
|
|
|
-.container {
|
|
|
- min-height: 100vh;
|
|
|
- background: #f5f7fa;
|
|
|
- padding-bottom: 60rpx;
|
|
|
-}
|
|
|
-
|
|
|
-/* ===== 看板 ===== */
|
|
|
-.board-section {
|
|
|
- padding: 30rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.board-header {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 16rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.board-title {
|
|
|
- font-size: 34rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #333;
|
|
|
-}
|
|
|
-
|
|
|
-.board-progress {
|
|
|
- font-size: 40rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #667eea;
|
|
|
-}
|
|
|
-
|
|
|
-.progress-bar-wrap {
|
|
|
- height: 12rpx;
|
|
|
- background: #e5e7eb;
|
|
|
- border-radius: 6rpx;
|
|
|
- overflow: hidden;
|
|
|
- margin-bottom: 24rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.progress-bar-fill {
|
|
|
- height: 100%;
|
|
|
- background: linear-gradient(90deg, #667eea, #764ba2);
|
|
|
- border-radius: 6rpx;
|
|
|
- transition: width 0.3s;
|
|
|
-}
|
|
|
-
|
|
|
-.board-summary {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- gap: 20rpx;
|
|
|
- margin-bottom: 24rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.summary-item {
|
|
|
- flex: 1;
|
|
|
- background: #fff;
|
|
|
- border-radius: 16rpx;
|
|
|
- padding: 24rpx;
|
|
|
- text-align: center;
|
|
|
- box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.05);
|
|
|
-}
|
|
|
-
|
|
|
-.summary-value {
|
|
|
- font-size: 40rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #667eea;
|
|
|
- display: block;
|
|
|
-}
|
|
|
-
|
|
|
-.summary-label {
|
|
|
- font-size: 24rpx;
|
|
|
- color: #888;
|
|
|
- display: block;
|
|
|
- margin-top: 4rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.board-items {
|
|
|
- background: #fff;
|
|
|
- border-radius: 20rpx;
|
|
|
- overflow: hidden;
|
|
|
-}
|
|
|
-
|
|
|
-.board-item {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- align-items: center;
|
|
|
- padding: 24rpx;
|
|
|
- border-bottom: 1rpx solid #f0f0f0;
|
|
|
-}
|
|
|
-
|
|
|
-.board-item:last-child { border-bottom: none; }
|
|
|
-
|
|
|
-.bi-left {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- align-items: center;
|
|
|
- flex: 1;
|
|
|
- gap: 16rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.bi-check {
|
|
|
- width: 44rpx;
|
|
|
- height: 44rpx;
|
|
|
- border-radius: 50%;
|
|
|
- border: 3rpx solid #e5e7eb;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- font-size: 22rpx;
|
|
|
- color: #d1d5db;
|
|
|
- flex-shrink: 0;
|
|
|
-}
|
|
|
-
|
|
|
-.bi-check.done {
|
|
|
- background: #22c55e;
|
|
|
- border-color: #22c55e;
|
|
|
- color: #fff;
|
|
|
-}
|
|
|
-
|
|
|
-.bi-info {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 4rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.bi-dim {
|
|
|
- font-size: 22rpx;
|
|
|
- color: #888;
|
|
|
-}
|
|
|
-
|
|
|
-.bi-title {
|
|
|
- font-size: 28rpx;
|
|
|
- color: #333;
|
|
|
- font-weight: 500;
|
|
|
-}
|
|
|
-
|
|
|
-.bi-right {}
|
|
|
-
|
|
|
-.bi-status {
|
|
|
- font-size: 22rpx;
|
|
|
- padding: 4rpx 16rpx;
|
|
|
- border-radius: 16rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.bi-pending { background: #f3f4f6; color: #666; }
|
|
|
-.bi-completed { background: #d1fae5; color: #059669; }
|
|
|
-.bi-skipped { background: #fee2e2; color: #dc2626; }
|
|
|
-
|
|
|
-/* ===== 编辑模式 ===== */
|
|
|
-.edit-section {
|
|
|
- padding: 20rpx 30rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.section-card {
|
|
|
- background: #fff;
|
|
|
- border-radius: 20rpx;
|
|
|
- padding: 28rpx;
|
|
|
- margin-bottom: 24rpx;
|
|
|
- box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04);
|
|
|
-}
|
|
|
-
|
|
|
-.section-header {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 16rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.section-title {
|
|
|
- font-size: 30rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #333;
|
|
|
- margin-bottom: 16rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.section-header .section-title {
|
|
|
- margin-bottom: 0;
|
|
|
-}
|
|
|
-
|
|
|
-.form-item {
|
|
|
- margin-bottom: 20rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.form-item:last-child { margin-bottom: 0; }
|
|
|
-
|
|
|
-.form-label {
|
|
|
- font-size: 26rpx;
|
|
|
- color: #888;
|
|
|
- display: block;
|
|
|
- margin-bottom: 8rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.form-input {
|
|
|
- width: 100%;
|
|
|
- height: 72rpx;
|
|
|
- background: #f9fafb;
|
|
|
- border-radius: 12rpx;
|
|
|
- padding: 0 20rpx;
|
|
|
- font-size: 28rpx;
|
|
|
- color: #333;
|
|
|
- box-sizing: border-box;
|
|
|
- border: 1rpx solid #e5e7eb;
|
|
|
-}
|
|
|
-
|
|
|
-.form-textarea {
|
|
|
- width: 100%;
|
|
|
- background: #f9fafb;
|
|
|
- border-radius: 12rpx;
|
|
|
- padding: 16rpx 20rpx;
|
|
|
- font-size: 28rpx;
|
|
|
- color: #333;
|
|
|
- box-sizing: border-box;
|
|
|
- border: 1rpx solid #e5e7eb;
|
|
|
- min-height: 120rpx;
|
|
|
- display: block;
|
|
|
-}
|
|
|
-
|
|
|
-.form-actions {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- justify-content: flex-end;
|
|
|
- margin-top: 20rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.btn-save {
|
|
|
- background: linear-gradient(135deg, #667eea, #764ba2);
|
|
|
- padding: 16rpx 48rpx;
|
|
|
- border-radius: 32rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.btn-save:active { opacity: 0.8; }
|
|
|
-
|
|
|
-.btn-save text {
|
|
|
- color: #fff;
|
|
|
- font-size: 28rpx;
|
|
|
- font-weight: 500;
|
|
|
-}
|
|
|
-
|
|
|
-.status-badge {
|
|
|
- display: inline-block;
|
|
|
- font-size: 24rpx;
|
|
|
- padding: 6rpx 20rpx;
|
|
|
- border-radius: 16rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.status-draft { background: #f3f4f6; color: #666; }
|
|
|
-.status-published { background: #dbeafe; color: #2563eb; }
|
|
|
-.status-completed { background: #d1fae5; color: #059669; }
|
|
|
-
|
|
|
-/* 添加项目按钮 */
|
|
|
-.btn-add-item {
|
|
|
- background: #eef2ff;
|
|
|
- padding: 8rpx 24rpx;
|
|
|
- border-radius: 20rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.btn-add-item:active { opacity: 0.7; }
|
|
|
-
|
|
|
-.btn-add-item text {
|
|
|
- font-size: 26rpx;
|
|
|
- color: #667eea;
|
|
|
- font-weight: 500;
|
|
|
-}
|
|
|
-
|
|
|
-/* 项目列表 */
|
|
|
-.item-list {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 16rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.plan-item {
|
|
|
- background: #f9fafb;
|
|
|
- border-radius: 16rpx;
|
|
|
- padding: 20rpx;
|
|
|
- border: 1rpx solid #e5e7eb;
|
|
|
-}
|
|
|
-
|
|
|
-.item-header {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- align-items: center;
|
|
|
- gap: 12rpx;
|
|
|
- margin-bottom: 12rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.item-seq {
|
|
|
- width: 36rpx;
|
|
|
- height: 36rpx;
|
|
|
- background: #667eea;
|
|
|
- border-radius: 50%;
|
|
|
- color: #fff;
|
|
|
- font-size: 22rpx;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- font-weight: bold;
|
|
|
-}
|
|
|
-
|
|
|
-.item-dim-tag {
|
|
|
- background: #eef2ff;
|
|
|
- color: #667eea;
|
|
|
- font-size: 22rpx;
|
|
|
- padding: 4rpx 14rpx;
|
|
|
- border-radius: 12rpx;
|
|
|
- font-weight: 500;
|
|
|
-}
|
|
|
-
|
|
|
-.item-status-badge {
|
|
|
- font-size: 22rpx;
|
|
|
- padding: 4rpx 14rpx;
|
|
|
- border-radius: 12rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.item-pending { background: #f3f4f6; color: #666; }
|
|
|
-.item-completed { background: #d1fae5; color: #059669; }
|
|
|
-
|
|
|
-.item-body {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 10rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.item-field {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 4rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.item-field-row {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- gap: 12rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.item-field.half {
|
|
|
- flex: 1;
|
|
|
-}
|
|
|
-
|
|
|
-.item-field-label {
|
|
|
- font-size: 22rpx;
|
|
|
- color: #999;
|
|
|
-}
|
|
|
-
|
|
|
-.item-field-value {
|
|
|
- font-size: 26rpx;
|
|
|
- color: #333;
|
|
|
-}
|
|
|
-
|
|
|
-.item-input {
|
|
|
- background: #fff;
|
|
|
- border: 1rpx solid #e5e7eb;
|
|
|
- border-radius: 8rpx;
|
|
|
- padding: 8rpx 12rpx;
|
|
|
- font-size: 26rpx;
|
|
|
- color: #333;
|
|
|
-}
|
|
|
-
|
|
|
-.item-actions {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- justify-content: flex-end;
|
|
|
- margin-top: 10rpx;
|
|
|
- border-top: 1rpx solid #e5e7eb;
|
|
|
- padding-top: 10rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.item-action-btn {
|
|
|
- padding: 8rpx 20rpx;
|
|
|
- border-radius: 16rpx;
|
|
|
- font-size: 24rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.item-action-btn.delete {
|
|
|
- background: #fee2e2;
|
|
|
- color: #dc2626;
|
|
|
-}
|
|
|
-
|
|
|
-.item-action-btn.delete:active { opacity: 0.7; }
|
|
|
-
|
|
|
-.empty-items {
|
|
|
- padding: 40rpx 0;
|
|
|
- text-align: center;
|
|
|
-}
|
|
|
-
|
|
|
-.empty-items-text {
|
|
|
- font-size: 26rpx;
|
|
|
- color: #999;
|
|
|
-}
|
|
|
-
|
|
|
-/* 底部操作栏 */
|
|
|
-.action-bar {
|
|
|
- position: fixed;
|
|
|
- bottom: 0;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- background: #fff;
|
|
|
- padding: 20rpx 30rpx;
|
|
|
- box-shadow: 0 -4rpx 16rpx rgba(0,0,0,0.06);
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- justify-content: center;
|
|
|
-}
|
|
|
-
|
|
|
-.btn-deploy {
|
|
|
- background: linear-gradient(135deg, #667eea, #764ba2);
|
|
|
- padding: 20rpx 80rpx;
|
|
|
- border-radius: 40rpx;
|
|
|
- box-shadow: 0 4rpx 16rpx rgba(102,126,234,0.3);
|
|
|
-}
|
|
|
-
|
|
|
-.btn-deploy:active { opacity: 0.85; }
|
|
|
-
|
|
|
-.btn-deploy text {
|
|
|
- color: #fff;
|
|
|
- font-size: 30rpx;
|
|
|
- font-weight: bold;
|
|
|
-}
|
|
|
-
|
|
|
-/* 弹窗 */
|
|
|
-.modal-mask {
|
|
|
- position: fixed;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- bottom: 0;
|
|
|
- background: rgba(0,0,0,0.5);
|
|
|
- z-index: 100;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
-}
|
|
|
-
|
|
|
-.modal-card {
|
|
|
- background: #fff;
|
|
|
- border-radius: 24rpx;
|
|
|
- padding: 40rpx;
|
|
|
- width: 640rpx;
|
|
|
- max-height: 75vh;
|
|
|
- overflow-y: auto;
|
|
|
-}
|
|
|
-
|
|
|
-.modal-title {
|
|
|
- font-size: 32rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #333;
|
|
|
- display: block;
|
|
|
- margin-bottom: 24rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.modal-form {
|
|
|
- margin-bottom: 24rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.form-item-row {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- gap: 16rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.form-item.half {
|
|
|
- flex: 1;
|
|
|
-}
|
|
|
-
|
|
|
-.dim-picker {
|
|
|
- background: #f9fafb;
|
|
|
- border: 1rpx solid #e5e7eb;
|
|
|
- border-radius: 12rpx;
|
|
|
- padding: 16rpx 20rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.picker-text {
|
|
|
- font-size: 28rpx;
|
|
|
- color: #333;
|
|
|
-}
|
|
|
-
|
|
|
-.modal-actions {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- gap: 16rpx;
|
|
|
- justify-content: center;
|
|
|
-}
|
|
|
-
|
|
|
-.btn-cancel, .btn-confirm {
|
|
|
- padding: 16rpx 48rpx;
|
|
|
- border-radius: 32rpx;
|
|
|
- font-size: 28rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.btn-cancel {
|
|
|
- background: #f3f4f6;
|
|
|
- color: #666;
|
|
|
-}
|
|
|
-
|
|
|
-.btn-confirm {
|
|
|
- background: linear-gradient(135deg, #667eea, #764ba2);
|
|
|
- color: #fff;
|
|
|
-}
|
|
|
-
|
|
|
-.btn-cancel:active, .btn-confirm:active { opacity: 0.7; }
|
|
|
-
|
|
|
-.modal-child-list {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 12rpx;
|
|
|
- margin-bottom: 20rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.modal-child-item {
|
|
|
- padding: 20rpx;
|
|
|
- background: #f9fafb;
|
|
|
- border-radius: 12rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.modal-child-item:active { background: #eef2ff; }
|
|
|
-
|
|
|
-.modal-child-item text {
|
|
|
- font-size: 28rpx;
|
|
|
- color: #333;
|
|
|
-}
|
|
|
-
|
|
|
-.modal-footer {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- justify-content: center;
|
|
|
-}
|
|
|
-
|
|
|
-.empty-small-text {
|
|
|
- font-size: 26rpx;
|
|
|
- color: #999;
|
|
|
- text-align: center;
|
|
|
- padding: 20rpx 0;
|
|
|
-}
|
|
|
-
|
|
|
-.loading {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- padding: 80rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.loading-text {
|
|
|
- font-size: 28rpx;
|
|
|
- color: #999;
|
|
|
-}
|
|
|
-</style>
|