feat(admin): 未保存离开保护(Batch 3 收口)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -98,6 +98,8 @@
|
||||
): void
|
||||
/** footer 回滚入口:请求父层切到版本历史 */
|
||||
(e: 'navigate', group: string): void
|
||||
/** 未保存修改上报(Task 14:父层三处离开拦截消费) */
|
||||
(e: 'dirty', value: boolean): void
|
||||
}>()
|
||||
|
||||
// ---- 编辑态(group 引用变化 = 保存回写/重置/快照重载 → 重置表单) ----------------------
|
||||
@@ -107,17 +109,25 @@
|
||||
const saving = ref(false)
|
||||
const resetting = ref(false)
|
||||
const actionError = ref('')
|
||||
/** dirty 基线(与表单重建同 watch 刷新——保存/重置回写经父层快照组
|
||||
* 引用变化走同一条链,无需手动清) */
|
||||
const baseline = ref(JSON.stringify(contactToForm(undefined)))
|
||||
|
||||
watch(
|
||||
() => props.group,
|
||||
(group) => {
|
||||
Object.assign(form, contactToForm(group))
|
||||
const next = contactToForm(group)
|
||||
Object.assign(form, next)
|
||||
baseline.value = JSON.stringify(next)
|
||||
saveComment.value = ''
|
||||
actionError.value = ''
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const dirty = computed(() => JSON.stringify(form) !== baseline.value)
|
||||
watch(dirty, (v) => emit('dirty', v), { immediate: true })
|
||||
|
||||
// ---- 保存(contactToPayload → PUT 整组;类型校验后端兜底) ----------------------------
|
||||
|
||||
async function onSave(): Promise<void> {
|
||||
|
||||
@@ -407,6 +407,8 @@
|
||||
): void
|
||||
/** footer 回滚入口:请求父层切到对应组('versions' = 版本历史) */
|
||||
(e: 'navigate', group: string): void
|
||||
/** 未保存修改上报(Task 14:三卡合一,父层三处离开拦截消费) */
|
||||
(e: 'dirty', value: boolean): void
|
||||
}>()
|
||||
|
||||
// ---- 编辑态(组引用变化 = 保存回写/租户切换/重载 → 重置表单与密钥态) ----------------
|
||||
@@ -432,6 +434,12 @@
|
||||
const emailError = ref('')
|
||||
const pushError = ref('')
|
||||
const aiError = ref('')
|
||||
/** dirty 基线(三表单各自 normalize*Group().form 的 JSON 快照——与
|
||||
* resetAll 同 watch 刷新;密钥输入不进基线:掩码 GET 本就无原值可比,
|
||||
* 由 dirty 计算里的三态判定补足) */
|
||||
const emailBaseline = ref(JSON.stringify(normalizeEmailCredsGroup(undefined).form))
|
||||
const pushBaseline = ref(JSON.stringify(normalizePushCredsGroup(undefined).form))
|
||||
const aiBaseline = ref(JSON.stringify(normalizeAiCredsGroup(undefined).form))
|
||||
|
||||
// 测试态(声明先于下方 immediate watch 的 resetAll——避免 TDZ 引用)
|
||||
const emailTestTo = ref('')
|
||||
@@ -454,14 +462,17 @@
|
||||
Object.assign(emailForm, es.form)
|
||||
emailPasswordMasked.value = es.passwordMasked
|
||||
emailPasswordCleared.value = false
|
||||
emailBaseline.value = JSON.stringify(es.form)
|
||||
const ps = normalizePushCredsGroup(pushGroup)
|
||||
Object.assign(pushForm, ps.form)
|
||||
pushApiKeyMasked.value = ps.apiKeyMasked
|
||||
pushApiKeyCleared.value = false
|
||||
pushBaseline.value = JSON.stringify(ps.form)
|
||||
const as = normalizeAiCredsGroup(aiGroup)
|
||||
Object.assign(aiForm, as.form)
|
||||
aiApiKeyMasked.value = as.apiKeyMasked
|
||||
aiApiKeyCleared.value = false
|
||||
aiBaseline.value = JSON.stringify(as.form)
|
||||
emailComment.value = ''
|
||||
pushComment.value = ''
|
||||
aiComment.value = ''
|
||||
@@ -481,6 +492,22 @@
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
/** 未保存判定:三表单任一偏离基线,或密钥三态(输入非空 = 覆盖新值 /
|
||||
* cleared = 显式清除)——密钥输入框基线恒空串,只能独立判定 */
|
||||
const dirty = computed(
|
||||
() =>
|
||||
JSON.stringify(emailForm) !== emailBaseline.value ||
|
||||
emailForm.smtpPassword !== '' ||
|
||||
emailPasswordCleared.value ||
|
||||
JSON.stringify(pushForm) !== pushBaseline.value ||
|
||||
pushForm.apiKey !== '' ||
|
||||
pushApiKeyCleared.value ||
|
||||
JSON.stringify(aiForm) !== aiBaseline.value ||
|
||||
aiForm.apiKey !== '' ||
|
||||
aiApiKeyCleared.value
|
||||
)
|
||||
watch(dirty, (v) => emit('dirty', v), { immediate: true })
|
||||
|
||||
// ---- 保存(前端预检 → 整组 PUT;密钥不动回哨兵保持库值) ------------------------------
|
||||
|
||||
async function onSaveEmail(): Promise<void> {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</div>
|
||||
</ElForm>
|
||||
|
||||
<ElCollapse class="seed-collapse">
|
||||
<ElCollapse v-model="openSections" class="seed-collapse">
|
||||
<ElCollapseItem name="seed" title="会话上下文映射(context_seed_keys · 高级)">
|
||||
<p class="section-note">
|
||||
产品字段 → 会话上下文种子键的映射;空行保存时自动剔除,清空全部行 =
|
||||
@@ -86,7 +86,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
RESET_GROUP_LABELS,
|
||||
engineToForm,
|
||||
engineToPayload,
|
||||
validateEngineForm,
|
||||
@@ -116,6 +115,8 @@
|
||||
): void
|
||||
/** footer 回滚入口:请求父层切到版本历史 */
|
||||
(e: 'navigate', group: string): void
|
||||
/** 未保存修改上报(Task 14:父层三处离开拦截消费) */
|
||||
(e: 'dirty', value: boolean): void
|
||||
}>()
|
||||
|
||||
// ---- 编辑态(group 引用变化 = 保存回写/重置/快照重载 → 重置表单) ----------------------
|
||||
@@ -125,19 +126,27 @@
|
||||
const saving = ref(false)
|
||||
const resetting = ref(false)
|
||||
const actionError = ref('')
|
||||
/** 高级折叠区默认收起(引擎级参数——按需展开) */
|
||||
/** 高级折叠区展开项(默认收起——引擎级参数按需展开) */
|
||||
const openSections = ref<string[]>([])
|
||||
/** dirty 基线(与表单重建同 watch 刷新——KeyValueEditor 行变化经
|
||||
* form.seedRows 引用/行内变更自然涵盖) */
|
||||
const baseline = ref(JSON.stringify(engineToForm(undefined)))
|
||||
|
||||
watch(
|
||||
() => props.group,
|
||||
(group) => {
|
||||
Object.assign(form, engineToForm(group))
|
||||
const next = engineToForm(group)
|
||||
Object.assign(form, next)
|
||||
baseline.value = JSON.stringify(next)
|
||||
saveComment.value = ''
|
||||
actionError.value = ''
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const dirty = computed(() => JSON.stringify(form) !== baseline.value)
|
||||
watch(dirty, (v) => emit('dirty', v), { immediate: true })
|
||||
|
||||
// ---- 保存(顺序纪律:两段校验合并 → payload → PUT 整组) -----------------------------
|
||||
|
||||
async function onSave(): Promise<void> {
|
||||
@@ -180,9 +189,9 @@
|
||||
try {
|
||||
const res = await confirmAndResetGroup(props.slug, 'engine')
|
||||
if (!res) return // 用户取消确认
|
||||
ElMessage.success(
|
||||
`${RESET_GROUP_LABELS.engine}已重置出厂(版本 v${res.version},热生效)`
|
||||
)
|
||||
// 重置成功 toast 用卡头标题(「聊天限流」)——RESET_GROUP_LABELS 的
|
||||
// 「引擎」是后端组名镜像,与用户视角的卡头标题错位(Task 14 折入)
|
||||
ElMessage.success(`聊天限流已重置出厂(版本 v${res.version},热生效)`)
|
||||
emit('saved', { version: res.version, group: 'engine', config: res.config })
|
||||
} catch (error) {
|
||||
actionError.value = error instanceof Error ? error.message : '重置失败'
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
RESET_GROUP_LABELS,
|
||||
miscToForm,
|
||||
miscToPayload,
|
||||
validateKeyValueRows,
|
||||
@@ -88,6 +87,8 @@
|
||||
): void
|
||||
/** footer 回滚入口:请求父层切到版本历史 */
|
||||
(e: 'navigate', group: string): void
|
||||
/** 未保存修改上报(Task 14:父层三处离开拦截消费) */
|
||||
(e: 'dirty', value: boolean): void
|
||||
}>()
|
||||
|
||||
// ---- 编辑态(group 引用变化 = 保存回写/重置/快照重载 → 重置表单) ----------------------
|
||||
@@ -97,17 +98,25 @@
|
||||
const saving = ref(false)
|
||||
const resetting = ref(false)
|
||||
const actionError = ref('')
|
||||
/** dirty 基线(与表单重建同 watch 刷新——KeyValueEditor 行变化经
|
||||
* form.extrasRows 引用/行内变更自然涵盖) */
|
||||
const baseline = ref(JSON.stringify(miscToForm(undefined)))
|
||||
|
||||
watch(
|
||||
() => props.group,
|
||||
(group) => {
|
||||
Object.assign(form, miscToForm(group))
|
||||
const next = miscToForm(group)
|
||||
Object.assign(form, next)
|
||||
baseline.value = JSON.stringify(next)
|
||||
saveComment.value = ''
|
||||
actionError.value = ''
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const dirty = computed(() => JSON.stringify(form) !== baseline.value)
|
||||
watch(dirty, (v) => emit('dirty', v), { immediate: true })
|
||||
|
||||
// ---- 保存(validateKeyValueRows → payload → PUT 整组) -------------------------------
|
||||
|
||||
async function onSave(): Promise<void> {
|
||||
@@ -147,9 +156,9 @@
|
||||
try {
|
||||
const res = await confirmAndResetGroup(props.slug, 'misc')
|
||||
if (!res) return // 用户取消确认
|
||||
ElMessage.success(
|
||||
`${RESET_GROUP_LABELS.misc}已重置出厂(版本 v${res.version},热生效)`
|
||||
)
|
||||
// 重置成功 toast 用卡头标题(「国家区号映射」)——RESET_GROUP_LABELS 的
|
||||
// 「杂项」是后端组名镜像,与用户视角的卡头标题错位(Task 14 折入)
|
||||
ElMessage.success(`国家区号映射已重置出厂(版本 v${res.version},热生效)`)
|
||||
emit('saved', { version: res.version, group: 'misc', config: res.config })
|
||||
} catch (error) {
|
||||
actionError.value = error instanceof Error ? error.message : '重置失败'
|
||||
|
||||
@@ -183,6 +183,8 @@
|
||||
(e: 'saved', payload: { version: number; group: 'prompts'; config: Record<string, unknown> }): void
|
||||
/** footer 回滚链接:请求父层切到版本历史组 */
|
||||
(e: 'navigate', group: string): void
|
||||
/** 未保存修改上报(Task 14:父层三处离开拦截消费) */
|
||||
(e: 'dirty', value: boolean): void
|
||||
}>()
|
||||
|
||||
// ---- 编辑态(group 引用变化 = 租户切换/重载 → 重置表单) ----------------------------
|
||||
@@ -191,17 +193,25 @@
|
||||
const saveComment = ref('')
|
||||
const saving = ref(false)
|
||||
const actionError = ref('')
|
||||
/** dirty 基线(与表单重建同 watch 刷新——保存/重置回写经父层快照组
|
||||
* 引用变化走同一条链,无需手动清) */
|
||||
const baseline = ref(JSON.stringify(normalizePromptsGroup(undefined)))
|
||||
|
||||
watch(
|
||||
() => props.group,
|
||||
(group) => {
|
||||
Object.assign(draft, normalizePromptsGroup(group))
|
||||
const next = normalizePromptsGroup(group)
|
||||
Object.assign(draft, next)
|
||||
baseline.value = JSON.stringify(next)
|
||||
saveComment.value = ''
|
||||
actionError.value = ''
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const dirty = computed(() => JSON.stringify(draft) !== baseline.value)
|
||||
watch(dirty, (v) => emit('dirty', v), { immediate: true })
|
||||
|
||||
// ---- 字节实时提示 -------------------------------------------------------------------
|
||||
|
||||
const byteInfos = computed(() => {
|
||||
|
||||
@@ -225,6 +225,8 @@
|
||||
(e: 'saved', payload: { version: number; group: 'wall'; config: Record<string, unknown> }): void
|
||||
/** footer 回滚链接:请求父层切到版本历史组 */
|
||||
(e: 'navigate', group: string): void
|
||||
/** 未保存修改上报(Task 14:父层三处离开拦截消费) */
|
||||
(e: 'dirty', value: boolean): void
|
||||
}>()
|
||||
|
||||
// ---- 编辑态(group 引用变化 = 租户切换/重载 → 重建模型) ----------------------------
|
||||
@@ -237,11 +239,17 @@
|
||||
const testing = ref(false)
|
||||
const testResults = ref<WallTestResponse['results'] | null>(null)
|
||||
const lastWallSource = ref<WallTestResponse['wall_source'] | ''>('')
|
||||
/** dirty 对比基线:编辑模型的深克隆(JSON 往返脱离响应式;与模型重建
|
||||
* 同 watch 刷新——保存/重置回写经父层快照组引用变化走同一条链) */
|
||||
const baseline = ref<WallEditModel | null>(null)
|
||||
|
||||
watch(
|
||||
() => props.group,
|
||||
(group) => {
|
||||
model.value = initWallEdit(group)
|
||||
baseline.value = model.value
|
||||
? (JSON.parse(JSON.stringify(model.value)) as WallEditModel)
|
||||
: null
|
||||
saveComment.value = ''
|
||||
actionError.value = ''
|
||||
testResults.value = null
|
||||
@@ -249,6 +257,17 @@
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
/** 未保存判定:序列化等价物对比——buildWallPayload 产物不含 showComment
|
||||
* 等瞬态 UI 字段(展开备注再收起不会误报 dirty) */
|
||||
const dirty = computed(() => {
|
||||
if (!model.value || !baseline.value) return false
|
||||
return (
|
||||
JSON.stringify(buildWallPayload(model.value)) !==
|
||||
JSON.stringify(buildWallPayload(baseline.value))
|
||||
)
|
||||
})
|
||||
watch(dirty, (v) => emit('dirty', v), { immediate: true })
|
||||
|
||||
// ---- 主面小卡白话标签 ------------------------------------------------------------------
|
||||
|
||||
const NO_CONTACT_LABELS: Record<(typeof NO_CONTACT_KEYS)[number], string> = {
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
退役通用 JSON 整组编辑(旧 JsonGroup 组件删除),换 ContactFormPane /
|
||||
EngineFormPane / MiscFormPane 三表单卡(ConfigCard 基线 + KeyValueEditor
|
||||
行编辑——engine context_seed_keys / misc name_to_region_extras 共用)
|
||||
- Task 14 未保存离开保护(Batch 3 收口):七编辑面 dirty 上报(brand
|
||||
内联基线快照 + 六 pane emit dirty)汇聚 paneDirty;三处拦截——切导航组
|
||||
(onGroupSelect)/ 切租户(ElSelect 改 :model-value + change 前置确认,
|
||||
取消时显示与草稿均还原)/ 路由离开(onBeforeRouteLeave)——
|
||||
ElMessageBox「丢弃修改 / 留下」确认后才放行
|
||||
- 挂件实时预览:**本地简化预览组件**(标题/颜色气泡/头像/话题/占位的
|
||||
静态模拟,零后端依赖、即时反映表单值;真机预览留给嵌入代码上真站)
|
||||
- 嵌入代码:域名取租户详情 sites[].domains(超管可枚举全租户,租户管理
|
||||
@@ -57,10 +62,11 @@
|
||||
<ElTag v-if="version !== null" type="info" size="large">当前版本 v{{ version }}</ElTag>
|
||||
<ElSelect
|
||||
v-if="isSuper"
|
||||
v-model="activeSlug"
|
||||
:model-value="activeSlug"
|
||||
:loading="tenantsLoading"
|
||||
placeholder="选择租户"
|
||||
class="tenant-switcher"
|
||||
@change="onTenantSwitch"
|
||||
>
|
||||
<ElOption
|
||||
v-for="tenant in tenants"
|
||||
@@ -431,6 +437,7 @@
|
||||
:group="snapshot?.prompts"
|
||||
@saved="onPaneSaved"
|
||||
@navigate="onGroupSelect"
|
||||
@dirty="childPaneDirty = $event"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -453,6 +460,7 @@
|
||||
:group="snapshot?.wall"
|
||||
@saved="onPaneSaved"
|
||||
@navigate="onGroupSelect"
|
||||
@dirty="childPaneDirty = $event"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -477,6 +485,7 @@
|
||||
:ai-group="snapshot?.ai"
|
||||
@saved="onPaneSaved"
|
||||
@navigate="onGroupSelect"
|
||||
@dirty="childPaneDirty = $event"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -499,6 +508,7 @@
|
||||
:group="snapshot?.contact"
|
||||
@saved="onPaneSaved"
|
||||
@navigate="onGroupSelect"
|
||||
@dirty="childPaneDirty = $event"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -521,6 +531,7 @@
|
||||
:group="snapshot?.engine"
|
||||
@saved="onPaneSaved"
|
||||
@navigate="onGroupSelect"
|
||||
@dirty="childPaneDirty = $event"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -543,6 +554,7 @@
|
||||
:group="snapshot?.misc"
|
||||
@saved="onPaneSaved"
|
||||
@navigate="onGroupSelect"
|
||||
@dirty="childPaneDirty = $event"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -564,6 +576,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { useAuthStore } from '@/store/modules/auth'
|
||||
import { fetchTenantDetail, type TenantDetailResponse } from '@/api/tenants'
|
||||
import { useTenantOptions } from '@/composables/useTenantOptions'
|
||||
@@ -638,7 +652,14 @@
|
||||
|
||||
const activeGroup = ref<GroupKey>('brand')
|
||||
|
||||
function onGroupSelect(index: string): void {
|
||||
/** 六 pane 的 dirty 上报值(pane 卸载不触发 emit——切组/切租户放行时
|
||||
* 手动清残留;brand 内联表单不入此列,直接并入 paneDirty) */
|
||||
const childPaneDirty = ref(false)
|
||||
|
||||
async function onGroupSelect(index: string): Promise<void> {
|
||||
if (index === activeGroup.value) return
|
||||
if (!(await confirmDiscard())) return
|
||||
childPaneDirty.value = false
|
||||
activeGroup.value = index as GroupKey
|
||||
}
|
||||
|
||||
@@ -685,6 +706,32 @@
|
||||
const saveComment = ref('')
|
||||
const saving = ref(false)
|
||||
|
||||
// ---- 未保存离开保护(Task 14:dirty 汇聚 + 三处拦截消费) ----------------------------
|
||||
|
||||
/** 品牌表单基线快照(加载/保存回写/重置/回滚/切租户清空后刷新) */
|
||||
const brandBaseline = ref(JSON.stringify(form))
|
||||
const brandDirty = computed(() => JSON.stringify(form) !== brandBaseline.value)
|
||||
/** 全页未保存态:brand 内联表单 + 当前挂载 pane(切导航组/切租户/路由离开消费) */
|
||||
const paneDirty = computed(() => brandDirty.value || childPaneDirty.value)
|
||||
|
||||
/** 离开确认:有未保存修改时弹「丢弃修改 / 留下」;取消(含 ESC/遮罩)= 留下 */
|
||||
async function confirmDiscard(): Promise<boolean> {
|
||||
if (!paneDirty.value) return true
|
||||
try {
|
||||
await ElMessageBox.confirm('有未保存的修改,离开将丢失。确定离开?', '未保存的修改', {
|
||||
type: 'warning',
|
||||
confirmButtonText: '丢弃修改',
|
||||
cancelButtonText: '留下'
|
||||
})
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 路由离开拦截(切导航组/切租户另在 onGroupSelect/onTenantSwitch 内前置拦截)
|
||||
onBeforeRouteLeave(async () => await confirmDiscard())
|
||||
|
||||
async function loadConfig(epoch: number): Promise<void> {
|
||||
const slug = activeSlug.value
|
||||
if (!slug) return
|
||||
@@ -696,6 +743,7 @@
|
||||
version.value = res.version
|
||||
snapshot.value = res.config
|
||||
Object.assign(form, normalizeBrandGroup(res.config.brand))
|
||||
brandBaseline.value = JSON.stringify(form)
|
||||
configLoaded.value = true
|
||||
avatarImgFailed.value = false
|
||||
} catch (error) {
|
||||
@@ -731,6 +779,7 @@
|
||||
version.value = payload.version
|
||||
snapshot.value = payload.config
|
||||
Object.assign(form, normalizeBrandGroup(payload.config.brand))
|
||||
brandBaseline.value = JSON.stringify(form)
|
||||
avatarImgFailed.value = false
|
||||
}
|
||||
|
||||
@@ -751,6 +800,7 @@
|
||||
snapshot.value = { ...snapshot.value, brand: res.config }
|
||||
}
|
||||
Object.assign(form, normalizeBrandGroup(res.config))
|
||||
brandBaseline.value = JSON.stringify(form)
|
||||
avatarImgFailed.value = false
|
||||
}
|
||||
ElMessage.success(`品牌已重置出厂(版本 v${res.version},热生效)`)
|
||||
@@ -798,6 +848,7 @@
|
||||
version.value = res.version
|
||||
// 响应即该组掩码视图(品牌组无密钥字段)——回写表单保持与库一致
|
||||
Object.assign(form, normalizeBrandGroup(res.config))
|
||||
brandBaseline.value = JSON.stringify(form)
|
||||
avatarImgFailed.value = false
|
||||
ElMessage.success(`品牌已保存(版本 v${res.version},热生效)`)
|
||||
} catch {
|
||||
@@ -955,9 +1006,20 @@
|
||||
|
||||
// ---- 装配(租户切换:重置表单/版本/嵌入选择,配置+详情双载) ------------------------------
|
||||
|
||||
/** 切租户前置确认(Task 14):ElSelect 单向 :model-value——取消时 prop
|
||||
* 未变,选择器显示自动还原旧租户且草稿保留;确认后才写 activeSlug 触发
|
||||
* 下方 watch 重载(草稿随重载丢弃) */
|
||||
async function onTenantSwitch(slug: string): Promise<void> {
|
||||
if (!slug || slug === activeSlug.value) return
|
||||
if (!(await confirmDiscard())) return
|
||||
childPaneDirty.value = false
|
||||
activeSlug.value = slug
|
||||
}
|
||||
|
||||
watch(activeSlug, () => {
|
||||
if (!activeSlug.value) return
|
||||
Object.assign(form, blankForm())
|
||||
brandBaseline.value = JSON.stringify(form)
|
||||
version.value = null
|
||||
configLoaded.value = false
|
||||
configError.value = ''
|
||||
|
||||
Reference in New Issue
Block a user