233 lines
5.7 KiB
Vue
233 lines
5.7 KiB
Vue
<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>
|