修复Vue Admin布局中RouterView未绑定唯一key的问题,解决RBAC系统资源页面切换后内容不刷新的ISSUE-001故障,新增routeViewKey工具函数区分路由实例 完善路由配置,新增admin空路径路由,修复路由命名空路径告警 新增媒体管理、运维发布/重置页面,补全全站路由注册与动态路由过滤逻辑 新增玩法、线索、管家、系统资源管理的全套工具函数、业务组件及单元测试用例 修复后端app/seed.py中媒体资源重复检测未考虑会话内pending资源的bug,新增对应测试验证修复逻辑 修正alembic迁移版本0015的依赖关系,删除废弃的旧迁移文件 优化后端系统接口的字典序列化逻辑,简化重复代码 添加QA验收报告与相关测试截图,完善项目测试覆盖
93 lines
4.2 KiB
Vue
93 lines
4.2 KiB
Vue
<script setup lang="ts">
|
|
import { ref, watch } from "vue";
|
|
import { compactDetail, compactWanfaRoute, createEmptyDetail, createEmptyWanfaRoute, validateWanfaWrite } from "@/lib/wanfa";
|
|
import type { ConciergeAdvisor, DetailCreate, DetailRecord, WanfaRoute, WanfaRouteCreate } from "@/types";
|
|
import DetailFields from "@/components/wanfa/DetailFields.vue";
|
|
|
|
const props = defineProps<{
|
|
categoryLabel: string;
|
|
route?: WanfaRoute;
|
|
detail?: DetailRecord;
|
|
advisors: ConciergeAdvisor[];
|
|
saving: boolean;
|
|
}>();
|
|
const emit = defineEmits<{
|
|
(event: "save", value: { route: WanfaRouteCreate; detail: DetailCreate }): void;
|
|
(event: "cancel"): void;
|
|
}>();
|
|
|
|
const routeDraft = ref<WanfaRouteCreate>(createEmptyWanfaRoute());
|
|
const detailDraft = ref<DetailCreate>(createEmptyDetail());
|
|
const error = ref("");
|
|
const editing = ref(Boolean(props.route));
|
|
|
|
watch(() => [props.route?.id, props.detail?.id], () => {
|
|
editing.value = Boolean(props.route);
|
|
routeDraft.value = props.route
|
|
? { title: props.route.title, subtitle: props.route.subtitle, image: props.route.image, routeCount: props.route.routeCount, demandKeyword: props.route.demandKeyword }
|
|
: createEmptyWanfaRoute();
|
|
detailDraft.value = props.detail
|
|
? {
|
|
key: props.detail.key,
|
|
eyebrow: props.detail.eyebrow,
|
|
duration: props.detail.duration,
|
|
title: props.detail.title,
|
|
subtitle: props.detail.subtitle,
|
|
intro: props.detail.intro,
|
|
highlights: [...props.detail.highlights],
|
|
included: [...props.detail.included],
|
|
excluded: [...props.detail.excluded],
|
|
notes: [...props.detail.notes],
|
|
gallery: [...props.detail.gallery],
|
|
conciergeAdvisorId: props.detail.conciergeAdvisorId,
|
|
isActive: props.detail.isActive,
|
|
sortOrder: props.detail.sortOrder,
|
|
}
|
|
: createEmptyDetail();
|
|
error.value = "";
|
|
}, { immediate: true });
|
|
|
|
function submit() {
|
|
const route = compactWanfaRoute(routeDraft.value);
|
|
const detail = compactDetail(detailDraft.value);
|
|
const validationError = validateWanfaWrite(route, detail);
|
|
if (validationError) {
|
|
error.value = validationError;
|
|
return;
|
|
}
|
|
error.value = "";
|
|
emit("save", { route, detail });
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<el-form class="wanfa-editor route-editor" label-position="top" @submit.prevent="submit">
|
|
<div class="editor-heading">
|
|
<div>
|
|
<p class="eyebrow">{{ editing ? "EDIT" : "CREATE" }} / PLAY ROUTE</p>
|
|
<h3>{{ editing ? "编辑分类路线" : "新增分类路线" }}</h3>
|
|
<p>{{ editing ? `修改「${categoryLabel}」下的路线卡片和详情。` : `添加到「${categoryLabel}」,保存后会排在当前路线末尾。` }}</p>
|
|
</div>
|
|
<el-button text :disabled="saving" @click="emit('cancel')">取消</el-button>
|
|
</div>
|
|
|
|
<el-form-item label="路线标题" required><el-input v-model="routeDraft.title" maxlength="80" :disabled="saving" placeholder="例如:亲子玩水" autofocus /></el-form-item>
|
|
<el-form-item label="副标题/目的地说明" required><el-input v-model="routeDraft.subtitle" type="textarea" :rows="3" maxlength="160" :disabled="saving" /></el-form-item>
|
|
<div class="editor-options">
|
|
<el-form-item label="路线数量" required><el-input-number v-model="routeDraft.routeCount" :disabled="saving" :min="0" :step="1" controls-position="right" /></el-form-item>
|
|
<el-form-item label="需求关键词" required><el-input v-model="routeDraft.demandKeyword" maxlength="60" :disabled="saving" placeholder="例如:亲子玩水" /></el-form-item>
|
|
</div>
|
|
<el-form-item label="路线封面 URL" required>
|
|
<el-input v-model="routeDraft.image" :disabled="saving" placeholder="https://..." />
|
|
<el-image v-if="routeDraft.image" class="site-image-preview" :src="routeDraft.image" fit="cover" :preview-src-list="[routeDraft.image]" />
|
|
</el-form-item>
|
|
|
|
<DetailFields v-model="detailDraft" :advisors="advisors" :disabled="saving" />
|
|
<el-alert v-if="error" :title="error" type="error" :closable="false" show-icon />
|
|
<div class="editor-actions">
|
|
<el-button :disabled="saving" @click="emit('cancel')">取消</el-button>
|
|
<el-button type="primary" native-type="submit" :loading="saving">{{ editing ? "保存修改" : "创建路线" }}</el-button>
|
|
</div>
|
|
</el-form>
|
|
</template>
|