715 lines
16 KiB
Vue
715 lines
16 KiB
Vue
<script lang="js" setup>
|
|
import {
|
|
checkColumnsApi,
|
|
loadDataApi,
|
|
loadAllColumnsApi,
|
|
loadDataBySqlApi,
|
|
saveObjectApi,
|
|
} from "@/request/api";
|
|
import { ElMessage } from "element-plus";
|
|
import PreviewSql from "./preview-sql.vue";
|
|
import { diffObject } from "@/utils/diff";
|
|
|
|
const tableRef = ref();
|
|
|
|
const loading = ref(false);
|
|
|
|
const leftSize = ref(200);
|
|
const isCollapse = ref(false);
|
|
|
|
const showSql = ref(false);
|
|
|
|
const treeData = [{ b_id: "root", mc_b: "全部分类", isLeaf: false, children: [] }];
|
|
|
|
const treeProps = {
|
|
children: "children",
|
|
label: "mc_b",
|
|
};
|
|
const currentNode = ref(null);
|
|
const currentModule = ref(null);
|
|
const isSelectedNode = computed(() => !!currentNode.value);
|
|
|
|
const tableData = ref([]);
|
|
|
|
const currentRow = ref({});
|
|
const isSelectedRow = computed(() => !!currentRow.value?.col_FieldName);
|
|
let oldRowData = {};
|
|
|
|
const columns = [
|
|
{
|
|
prop: "col_field_name",
|
|
label: "字段",
|
|
width: 140,
|
|
fixed: "left",
|
|
},
|
|
{
|
|
prop: "col_caption",
|
|
label: "标题",
|
|
width: 100,
|
|
fixed: "left",
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_field_name_v",
|
|
label: "视图字段",
|
|
width: 100,
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_group_index",
|
|
label: "分组",
|
|
width: 80,
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_visible",
|
|
label: "显示",
|
|
width: 80,
|
|
type: "checkbox",
|
|
},
|
|
{
|
|
prop: "col_allow_readonly",
|
|
label: "只读",
|
|
width: 80,
|
|
type: "checkbox",
|
|
},
|
|
{
|
|
prop: "col_allow_null",
|
|
label: "可不输",
|
|
width: 80,
|
|
type: "checkbox",
|
|
},
|
|
{
|
|
prop: "col_allow_repeat",
|
|
label: "可重复",
|
|
width: 80,
|
|
type: "checkbox",
|
|
},
|
|
{
|
|
prop: "col_allow_reserve",
|
|
label: "可保留",
|
|
width: 80,
|
|
type: "checkbox",
|
|
},
|
|
{
|
|
prop: "col_search",
|
|
label: "可查找",
|
|
width: 80,
|
|
type: "checkbox",
|
|
},
|
|
{
|
|
prop: "col_search_order",
|
|
label: "查找顺序",
|
|
width: 100,
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_search_group_index",
|
|
label: "查找分组",
|
|
width: 100,
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_type",
|
|
label: "字段类型",
|
|
width: 100,
|
|
type: "option",
|
|
},
|
|
{
|
|
prop: "col_default_value",
|
|
label: "默认值",
|
|
width: 80,
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_width",
|
|
label: "宽度",
|
|
width: 80,
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_position",
|
|
label: "位置",
|
|
width: 80,
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_show_summary",
|
|
label: "合计",
|
|
width: 80,
|
|
type: "checkbox",
|
|
},
|
|
{
|
|
prop: "col_display_format",
|
|
label: "显示格式",
|
|
width: 100,
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_edit",
|
|
label: "编辑",
|
|
width: 80,
|
|
type: "checkbox",
|
|
},
|
|
{
|
|
prop: "col_edit_position",
|
|
label: "编辑位置",
|
|
width: 100,
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_edit_width",
|
|
label: "编辑宽度",
|
|
width: 100,
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_edit_height",
|
|
label: "编辑高度",
|
|
width: 100,
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_edit_source_data",
|
|
label: "编辑数据",
|
|
width: 160,
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_edit_source_field",
|
|
label: "下拉表显示字段",
|
|
width: 100,
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_edit_group_index",
|
|
label: "编辑分组",
|
|
width: 100,
|
|
type: "input",
|
|
},
|
|
{
|
|
prop: "col_edit_batch",
|
|
label: "批量编辑",
|
|
width: 100,
|
|
type: "checkbox",
|
|
},
|
|
{
|
|
prop: "col_show_property",
|
|
label: "属性",
|
|
width: 80,
|
|
type: "checkbox",
|
|
},
|
|
];
|
|
|
|
const editTypeOptions = [
|
|
{
|
|
value: "input",
|
|
label: "input",
|
|
},
|
|
{
|
|
value: "textarea",
|
|
label: "textarea",
|
|
},
|
|
{
|
|
value: "date",
|
|
label: "date",
|
|
},
|
|
{
|
|
value: "datetime",
|
|
label: "datetime",
|
|
},
|
|
{
|
|
value: "singleId",
|
|
label: "singleId",
|
|
},
|
|
{
|
|
value: "singleName",
|
|
label: "singleName",
|
|
},
|
|
{
|
|
value: "option",
|
|
label: "option",
|
|
},
|
|
{
|
|
value: "checkbox",
|
|
label: "checkbox",
|
|
},
|
|
{
|
|
value: "radio",
|
|
label: "radio",
|
|
},
|
|
];
|
|
|
|
function getOptions(field) {
|
|
if (field === "col_type") {
|
|
return editTypeOptions;
|
|
}
|
|
}
|
|
|
|
function updateCollapse() {
|
|
if (isCollapse.value) {
|
|
isCollapse.value = false;
|
|
leftSize.value = 200;
|
|
} else {
|
|
isCollapse.value = true;
|
|
leftSize.value = 0;
|
|
}
|
|
}
|
|
|
|
async function handleTreeLoad(node, resolve) {
|
|
try {
|
|
const { level: nodeLevel, data: nodeData } = node;
|
|
if (nodeLevel === 0) {
|
|
resolve([
|
|
{
|
|
b_id: "root",
|
|
mc_b: "全部分类",
|
|
children: [],
|
|
isLeaf: false,
|
|
},
|
|
]);
|
|
return;
|
|
}
|
|
|
|
const currentLevel = nodeLevel - 1;
|
|
let sql = "";
|
|
|
|
if (currentLevel === 0) {
|
|
sql = `SELECT * FROM s_module_bmfl WHERE js='1' AND b_canuse='1' ORDER BY b_xh`;
|
|
} else {
|
|
if (!nodeData) {
|
|
resolve([]);
|
|
return;
|
|
}
|
|
|
|
const parentField = `bm_${currentLevel}_b`;
|
|
const parentCondition = ` AND ${parentField}='${nodeData.b_id}'`;
|
|
const childLevel = currentLevel + 1;
|
|
|
|
sql = `
|
|
SELECT s_module_bmfl.*, s_module.b_id AS b_moduleid
|
|
FROM s_module_bmfl
|
|
LEFT JOIN s_module ON s_module.b_cate_id = s_module_bmfl.b_id
|
|
WHERE s_module_bmfl.b_canuse='1'
|
|
AND js='${childLevel}'
|
|
${parentCondition}
|
|
AND s_module_bmfl.b_canuse='1'
|
|
ORDER BY b_xh
|
|
`;
|
|
}
|
|
|
|
const res = await loadDataBySqlApi(sql);
|
|
if (res.success) {
|
|
resolve(res.data);
|
|
} else {
|
|
resolve([]);
|
|
}
|
|
} catch (err) {
|
|
console.error(err);
|
|
resolve([]);
|
|
}
|
|
}
|
|
|
|
async function handleTreeNodeClick(nodeData) {
|
|
if (!nodeData || nodeData.b_id === "root" || !nodeData.b_moduleid) return;
|
|
|
|
currentNode.value = nodeData;
|
|
loading.value = true;
|
|
|
|
try {
|
|
const res = await loadDataApi("s_module", `b_id = '${nodeData.b_moduleid}'`);
|
|
|
|
if (res.success) {
|
|
currentModule.value = res.data[0] || null;
|
|
|
|
if (currentModule.value?.b_ViewTable) {
|
|
const viewTable = currentModule.value?.b_ViewTable;
|
|
await loadDatas(viewTable);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error("加载模块或列失败", err);
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
// 加载数据
|
|
async function loadDatas(viewName) {
|
|
if (!viewName) {
|
|
return;
|
|
}
|
|
|
|
const res = await loadAllColumnsApi(viewName);
|
|
|
|
if (res.success) {
|
|
tableData.value = res.data;
|
|
}
|
|
}
|
|
|
|
// 编辑关闭事件
|
|
async function handleEditClosed({ row, rowIndex, column, columnIndex }) {
|
|
const $table = tableRef.value;
|
|
if ($table) {
|
|
await handleSaveRow(row);
|
|
}
|
|
}
|
|
|
|
// 编辑激活事件
|
|
function handleEditActivated({ row }) {
|
|
const $table = tableRef.value;
|
|
if ($table) {
|
|
oldRowData = JSON.parse(JSON.stringify(row));
|
|
}
|
|
}
|
|
|
|
// 选中行事件
|
|
function handleCurrentRowChange({ newValue }) {
|
|
currentRow.value = newValue;
|
|
}
|
|
|
|
// 保存
|
|
function handleSaveRow(row) {
|
|
if (!row?.b_id) return;
|
|
|
|
const diffRowResult = diffObject(row, oldRowData, ["b_id"], []);
|
|
|
|
if (!diffRowResult.isChanged) {
|
|
return;
|
|
}
|
|
|
|
const saveData = [
|
|
{
|
|
tableName: "s_columnLib",
|
|
keyField: "b_id",
|
|
inserts: [],
|
|
updates: [diffRowResult.result],
|
|
deletes: [],
|
|
log: "0",
|
|
user: "0",
|
|
},
|
|
];
|
|
|
|
saveObjectApi(saveData).then((res) => {
|
|
if (res.success) {
|
|
ElMessage.success("保存成功");
|
|
}
|
|
});
|
|
}
|
|
|
|
// 检查
|
|
function handleCheckColumns() {
|
|
if (!currentModule.value) {
|
|
return;
|
|
}
|
|
|
|
const viewName = currentModule.value.b_ViewTable || "";
|
|
|
|
if (!viewName) {
|
|
return;
|
|
}
|
|
|
|
loading.value = true;
|
|
|
|
checkColumnsApi(viewName)
|
|
.then(async (res) => {
|
|
if (res.success) {
|
|
ElMessage.success("检查成功");
|
|
await loadDatas(viewName);
|
|
}
|
|
})
|
|
.finally(() => {
|
|
loading.value = false;
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="g3-wrapper">
|
|
<el-splitter>
|
|
<el-splitter-panel :size="leftSize" :min="0" :max="260">
|
|
<div class="g3-wrapper-left">
|
|
<div class="tree-header">
|
|
<el-text>模块名</el-text>
|
|
<div class="tree-options"></div>
|
|
</div>
|
|
|
|
<div class="tree-container">
|
|
<el-scrollbar style="flex: 1">
|
|
<el-tree
|
|
lazy
|
|
highlight-current
|
|
node-key="b_id"
|
|
:data="treeData"
|
|
:props="treeProps"
|
|
:load="handleTreeLoad"
|
|
:default-expanded-keys="['root']"
|
|
@node-click="handleTreeNodeClick"
|
|
/>
|
|
</el-scrollbar>
|
|
</div>
|
|
</div>
|
|
</el-splitter-panel>
|
|
<el-splitter-panel style="margin-left: 8px">
|
|
<div class="g3-wrapper-right">
|
|
<div class="content">
|
|
<div class="content-head">
|
|
<div class="content-head-left">
|
|
<el-button type="primary" :disabled="!isSelectedNode" @click="handleCheckColumns"
|
|
>检查</el-button
|
|
>
|
|
<el-button type="primary" :disabled="!isSelectedNode">浏览栏位预览</el-button>
|
|
<el-button type="primary" :disabled="!isSelectedNode">编辑栏位预览</el-button>
|
|
<el-button type="primary" :disabled="!isSelectedNode">属性预览</el-button>
|
|
<el-button type="primary">修改标签</el-button>
|
|
<el-button type="primary" :disabled="!isSelectedNode" @click="showSql = !showSql">
|
|
SQL语句
|
|
</el-button>
|
|
</div>
|
|
|
|
<div class="content-head-right">
|
|
<el-input placeholder="请输入关键词搜索" />
|
|
<el-button type="primary">搜索</el-button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="content-table">
|
|
<vxe-table
|
|
ref="tableRef"
|
|
height="100%"
|
|
:data="tableData"
|
|
:loading="loading"
|
|
:row-config="{
|
|
useKey: true,
|
|
keyField: 'col_FieldName',
|
|
isCurrent: true,
|
|
}"
|
|
:column-config="{
|
|
resizable: true,
|
|
useKey: true,
|
|
}"
|
|
:edit-config="{
|
|
trigger: 'click',
|
|
mode: 'row',
|
|
autoClear: false,
|
|
showStatus: false,
|
|
showIcon: false,
|
|
}"
|
|
:scroll-x="{
|
|
gt: 0,
|
|
enabled: true,
|
|
}"
|
|
:scroll-y="{
|
|
gt: 0,
|
|
enabled: true,
|
|
}"
|
|
show-overflow
|
|
show-header-overflow
|
|
@current-row-change="handleCurrentRowChange"
|
|
@edit-closed="handleEditClosed"
|
|
@edit-activated="handleEditActivated"
|
|
>
|
|
<vxe-column type="seq" width="50" fixed="left" />
|
|
|
|
<vxe-column
|
|
v-for="column in columns"
|
|
:key="column.prop"
|
|
:field="column.prop"
|
|
:title="column.label"
|
|
:width="column.width"
|
|
align="center"
|
|
sortable
|
|
:edit-render="{ autoFocus: true }"
|
|
:fixed="column.fixed"
|
|
>
|
|
<template #default="{ row }">
|
|
<ElCheckbox
|
|
v-if="column.type === 'checkbox'"
|
|
true-value="1"
|
|
false-value="0"
|
|
:checked="row[column.prop] === '1'"
|
|
/>
|
|
<template v-else>
|
|
{{ row[column.prop] }}
|
|
</template>
|
|
</template>
|
|
<template #edit="{ row }">
|
|
<ElCheckbox
|
|
v-if="column.type === 'checkbox'"
|
|
v-model="row[column.prop]"
|
|
true-value="1"
|
|
false-value="0"
|
|
/>
|
|
<el-select
|
|
v-else-if="column.type === 'option'"
|
|
v-model="row[column.prop]"
|
|
size="small"
|
|
placeholder=" "
|
|
>
|
|
<el-option
|
|
v-for="item in getOptions(column.prop)"
|
|
:key="item.value"
|
|
:label="item.label"
|
|
:value="item.value"
|
|
/>
|
|
</el-select>
|
|
<ElInput
|
|
v-else-if="column.type === 'input'"
|
|
v-model="row[column.prop]"
|
|
size="small"
|
|
/>
|
|
<template v-else>
|
|
{{ row[column.prop] }}
|
|
</template>
|
|
</template>
|
|
</vxe-column>
|
|
</vxe-table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</el-splitter-panel>
|
|
</el-splitter>
|
|
|
|
<PreviewSql v-model="showSql" :data="tableData" :viewtable="currentModule?.b_ViewTable" />
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
@use "@/styles/mixins.scss" as *;
|
|
|
|
.g3-wrapper {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
:deep(.el-splitter-bar__dragger-horizontal::after),
|
|
:deep(.el-splitter-bar__dragger-horizontal::before) {
|
|
width: 1px;
|
|
}
|
|
|
|
:deep(.el-splitter-bar__dragger::after),
|
|
:deep(.el-splitter-bar__dragger::before) {
|
|
background-color: #f2f6fc;
|
|
}
|
|
}
|
|
|
|
.g3-wrapper-left {
|
|
height: 100%;
|
|
width: 100%;
|
|
background-color: #fff;
|
|
|
|
.tree-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
height: 40px;
|
|
padding: 0 10px;
|
|
border-bottom: 1px solid #f2f6fc;
|
|
}
|
|
.tree-options {
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
.item {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
list-style: none;
|
|
margin-left: 5px;
|
|
cursor: pointer;
|
|
border-radius: 6px;
|
|
font-size: 16px;
|
|
height: 26px;
|
|
width: 26px;
|
|
color: #333;
|
|
transition: background-color 0.3s ease;
|
|
|
|
&:hover {
|
|
background-color: #f5f7fa;
|
|
}
|
|
}
|
|
}
|
|
|
|
.tree-container {
|
|
height: calc(100% - 40px);
|
|
padding: 10px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
}
|
|
|
|
.g3-wrapper-right {
|
|
flex: 1;
|
|
transition: width 0.3s;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background-color: #fff;
|
|
|
|
.head {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
height: 40px;
|
|
padding: 0 10px;
|
|
border-bottom: 1px solid #f2f6fc;
|
|
flex-shrink: 0;
|
|
.options {
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
.item {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
list-style: none;
|
|
margin-left: 5px;
|
|
border-radius: 6px;
|
|
font-size: 16px;
|
|
height: 26px;
|
|
width: 26px;
|
|
color: #333;
|
|
transition: background-color 0.3s ease;
|
|
}
|
|
|
|
.item-bg {
|
|
cursor: pointer;
|
|
background-color: #f5f7fa;
|
|
}
|
|
}
|
|
}
|
|
|
|
.content {
|
|
padding: 10px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-width: 0;
|
|
height: 100%;
|
|
|
|
.content-head {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
|
|
.content-head-left {
|
|
display: flex;
|
|
@include flexGapWrap(4px, 8px);
|
|
}
|
|
|
|
.content-head-right {
|
|
display: flex;
|
|
@include flexGapWrap(4px, 8px);
|
|
flex-wrap: nowrap;
|
|
min-width: 200px;
|
|
}
|
|
}
|
|
|
|
.content-table {
|
|
margin-top: 8px;
|
|
flex: 1;
|
|
}
|
|
}
|
|
}
|
|
</style>
|