|
|
@@ -14,7 +14,7 @@
|
|
|
<el-option label="延年圣手" :value="4" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="功效类型">
|
|
|
+ <el-form-item label="作用类型">
|
|
|
<el-select v-model="queryForm.effectType" placeholder="全部" clearable style="width:110px">
|
|
|
<el-option v-for="e in effectOptions" :key="e" :label="e" :value="e" />
|
|
|
</el-select>
|
|
|
@@ -81,7 +81,7 @@
|
|
|
stripe
|
|
|
@selection-change="handleSelectionChange"
|
|
|
>
|
|
|
- <el-table-column type="selection" width="50" align="center" />
|
|
|
+ <el-table-column type="selection" width="50" align="center" :selectable="isRowSelectable" />
|
|
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
|
|
<el-table-column prop="planCode" label="方案ID" width="100" />
|
|
|
<el-table-column prop="name" label="方案名称" min-width="140" show-overflow-tooltip />
|
|
|
@@ -90,7 +90,7 @@
|
|
|
<el-tag :type="modeTypeTag(row.modeType)" size="small">{{ modeTypeLabel(row.modeType) }}</el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="effectType" label="功效类型" width="90" align="center">
|
|
|
+ <el-table-column prop="effectType" label="作用类型" width="90" align="center">
|
|
|
<template #default="{ row }">{{ row.effectType || '-' }}</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column prop="authorName" label="创作人" width="100">
|
|
|
@@ -108,9 +108,11 @@
|
|
|
<template #default="{ row }">
|
|
|
<el-button size="small" type="primary" link @click="handleView(row)">查看</el-button>
|
|
|
<el-button size="small" type="warning" link @click="handleEdit(row)">编辑</el-button>
|
|
|
- <el-button v-if="row.status === 0 || row.status === 2" size="small" type="success" link @click="handlePublish(row)">发布</el-button>
|
|
|
- <el-button v-if="row.status === 1" size="small" type="info" link @click="handleOffshelf(row)">下架</el-button>
|
|
|
- <el-button v-if="row.status === 0" size="small" type="danger" link @click="handleDelete(row)">删除</el-button>
|
|
|
+ <template v-if="!isSystemPresetPlan(row)">
|
|
|
+ <el-button v-if="row.status === 0 || row.status === 2" size="small" type="success" link @click="handlePublish(row)">发布</el-button>
|
|
|
+ <el-button v-if="row.status === 1" size="small" type="info" link @click="handleOffshelf(row)">下架</el-button>
|
|
|
+ <el-button v-if="row.status === 0" size="small" type="danger" link @click="handleDelete(row)">删除</el-button>
|
|
|
+ </template>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
@@ -146,24 +148,43 @@
|
|
|
<el-row :gutter="20">
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="方案名称" prop="name">
|
|
|
- <el-input v-model="formData.name" placeholder="请输入方案名称" maxlength="100" />
|
|
|
+ <el-input
|
|
|
+ v-model="formData.name"
|
|
|
+ placeholder="请输入方案名称"
|
|
|
+ maxlength="100"
|
|
|
+ :disabled="isPlanNameLocked"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="模式类型" prop="modeType">
|
|
|
- <el-select v-model="formData.modeType" placeholder="请选择模式类型" style="width:100%">
|
|
|
- <el-option label="一键艾灸" :value="1" />
|
|
|
- <el-option label="专业模式" :value="2" />
|
|
|
- <el-option label="自定义模式" :value="3" />
|
|
|
- <el-option label="延年圣手" :value="4" />
|
|
|
+ <el-select
|
|
|
+ v-model="formData.modeType"
|
|
|
+ placeholder="请选择模式类型"
|
|
|
+ style="width:100%"
|
|
|
+ :disabled="isModeTypeLocked"
|
|
|
+ @change="handleModeTypeChange"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in editableModeTypeOptions"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
<el-row :gutter="20">
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="功效类型" prop="effectType">
|
|
|
- <el-select v-model="formData.effectType" placeholder="请选择功效" clearable style="width:100%">
|
|
|
+ <el-col v-if="showEffectTypeField" :span="12">
|
|
|
+ <el-form-item label="作用类型" prop="effectType">
|
|
|
+ <el-select
|
|
|
+ v-model="formData.effectType"
|
|
|
+ placeholder="请选择作用"
|
|
|
+ :clearable="!isEffectTypeLocked"
|
|
|
+ :disabled="isEffectTypeLocked"
|
|
|
+ style="width:100%"
|
|
|
+ >
|
|
|
<el-option v-for="e in effectOptions" :key="e" :label="e" :value="e" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
@@ -212,19 +233,40 @@
|
|
|
<el-input v-model="formData.description" type="textarea" :rows="2" maxlength="200" show-word-limit />
|
|
|
</el-form-item>
|
|
|
|
|
|
- <!-- 治疗步骤 -->
|
|
|
- <el-divider>治疗步骤</el-divider>
|
|
|
+ <!-- 灸方参数 -->
|
|
|
+ <el-divider>灸方参数</el-divider>
|
|
|
<div v-for="(step, index) in formData.steps" :key="index" class="step-row">
|
|
|
<div class="step-header">
|
|
|
- <span class="step-num">步骤 {{ index + 1 }}</span>
|
|
|
- <el-button
|
|
|
- v-if="dialogMode !== 'view'"
|
|
|
- size="small"
|
|
|
- type="danger"
|
|
|
- link
|
|
|
- :disabled="formData.steps.length <= 1"
|
|
|
- @click="removeStep(index)"
|
|
|
- >删除</el-button>
|
|
|
+ <span class="step-num">参数 {{ index + 1 }}</span>
|
|
|
+ <div v-if="dialogMode !== 'view'" class="step-actions">
|
|
|
+ <el-tooltip content="上移参数" placement="top">
|
|
|
+ <el-button
|
|
|
+ size="small"
|
|
|
+ :icon="ArrowUp"
|
|
|
+ link
|
|
|
+ :disabled="index === 0"
|
|
|
+ aria-label="上移参数"
|
|
|
+ @click="moveStep(index, -1)"
|
|
|
+ />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip content="下移参数" placement="top">
|
|
|
+ <el-button
|
|
|
+ size="small"
|
|
|
+ :icon="ArrowDown"
|
|
|
+ link
|
|
|
+ :disabled="index === formData.steps.length - 1"
|
|
|
+ aria-label="下移参数"
|
|
|
+ @click="moveStep(index, 1)"
|
|
|
+ />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-button
|
|
|
+ size="small"
|
|
|
+ type="danger"
|
|
|
+ link
|
|
|
+ :disabled="formData.steps.length <= 1"
|
|
|
+ @click="removeStep(index)"
|
|
|
+ >删除</el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<el-row :gutter="12">
|
|
|
<el-col :span="8">
|
|
|
@@ -299,14 +341,16 @@
|
|
|
size="small"
|
|
|
@click="addStep"
|
|
|
style="margin-top:4px"
|
|
|
- >+ 添加步骤</el-button>
|
|
|
+ >+ 添加参数</el-button>
|
|
|
</el-form>
|
|
|
|
|
|
<template #footer>
|
|
|
<el-button @click="dialogVisible = false">{{ dialogMode === 'view' ? '关闭' : '取消' }}</el-button>
|
|
|
<template v-if="dialogMode !== 'view'">
|
|
|
- <el-button :loading="submitLoading" @click="handleSubmit(0)">保存为草稿</el-button>
|
|
|
- <el-button type="primary" :loading="submitLoading" @click="handleSubmit(1)">发布方案</el-button>
|
|
|
+ <el-button v-if="!isSystemPresetPlan(formData)" :loading="submitLoading" @click="handleSubmit(0)">保存为草稿</el-button>
|
|
|
+ <el-button type="primary" :loading="submitLoading" @click="handleSubmit(isSystemPresetPlan(formData) ? formData.status : 1)">
|
|
|
+ {{ isSystemPresetPlan(formData) ? '保存修改' : '发布方案' }}
|
|
|
+ </el-button>
|
|
|
</template>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
@@ -318,7 +362,7 @@
|
|
|
<el-descriptions-item label="方案编码">{{ detailData.planCode }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="方案名称">{{ detailData.name }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="模式类型">{{ modeTypeLabel(detailData.modeType) }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="功效类型">{{ detailData.effectType || '-' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="作用类型">{{ detailData.effectType || '-' }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="创作人">{{ detailData.authorName || 'ADMIN' }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="状态">{{ statusLabel(detailData.status) }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="适用人群">{{ ['不限', '仅男', '仅女'][detailData.applicableGender] || '-' }}</el-descriptions-item>
|
|
|
@@ -331,9 +375,9 @@
|
|
|
<el-descriptions-item label="更新时间">{{ formatDateTime(detailData.updateTime) }}</el-descriptions-item>
|
|
|
</el-descriptions>
|
|
|
|
|
|
- <el-divider>治疗步骤</el-divider>
|
|
|
+ <el-divider>灸方参数</el-divider>
|
|
|
<el-table :data="detailData.steps || []" border size="small">
|
|
|
- <el-table-column prop="stepOrder" label="步骤" width="60" align="center" />
|
|
|
+ <el-table-column prop="stepOrder" label="参数" width="60" align="center" />
|
|
|
<el-table-column prop="acupointName" label="穴位" width="100" />
|
|
|
<el-table-column label="侧别" width="80" align="center">
|
|
|
<template #default="{ row }">{{ ['', '中心', '左侧', '右侧', '双侧'][row.side] || '-' }}</template>
|
|
|
@@ -354,9 +398,9 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, reactive, onMounted } from 'vue'
|
|
|
+import { ref, reactive, computed, onMounted } from 'vue'
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
-import { Search, Refresh, Plus, Delete } from '@element-plus/icons-vue'
|
|
|
+import { Search, Refresh, Plus, Delete, ArrowUp, ArrowDown } from '@element-plus/icons-vue'
|
|
|
import {
|
|
|
getPlanPage,
|
|
|
getPlanDetail,
|
|
|
@@ -375,8 +419,26 @@ import { useUserStore } from '@/store/user'
|
|
|
import { formatDateTime } from '@/utils/date'
|
|
|
// ============ 枚举常量 ============
|
|
|
const effectOptions = ['驱寒', '祛湿', '祛风', '化瘀', '活血', '化痰', '养颜', '扶阳']
|
|
|
+const modeTypeOptions = [
|
|
|
+ { label: '一键艾灸', value: 1 },
|
|
|
+ { label: '专业模式', value: 2 },
|
|
|
+ { label: '自定义模式', value: 3 },
|
|
|
+ { label: '延年圣手', value: 4 }
|
|
|
+]
|
|
|
+const addModeTypeOptions = modeTypeOptions.filter(item => [3, 4].includes(item.value))
|
|
|
const defaultSymptomsOptions = ['怕冷', '感冒', '咳嗽', '痰多', '疼痛', '腰酸', '失眠', '疲劳', '皮肤暗沉', '手脚冰凉']
|
|
|
const symptomsOptions = ref([...defaultSymptomsOptions])
|
|
|
+const systemPresetPlanCodes = Array.from({ length: 9 }, (_, index) => `S00${index + 1}`)
|
|
|
+const professionalPresetEffectMap = {
|
|
|
+ S002: '驱寒',
|
|
|
+ S003: '祛湿',
|
|
|
+ S004: '祛风',
|
|
|
+ S005: '化瘀',
|
|
|
+ S006: '活血',
|
|
|
+ S007: '化痰',
|
|
|
+ S008: '养颜',
|
|
|
+ S009: '扶阳'
|
|
|
+}
|
|
|
const userStore = useUserStore()
|
|
|
|
|
|
// ============ 选项列表(用于选择框) ============
|
|
|
@@ -427,6 +489,26 @@ function statusTag(val) {
|
|
|
return { 0: 'info', 1: 'success', 2: 'warning' }[val] ?? ''
|
|
|
}
|
|
|
|
|
|
+function isSystemPresetPlan(plan) {
|
|
|
+ return systemPresetPlanCodes.includes(plan?.planCode)
|
|
|
+}
|
|
|
+
|
|
|
+function isOneClickPresetPlan(plan) {
|
|
|
+ return plan?.planCode === 'S001'
|
|
|
+}
|
|
|
+
|
|
|
+function isProfessionalPresetPlan(plan) {
|
|
|
+ return Boolean(professionalPresetEffectMap[plan?.planCode])
|
|
|
+}
|
|
|
+
|
|
|
+function getProfessionalPresetEffect(plan) {
|
|
|
+ return professionalPresetEffectMap[plan?.planCode] || ''
|
|
|
+}
|
|
|
+
|
|
|
+function isRowSelectable(row) {
|
|
|
+ return !isSystemPresetPlan(row)
|
|
|
+}
|
|
|
+
|
|
|
// ============ 查询 ============
|
|
|
const queryForm = reactive({
|
|
|
name: '',
|
|
|
@@ -514,8 +596,9 @@ const defaultStep = () => ({
|
|
|
|
|
|
const defaultForm = () => ({
|
|
|
id: null,
|
|
|
+ planCode: '',
|
|
|
name: '',
|
|
|
- modeType: 1,
|
|
|
+ modeType: 3,
|
|
|
effectType: '',
|
|
|
symptomsList: [],
|
|
|
symptoms: '',
|
|
|
@@ -525,6 +608,7 @@ const defaultForm = () => ({
|
|
|
authorId: getCurrentAuthorId(),
|
|
|
authorName: getCurrentAuthorName(),
|
|
|
authorType: getCurrentAuthorType(),
|
|
|
+ status: 0,
|
|
|
description: '',
|
|
|
steps: [defaultStep()]
|
|
|
})
|
|
|
@@ -533,7 +617,19 @@ const formData = reactive(defaultForm())
|
|
|
|
|
|
const formRules = {
|
|
|
name: [{ required: true, message: '请输入方案名称', trigger: 'blur' }],
|
|
|
- modeType: [{ required: true, message: '请选择模式类型', trigger: 'change' }]
|
|
|
+ modeType: [{ required: true, message: '请选择模式类型', trigger: 'change' }],
|
|
|
+ effectType: [
|
|
|
+ {
|
|
|
+ validator: (_rule, value, callback) => {
|
|
|
+ if (formData.modeType === 2 && !value) {
|
|
|
+ callback(new Error('请选择作用类型'))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ callback()
|
|
|
+ },
|
|
|
+ trigger: 'change'
|
|
|
+ }
|
|
|
+ ]
|
|
|
}
|
|
|
|
|
|
const stepRules = {
|
|
|
@@ -612,19 +708,77 @@ function applyCurrentAuthor() {
|
|
|
formData.authorType = getCurrentAuthorType()
|
|
|
}
|
|
|
|
|
|
+const editableModeTypeOptions = computed(() => {
|
|
|
+ if (dialogMode.value === 'add') {
|
|
|
+ return addModeTypeOptions
|
|
|
+ }
|
|
|
+ if (isOneClickPresetPlan(formData)) {
|
|
|
+ return modeTypeOptions.filter(item => item.value === 1)
|
|
|
+ }
|
|
|
+ if (isProfessionalPresetPlan(formData)) {
|
|
|
+ return modeTypeOptions.filter(item => item.value === 2)
|
|
|
+ }
|
|
|
+ return modeTypeOptions
|
|
|
+})
|
|
|
+
|
|
|
+const isModeTypeLocked = computed(() => (
|
|
|
+ dialogMode.value === 'edit' && (isOneClickPresetPlan(formData) || isProfessionalPresetPlan(formData))
|
|
|
+))
|
|
|
+
|
|
|
+const isPlanNameLocked = computed(() => dialogMode.value === 'edit' && isSystemPresetPlan(formData))
|
|
|
+
|
|
|
+const isEffectTypeLocked = computed(() => dialogMode.value === 'edit' && isProfessionalPresetPlan(formData))
|
|
|
+
|
|
|
+const showEffectTypeField = computed(() => formData.modeType === 2 && !isOneClickPresetPlan(formData))
|
|
|
+
|
|
|
+function applyPlanModeRules() {
|
|
|
+ if (dialogMode.value === 'add' && ![3, 4].includes(formData.modeType)) {
|
|
|
+ formData.modeType = 3
|
|
|
+ }
|
|
|
+ if (isOneClickPresetPlan(formData)) {
|
|
|
+ formData.modeType = 1
|
|
|
+ formData.effectType = ''
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (isProfessionalPresetPlan(formData)) {
|
|
|
+ formData.modeType = 2
|
|
|
+ formData.effectType = getProfessionalPresetEffect(formData)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (formData.modeType !== 2) {
|
|
|
+ formData.effectType = ''
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function handleModeTypeChange() {
|
|
|
+ applyPlanModeRules()
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
- * 添加一个治疗步骤
|
|
|
+ * 添加一个灸方参数
|
|
|
*/
|
|
|
function addStep() {
|
|
|
formData.steps.push(defaultStep())
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 删除某个治疗步骤
|
|
|
+ * 调整灸方参数顺序
|
|
|
+ */
|
|
|
+function moveStep(index, direction) {
|
|
|
+ const targetIndex = index + direction
|
|
|
+ if (targetIndex < 0 || targetIndex >= formData.steps.length) return
|
|
|
+ const [step] = formData.steps.splice(index, 1)
|
|
|
+ formData.steps.splice(targetIndex, 0, step)
|
|
|
+ formRef.value?.clearValidate?.()
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 删除某个灸方参数
|
|
|
*/
|
|
|
function removeStep(index) {
|
|
|
if (formData.steps.length <= 1) return
|
|
|
formData.steps.splice(index, 1)
|
|
|
+ formRef.value?.clearValidate?.()
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -637,6 +791,7 @@ function handleAdd() {
|
|
|
applyCurrentAuthor()
|
|
|
dialogTitle.value = '新增方案'
|
|
|
dialogMode.value = 'add'
|
|
|
+ applyPlanModeRules()
|
|
|
dialogVisible.value = true
|
|
|
}
|
|
|
|
|
|
@@ -657,6 +812,7 @@ async function handleEdit(row) {
|
|
|
applyCurrentAuthor()
|
|
|
dialogTitle.value = '编辑方案'
|
|
|
dialogMode.value = 'edit'
|
|
|
+ applyPlanModeRules()
|
|
|
dialogVisible.value = true
|
|
|
}
|
|
|
|
|
|
@@ -664,6 +820,7 @@ async function handleEdit(row) {
|
|
|
* 提交保存(0=草稿,1=发布)
|
|
|
*/
|
|
|
async function handleSubmit(publishFlag) {
|
|
|
+ applyPlanModeRules()
|
|
|
await formRef.value.validate()
|
|
|
applyCurrentAuthor()
|
|
|
submitLoading.value = true
|
|
|
@@ -840,6 +997,16 @@ onMounted(() => {
|
|
|
color: #409eff;
|
|
|
font-size: 13px;
|
|
|
}
|
|
|
+
|
|
|
+ .step-actions {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+
|
|
|
+ :deep(.el-button + .el-button) {
|
|
|
+ margin-left: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
</style>
|