| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968 |
- <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>
|