u
This commit is contained in:
1 parent
15e0ae5b67
commit
e434374db5
71 files changed
+5726
-329
No files matched your search
@@ -0,0 +1,253 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 模块基础信息面板(s_module)。
|
||||
*
|
||||
* 数据契约:v-model:module 绑定父级持有的「编辑草稿」(选中模块的 s_module 行副本),
|
||||
* 面板就地修改字段(与原型一致);保存编排在父级,本面板只管 UI 与校验。
|
||||
* 编码(b_id 即主键):仅未落库草稿可改,改动时同步派生多语言键 module.xxx;
|
||||
* 类型切换会清掉不适用字段(module 无数据来源;virtual 无保存表)。
|
||||
*
|
||||
* 校验规则(随输入实时提示;保存前由父级调 validate() 统一跑一遍):
|
||||
* - 编码:必填、2-50 位、小写字母开头、只含小写字母/数字/下划线、禁止负数临时编码;
|
||||
* - 名称 / 类型必填;data 的查询来源(表或 SQL)、主键字段、保存表必填。
|
||||
*/
|
||||
import { computed, ref } from 'vue'
|
||||
import { G3Form, G3FormGroup, G3FormItem, G3Input, G3Select, G3Switch } from '@g3soft/ui'
|
||||
import type { FormRule } from '@g3soft/ui'
|
||||
import { buildModuleI18nKey, type ModuleFieldRow, type ModuleNode } from './utils'
|
||||
|
||||
const moduleModel = defineModel<ModuleNode | null>('module', { default: null })
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
/** 本模块的字段清单(s_field 最小投影):「数据范围字段」下拉的候选 */
|
||||
fields?: ModuleFieldRow[]
|
||||
/** 编码只读:已保存模块主键即编码,创建后不可修改 */
|
||||
codeReadonly?: boolean
|
||||
}>(),
|
||||
{ fields: () => [], codeReadonly: false },
|
||||
)
|
||||
|
||||
const typeOptions = [
|
||||
{ label: '模块', value: 'module' },
|
||||
{ label: '数据模块', value: 'data' },
|
||||
{ label: '虚拟模块', value: 'virtual' },
|
||||
]
|
||||
|
||||
const isData = computed(() => moduleModel.value?.b_module_type === 'data')
|
||||
const isVirtual = computed(() => moduleModel.value?.b_module_type === 'virtual')
|
||||
/** data / virtual 拥有查询来源与字段定义 */
|
||||
const hasDataSource = computed(() => isData.value || isVirtual.value)
|
||||
|
||||
/** 数据范围字段候选(本模块字段),空 b_field 的行直接剔除 */
|
||||
const scopeFieldOptions = computed(() =>
|
||||
props.fields
|
||||
.filter((field) => field.b_field != null && String(field.b_field) !== '')
|
||||
.map((field) => ({
|
||||
label: String(field.b_name || field.b_field),
|
||||
value: String(field.b_field),
|
||||
})),
|
||||
)
|
||||
|
||||
const formRules = computed<Record<string, FormRule[]>>(() => ({
|
||||
b_id: [
|
||||
{ required: true, message: '请填写模块编码' },
|
||||
{
|
||||
validator: (value) => {
|
||||
const code = String(value ?? '').trim()
|
||||
if (!code) return '编码不能为空'
|
||||
if (/^-\d+$/.test(code)) return '新建模块编码不能以负数临时值保存'
|
||||
if (code.length < 2 || code.length > 50) return '编码长度需为 2-50 个字符'
|
||||
return /^[a-z][a-z0-9_]*$/.test(code)
|
||||
? true
|
||||
: '编码须以小写字母开头,且只能包含小写字母、数字和下划线'
|
||||
},
|
||||
},
|
||||
],
|
||||
b_name: [{ required: true, message: '请填写模块名称' }],
|
||||
b_module_type: [{ required: true, message: '请选择模块类型' }],
|
||||
b_view_table: isData.value
|
||||
? [
|
||||
{
|
||||
validator: (value) => {
|
||||
if (String(value ?? '').trim()) return true
|
||||
if (String(moduleModel.value?.b_query_sql ?? '').trim()) return true
|
||||
return '请填写查询表/视图,或配置查询 SQL'
|
||||
},
|
||||
},
|
||||
]
|
||||
: [],
|
||||
b_key_field: isData.value ? [{ required: true, message: '请填写主键字段' }] : [],
|
||||
b_save_table: isData.value ? [{ required: true, message: '请填写保存表' }] : [],
|
||||
}))
|
||||
|
||||
const formRef = ref<InstanceType<typeof G3Form> | null>(null)
|
||||
|
||||
/** 校验全表:返回 [{ name, errors }],空数组即通过(供保存前调用) */
|
||||
async function validate() {
|
||||
return (await formRef.value?.validateAll()) ?? []
|
||||
}
|
||||
|
||||
defineExpose({ validate })
|
||||
|
||||
/** 就地写草稿字段:空字符串统一收敛成 null(数据库可空列的约定) */
|
||||
function setField(field: string, value: unknown) {
|
||||
const draft = moduleModel.value as Record<string, unknown> | null
|
||||
if (!draft) return
|
||||
draft[field] = value === '' ? null : value
|
||||
}
|
||||
|
||||
/** 编码变化:始终以完整编码重新派生多语言键 */
|
||||
function onCodeInput(value: string | number) {
|
||||
setField('b_id', value)
|
||||
setField('b_i18n', buildModuleI18nKey(String(value ?? '')))
|
||||
}
|
||||
|
||||
/** 切换类型:清掉不适用字段(module 无数据来源;virtual 无保存表),并刷新多语言键 */
|
||||
function onModuleTypeChange(value: unknown) {
|
||||
const type = String(value ?? '')
|
||||
setField('b_module_type', type)
|
||||
if (type === 'module') {
|
||||
setField('b_view_table', null)
|
||||
setField('b_save_table', null)
|
||||
setField('b_key_field', null)
|
||||
setField('b_scope_field', null)
|
||||
setField('b_order_sql', null)
|
||||
setField('b_query_sql', null)
|
||||
} else if (type === 'virtual') {
|
||||
setField('b_save_table', null)
|
||||
}
|
||||
setField('b_i18n', buildModuleI18nKey(String(moduleModel.value?.b_id ?? '')))
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="moduleModel" class="mm-basic">
|
||||
<G3Form
|
||||
ref="formRef"
|
||||
:model="moduleModel"
|
||||
:rules="formRules"
|
||||
:columns="3"
|
||||
max-width="720px"
|
||||
label-width="96px"
|
||||
>
|
||||
<G3FormGroup title="标识与归属">
|
||||
<G3FormItem label="模块编码" name="b_id" required>
|
||||
<G3Input
|
||||
:model-value="moduleModel.b_id ?? ''"
|
||||
:disabled="codeReadonly"
|
||||
@update:model-value="onCodeInput"
|
||||
/>
|
||||
</G3FormItem>
|
||||
<G3FormItem label="模块名称" name="b_name" required>
|
||||
<G3Input
|
||||
:model-value="moduleModel.b_name ?? ''"
|
||||
@update:model-value="(value) => setField('b_name', value)"
|
||||
/>
|
||||
</G3FormItem>
|
||||
<G3FormItem label="模块类型" name="b_module_type" required>
|
||||
<G3Select
|
||||
:model-value="moduleModel.b_module_type ?? ''"
|
||||
:options="typeOptions"
|
||||
@update:model-value="onModuleTypeChange"
|
||||
/>
|
||||
</G3FormItem>
|
||||
<G3FormItem label="多语言键" name="b_i18n" :span="2">
|
||||
<G3Input :model-value="moduleModel.b_i18n ?? ''" readonly />
|
||||
</G3FormItem>
|
||||
</G3FormGroup>
|
||||
|
||||
<G3FormGroup v-if="hasDataSource" title="数据来源">
|
||||
<G3FormItem label="查询表/视图" name="b_view_table" :required="isData">
|
||||
<G3Input
|
||||
:model-value="moduleModel.b_view_table ?? ''"
|
||||
@update:model-value="(value) => setField('b_view_table', value)"
|
||||
/>
|
||||
</G3FormItem>
|
||||
<G3FormItem label="主键字段" name="b_key_field" :required="isData">
|
||||
<G3Input
|
||||
:model-value="moduleModel.b_key_field ?? ''"
|
||||
@update:model-value="(value) => setField('b_key_field', value)"
|
||||
/>
|
||||
</G3FormItem>
|
||||
<!-- 数据范围字段:记录归属人字段(如录入人)。
|
||||
用户数据范围面板只选「看到哪一档」,用哪个字段判在这里定 ——
|
||||
同一个模块的所有用户必须用同一字段,否则同一份数据对不同用户含义不同 -->
|
||||
<G3FormItem label="数据范围字段" name="b_scope_field">
|
||||
<G3Select
|
||||
:model-value="moduleModel.b_scope_field ?? ''"
|
||||
:options="scopeFieldOptions"
|
||||
clearable
|
||||
title="记录归属人字段:「仅本人」按它比对,「本部门 / 本部门及下级」由该用户反查部门;留空表示本模块不按字段判范围"
|
||||
@update:model-value="(value) => setField('b_scope_field', value)"
|
||||
/>
|
||||
</G3FormItem>
|
||||
<G3FormItem v-if="isData" label="保存表" name="b_save_table" required>
|
||||
<G3Input
|
||||
:model-value="moduleModel.b_save_table ?? ''"
|
||||
@update:model-value="(value) => setField('b_save_table', value)"
|
||||
/>
|
||||
</G3FormItem>
|
||||
<G3FormItem label="默认排序 SQL" name="b_order_sql" :span="isData ? 1 : 3">
|
||||
<G3Input
|
||||
:model-value="moduleModel.b_order_sql ?? ''"
|
||||
@update:model-value="(value) => setField('b_order_sql', value)"
|
||||
/>
|
||||
</G3FormItem>
|
||||
</G3FormGroup>
|
||||
|
||||
<G3FormGroup v-if="hasDataSource" title="查询与扩展">
|
||||
<G3FormItem label="查询 SQL" name="b_query_sql" :span="3">
|
||||
<G3Input
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
:model-value="moduleModel.b_query_sql ?? ''"
|
||||
@update:model-value="(value) => setField('b_query_sql', value)"
|
||||
/>
|
||||
</G3FormItem>
|
||||
<G3FormItem label="扩展配置 JSON" name="b_config_json" :span="3">
|
||||
<G3Input
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
:model-value="moduleModel.b_config_json ?? ''"
|
||||
@update:model-value="(value) => setField('b_config_json', value)"
|
||||
/>
|
||||
</G3FormItem>
|
||||
</G3FormGroup>
|
||||
|
||||
<G3FormGroup title="行为设置">
|
||||
<G3FormItem label="排序号" name="b_xh">
|
||||
<G3Input
|
||||
type="number"
|
||||
:model-value="moduleModel.b_xh ?? ''"
|
||||
@update:model-value="(value) => setField('b_xh', value)"
|
||||
/>
|
||||
</G3FormItem>
|
||||
<G3FormItem label="启用" name="b_canuse">
|
||||
<G3Switch
|
||||
:model-value="Number(moduleModel.b_canuse) === 1"
|
||||
@update:model-value="(value) => setField('b_canuse', value ? 1 : 0)"
|
||||
/>
|
||||
</G3FormItem>
|
||||
</G3FormGroup>
|
||||
|
||||
<G3FormGroup title="更多">
|
||||
<G3FormItem label="描述" name="b_bz" :span="3">
|
||||
<G3Input
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
:model-value="moduleModel.b_bz ?? ''"
|
||||
@update:model-value="(value) => setField('b_bz', value)"
|
||||
/>
|
||||
</G3FormItem>
|
||||
</G3FormGroup>
|
||||
</G3Form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 面板内容自带内边距(骨架的 pane 只负责滚动,不设 padding) */
|
||||
.mm-basic {
|
||||
padding: 4px 0 16px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user