20260904232445

This commit is contained in:
oneao committed 2026-09-04 23:24:45 +08:00
1 parent 4e167df0f0
commit 1b01a31252
19 files changed
+1099 -1013

No files matched your search

@@ -11,6 +11,7 @@ import { computed, ref } from 'vue'
import { Languages } from '@lucide/vue'
import { Button, Form, FormItem, Input, Select } from '@/components/ui'
import IconPicker from '@/components/icon-picker/IconPicker.vue'
import { menuRouteOptions } from '@/router/routes'
import { MENU_TYPE_OPTIONS } from './utils'
const props = defineProps({
@@ -30,6 +31,21 @@ const maindata = defineModel({ type: Object, default: null })
const isPage = computed(() => maindata.value?.b_menu_type === 'page')
const isExternal = computed(() => maindata.value?.b_menu_type === 'external')
/**
* 路由地址候选:由静态路由表(@/router/routes)派生的页面路由,
* Select 的默认筛选即对 label / value 做包含匹配,等价于 like '%关键词%',
* 输入 "/module" 命中路径、输入 "模块" 命中标题。
* 库里已存但已不在路由表中的旧值(历史数据 / 路由已下线)补进首项,
* 否则它会变成下拉里选不到的孤立值。
*/
const routeOptions = computed(() => {
const current = String(maindata.value?.b_route ?? '').trim()
if (!current) return menuRouteOptions
return menuRouteOptions.some((option) => option.value === current)
? menuRouteOptions
: [{ value: current, label: `${current}(已不在路由表中)` }, ...menuRouteOptions]
})
/* ---------- 校验规则:错误直接显示在对应 FormItem 下方 ---------- */
const formRules = computed(() => ({
b_code: [{ required: true, message: '请填写菜单编码' }],
@@ -107,9 +123,15 @@ function onMenuTypeChange(menuType) {
@update:model-value="onMenuTypeChange"
/>
</FormItem>
<!-- page: 路由地址 -->
<!-- page: 路由地址:从静态路由表选择(输入即按路径 / 标题模糊匹配) -->
<FormItem v-if="isPage" label="路由地址" name="b_route" required>
<Input :model-value="maindata.b_route" @update:model-value="(v) => setField('b_route', v)" />
<Select
:model-value="maindata.b_route"
:options="routeOptions"
not-found-text="无匹配路由"
clearable
@update:model-value="(v) => setField('b_route', v)"
/>
</FormItem>
<!-- external: 外链地址(与路由地址共用 b_route 字段) -->
<FormItem v-if="isExternal" label="外链地址" name="b_route" required>
@@ -734,7 +734,11 @@ async function buildSaveData() {
const newPowerRows = []
// 访问权限点(s_power.b_power_type='menu',由菜单编码自动同步)
if (powerCode) {
// 目录必须整块跳过:上方分支刚把该菜单的访问权限点整条 deletes 掉,
// 这里若继续按「库里查得到」把它塞进 updates,就会去更新一个本次已被删除的行
// (同一事务内先删后改 → 0 行 → 后端抛「修改记录数异常: 0」→ 整个保存回滚,连图标也存不进去);
// 查不到时走 inserts 更糟:把本该清掉的目录权限又写回去。
if (!menuIsDirectory && powerCode) {
const previousCode = String(maindata_org.value?.b_code || '').trim()
const previousPowerCode = previousCode ? `menu.${previousCode}` : ''
if (previousPowerCode && previousPowerCode !== powerCode) {
@@ -1,83 +0,0 @@
<script setup>
import { computed } from 'vue'
import { useRoute } from 'vue-router'
defineOptions({ name: 'TestPage' })
import { Button } from '@/components/ui'
const route = useRoute()
const title = computed(() => route.meta?.title ?? '测试页')
const desc = computed(() => route.meta?.desc ?? '')
const index = computed(() => Number(route.params.id ?? 0))
</script>
<template>
<div class="fms-test-page">
<header class="fms-test-page__head">
<h1>{{ title }}</h1>
<p v-if="desc">{{ desc }}</p>
</header>
<div class="fms-test-page__body">
<div class="fms-test-page__card">
<p>这是用于测试 Tabs 导航的占位页面。</p>
<p>
当前路由:<code>{{ route.fullPath }}</code>
</p>
<p>
页签索引:<strong>{{ index }}</strong>
</p>
</div>
<div class="fms-test-page__actions">
<Button type="primary" @click="$router.push(`/test/${index + 1}`)">
前往下一个测试页 ({{ index + 1 }})
</Button>
<Button @click="$router.push('/dashboard')">回到工作台</Button>
</div>
</div>
</div>
</template>
<style scoped>
.fms-test-page {
padding: 16px;
}
.fms-test-page__head h1 {
margin: 0 0 4px;
font-size: 18px;
font-weight: 600;
}
.fms-test-page__head p {
margin: 0;
color: var(--fms-text-secondary);
font-size: 13px;
}
.fms-test-page__body {
margin-top: 16px;
}
.fms-test-page__card {
padding: 16px;
border-radius: var(--fms-control-radius);
background: var(--fms-card);
box-shadow: var(--fms-shadow);
font-size: 13px;
line-height: 1.8;
}
.fms-test-page__card code {
padding: 1px 6px;
border-radius: 4px;
background: var(--fms-background);
}
.fms-test-page__actions {
display: flex;
gap: 8px;
margin-top: 16px;
}
</style>