|
|
@@ -0,0 +1,264 @@
|
|
|
+<template>
|
|
|
+ <div class="guide-config admin-page">
|
|
|
+ <div class="header admin-page-header">
|
|
|
+ <h2 class="admin-page-title">引导配置</h2>
|
|
|
+ <div class="admin-page-desc">配置微信小程序引导遮罩的启用状态、展示位置与步骤内容</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="config-body" v-loading="loading">
|
|
|
+ <!-- 基本配置 -->
|
|
|
+ <el-card shadow="never" class="config-block">
|
|
|
+ <div slot="header" class="block-header">
|
|
|
+ <span class="block-title">基本配置</span>
|
|
|
+ </div>
|
|
|
+ <el-form :model="form" label-width="110px">
|
|
|
+ <el-form-item label="启用引导">
|
|
|
+ <el-switch v-model="form.enabled" active-text="启用" inactive-text="停用" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="版本号">
|
|
|
+ <el-input v-model="form.version" placeholder="如: 1.0" class="version-input" />
|
|
|
+ <div class="form-tip">升级版本号后,小程序用户会重新看到引导遮罩</div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="展示位置">
|
|
|
+ <el-checkbox-group v-model="form.positions">
|
|
|
+ <el-checkbox label="home">首页</el-checkbox>
|
|
|
+ <el-checkbox label="child">孩子端首页</el-checkbox>
|
|
|
+ </el-checkbox-group>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 步骤配置 -->
|
|
|
+ <el-card shadow="never" class="config-block">
|
|
|
+ <div slot="header" class="block-header">
|
|
|
+ <span class="block-title">步骤配置</span>
|
|
|
+ </div>
|
|
|
+ <el-tabs v-model="activeTab">
|
|
|
+ <el-tab-pane label="家长端" name="parent">
|
|
|
+ <div v-for="(step, index) in stepsFor('parent')" :key="index">
|
|
|
+ <el-card shadow="never" class="step-card">
|
|
|
+ <div class="step-card-head">
|
|
|
+ <span class="step-title">步骤 {{ index + 1 }}</span>
|
|
|
+ <div class="step-actions">
|
|
|
+ <el-button size="mini" type="danger" @click="removeStep('parent', index)">删除</el-button>
|
|
|
+ <el-button size="mini" :disabled="index === 0" @click="moveUp('parent', index)">↑ 上移</el-button>
|
|
|
+ <el-button size="mini" :disabled="index === stepsFor('parent').length - 1" @click="moveDown('parent', index)">↓ 下移</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="field-label">图标</div>
|
|
|
+ <el-input v-model="step.icon" placeholder="如: el-icon-star" />
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="field-label">标题</div>
|
|
|
+ <el-input v-model="step.title" placeholder="步骤标题" />
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="field-label">开场页</div>
|
|
|
+ <el-switch v-model="step.isIntro" active-text="是" inactive-text="否" />
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <div class="field-label desc-label">描述</div>
|
|
|
+ <el-input v-model="step.desc" type="textarea" :rows="2" placeholder="步骤描述" />
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ <el-button size="small" icon="el-icon-plus" @click="addStep('parent')">新增步骤</el-button>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <el-tab-pane label="孩子端" name="child">
|
|
|
+ <div v-for="(step, index) in stepsFor('child')" :key="index">
|
|
|
+ <el-card shadow="never" class="step-card">
|
|
|
+ <div class="step-card-head">
|
|
|
+ <span class="step-title">步骤 {{ index + 1 }}</span>
|
|
|
+ <div class="step-actions">
|
|
|
+ <el-button size="mini" type="danger" @click="removeStep('child', index)">删除</el-button>
|
|
|
+ <el-button size="mini" :disabled="index === 0" @click="moveUp('child', index)">↑ 上移</el-button>
|
|
|
+ <el-button size="mini" :disabled="index === stepsFor('child').length - 1" @click="moveDown('child', index)">↓ 下移</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="field-label">图标</div>
|
|
|
+ <el-input v-model="step.icon" placeholder="如: el-icon-star" />
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="field-label">标题</div>
|
|
|
+ <el-input v-model="step.title" placeholder="步骤标题" />
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="field-label">开场页</div>
|
|
|
+ <el-switch v-model="step.isIntro" active-text="是" inactive-text="否" />
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <div class="field-label desc-label">描述</div>
|
|
|
+ <el-input v-model="step.desc" type="textarea" :rows="2" placeholder="步骤描述" />
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ <el-button size="small" icon="el-icon-plus" @click="addStep('child')">新增步骤</el-button>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 保存 -->
|
|
|
+ <div class="save-wrap">
|
|
|
+ <el-button type="primary" size="medium" :loading="submitting" @click="handleSave">保存</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { getGuideConfig, saveGuideConfig } from '@/api/admin'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'GuideConfig',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ loading: false,
|
|
|
+ submitting: false,
|
|
|
+ activeTab: 'parent',
|
|
|
+ form: {
|
|
|
+ enabled: true,
|
|
|
+ version: '',
|
|
|
+ positions: [],
|
|
|
+ parent: [],
|
|
|
+ child: []
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.loadConfig()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ stepsFor(key) {
|
|
|
+ return key === 'child' ? this.form.child : this.form.parent
|
|
|
+ },
|
|
|
+ async loadConfig() {
|
|
|
+ this.loading = true
|
|
|
+ try {
|
|
|
+ const res = await getGuideConfig()
|
|
|
+ if (res.data) {
|
|
|
+ this.form.enabled = typeof res.data.enabled === 'boolean' ? res.data.enabled : true
|
|
|
+ this.form.version = res.data.version || ''
|
|
|
+ this.form.positions = res.data.positions && res.data.positions.length ? res.data.positions : []
|
|
|
+ this.form.parent = res.data.parent && res.data.parent.length ? res.data.parent : []
|
|
|
+ this.form.child = res.data.child && res.data.child.length ? res.data.child : []
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ this.$message.error('加载失败')
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ addStep(key) {
|
|
|
+ const list = this.stepsFor(key)
|
|
|
+ const isFirst = !list || list.length === 0
|
|
|
+ list.push({ icon: '', title: '', desc: '', isIntro: isFirst })
|
|
|
+ },
|
|
|
+ removeStep(key, index) {
|
|
|
+ this.stepsFor(key).splice(index, 1)
|
|
|
+ },
|
|
|
+ moveUp(key, index) {
|
|
|
+ const list = this.stepsFor(key)
|
|
|
+ if (index <= 0) return
|
|
|
+ const item = list[index]
|
|
|
+ list.splice(index, 1)
|
|
|
+ list.splice(index - 1, 0, item)
|
|
|
+ },
|
|
|
+ moveDown(key, index) {
|
|
|
+ const list = this.stepsFor(key)
|
|
|
+ if (index >= list.length - 1) return
|
|
|
+ const item = list[index]
|
|
|
+ list.splice(index, 1)
|
|
|
+ list.splice(index + 1, 0, item)
|
|
|
+ },
|
|
|
+ async handleSave() {
|
|
|
+ const version = (this.form.version || '').trim()
|
|
|
+ if (!version) {
|
|
|
+ this.$message.warning('请填写版本号')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const totalSteps = (this.form.parent ? this.form.parent.length : 0) + (this.form.child ? this.form.child.length : 0)
|
|
|
+ if (totalSteps === 0) {
|
|
|
+ this.$message.warning('请至少添加一个引导步骤')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const payload = {
|
|
|
+ enabled: this.form.enabled,
|
|
|
+ version: version,
|
|
|
+ positions: this.form.positions,
|
|
|
+ parent: this.form.parent,
|
|
|
+ child: this.form.child
|
|
|
+ }
|
|
|
+ this.submitting = true
|
|
|
+ try {
|
|
|
+ await saveGuideConfig(JSON.stringify(payload))
|
|
|
+ this.$message.success('保存成功')
|
|
|
+ } catch (e) {
|
|
|
+ this.$message.error('保存失败')
|
|
|
+ } finally {
|
|
|
+ this.submitting = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.guide-config {
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+.admin-page-desc {
|
|
|
+ margin-top: 4px;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #909399;
|
|
|
+}
|
|
|
+.config-body {
|
|
|
+ max-width: 1200px;
|
|
|
+ margin: 0 auto;
|
|
|
+}
|
|
|
+.config-block {
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+.block-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+.block-title {
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+.version-input {
|
|
|
+ max-width: 320px;
|
|
|
+}
|
|
|
+.form-tip {
|
|
|
+ margin-top: 6px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+ line-height: 1.5;
|
|
|
+}
|
|
|
+.step-card {
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+.step-card-head {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+.step-title {
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+.field-label {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+ margin-bottom: 6px;
|
|
|
+}
|
|
|
+.desc-label {
|
|
|
+ margin-top: 12px;
|
|
|
+}
|
|
|
+.save-wrap {
|
|
|
+ margin-top: 24px;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+</style>
|