Files
workspace/code/fms/fms-vue/src/views/module-management/components/ModuleFieldsPanel.vue
T
2026-07-26 22:52:21 +08:00

233 lines
5.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import { ref, watch, computed } from "vue";
import { message, Modal } from "antdv-next";
import FmsTable from "@/components/fms-table/FmsTable.vue";
const FIELD_TYPES = [
{ label: "输入框", value: "input" },
{ label: "文本域", value: "textarea" },
{ label: "数字", value: "number" },
{ label: "金额", value: "money" },
{ label: "日期", value: "date" },
{ label: "日期时间", value: "datetime" },
{ label: "开关", value: "switch" },
{ label: "下拉选择", value: "select" },
];
const props = defineProps({
moduleId: { type: String, required: true },
fields: { type: Array, default: () => [] },
viewTable: { type: String, default: null },
loading: { type: Boolean, default: false },
});
const emit = defineEmits(["update", "sync"]);
const localFields = ref([]);
const activeKey = ref(null);
// 同步:未配置查询视图时,手动输入表/视图名
const syncOpen = ref(false);
const syncTable = ref("");
function onSyncClick() {
if (props.viewTable) {
emit("sync", props.viewTable);
return;
}
syncTable.value = "";
syncOpen.value = true;
}
function onSyncConfirm() {
const name = syncTable.value.trim();
if (!name) {
message.warning("请输入表或视图名");
return;
}
syncOpen.value = false;
emit("sync", name);
}
watch(
() => props.fields,
(val) => {
if (val === localFields.value) return;
localFields.value = val?.map((r) => ({ ...r })) || [];
},
{ immediate: true },
);
function emitUpdate() {
emit("update", localFields.value);
}
// 编辑器已直接更新当前行,这里只向父级同步草稿。
function onCellChange() {
emitUpdate();
}
// 行多选:selectedRowKeys 由 FmsTable 的选择列回写;
// 传了 rowSelection 时 FmsTable 会在最前列插入复选框列(也是单击进入编辑态所需的机制)。
const selectedRowKeys = ref([]);
const rowSelection = computed(() => ({
selectedRowKeys: selectedRowKeys.value,
onChange: (keys) => {
selectedRowKeys.value = keys;
},
}));
function deleteRow(record) {
if (props.loading) return;
localFields.value = localFields.value.filter((r) => r.b_id !== record.b_id);
const k = String(record.b_id);
if (selectedRowKeys.value.includes(k)) {
selectedRowKeys.value = selectedRowKeys.value.filter((x) => x !== k);
}
emitUpdate();
}
// 批量删除选中行
function batchDelete() {
if (props.loading) return;
const keys = selectedRowKeys.value;
if (!keys.length) return;
Modal.confirm({
title: `确定删除选中的 ${keys.length} 条记录?`,
okText: "删除",
okType: "danger",
cancelText: "取消",
onOk: () => {
const delSet = new Set(keys);
localFields.value = localFields.value.filter((r) => !delSet.has(r.b_id));
selectedRowKeys.value = [];
if (activeKey.value && delSet.has(activeKey.value)) activeKey.value = "";
emitUpdate();
},
});
}
const columns = ref([
{ dataIndex: "b_field", title: "字段名", width: 140 },
{
dataIndex: "b_name",
title: "字段名称",
width: 140,
editor: { type: "input", onChange: () => emitUpdate() },
},
{
dataIndex: "b_type",
title: "控件类型",
width: 130,
editor: {
type: "select",
options: FIELD_TYPES,
onChange: () => emitUpdate(),
},
},
{
dataIndex: "b_default_value",
title: "默认值",
width: 120,
editor: { type: "input", onChange: () => emitUpdate() },
},
{
dataIndex: "b_i18n",
title: "多语言键",
width: 140,
editor: { type: "input", onChange: () => emitUpdate() },
},
{
dataIndex: "b_canuse",
title: "启用",
width: 70,
align: "center",
editor: { type: "checkbox", onChange: () => emitUpdate() },
},
{
dataIndex: "actions",
title: "操作",
width: 60,
actions: [
{
type: "delete",
label: "删除",
confirm: "确定删除该字段?",
onClick: (row) => deleteRow(row),
},
],
},
]);
</script>
<template>
<div class="fields-panel">
<div class="panel-toolbar">
<a-space>
<a-button type="primary" :disabled="loading" @click="onSyncClick">
同步字段{{ viewTable ? ` (${viewTable})` : "" }}
</a-button>
<a-button danger :disabled="loading || !selectedRowKeys.length" @click="batchDelete">
批量删除{{ selectedRowKeys.length ? `(${selectedRowKeys.length})` : "" }}
</a-button>
<span v-if="!viewTable" class="panel-hint">未配置查询视图,点击后手动输入表名</span>
</a-space>
</div>
<a-modal
v-model:open="syncOpen"
title="同步字段"
@ok="onSyncConfirm"
:confirm-loading="loading"
>
<p>当前模块未配置查询视图,请填写要同步字段的表或视图名:</p>
<a-input
v-model:value="syncTable"
placeholder="例如 s_log_login 或 v_biz"
@press-enter="onSyncConfirm"
/>
</a-modal>
<div class="panel-body">
<FmsTable
v-model:columns="columns"
:data-source="localFields"
:editable="!loading"
row-key="b_id"
:active-row-key="activeKey"
:row-selection="rowSelection"
@row-click="(_, row) => (activeKey = String(row.b_id))"
@change="onCellChange"
/>
</div>
</div>
</template>
<style scoped lang="scss">
.fields-panel {
display: flex;
flex-direction: column;
height: 100%;
}
.panel-toolbar {
display: flex;
flex: none;
gap: 8px;
padding: 12px 20px 0;
}
.panel-body {
flex: 1;
min-height: 0;
padding: 12px 20px 16px;
display: flex;
flex-direction: column;
}
.panel-body > :deep(.fms-table) {
flex: 1;
min-height: 0;
width: 100%;
}
</style>