u
This commit is contained in:
1 parent
aa813c9649
commit
62550b8182
45 files changed
+12535
-566
No files matched your search
@@ -0,0 +1,24 @@
|
||||
import G3Detail from "./index.vue";
|
||||
|
||||
export function showDetailDrawer(props) {
|
||||
// 1. 创建容器
|
||||
const container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
|
||||
// 2. 创建 app
|
||||
const app = createApp({
|
||||
render() {
|
||||
return h(G3Detail, {
|
||||
...props,
|
||||
// 关闭时销毁
|
||||
onClose: () => {
|
||||
app.unmount();
|
||||
container.remove();
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// 3. 挂载
|
||||
app.mount(container);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<script lang="js" setup>
|
||||
import { loadDetailColumnsApi, loadObjectApi } from "@/request/api";
|
||||
import { formatValueByFormat } from "@/utils/columns";
|
||||
const loading = ref(false);
|
||||
const visible = ref(true);
|
||||
|
||||
const props = defineProps({
|
||||
viewName: String,
|
||||
keyField: String,
|
||||
keyValue: [String, Number],
|
||||
});
|
||||
|
||||
const dataList = ref([]);
|
||||
|
||||
async function initData() {
|
||||
console.log(props);
|
||||
|
||||
if (!props.viewName) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const columnsRes = await loadDetailColumnsApi(props.viewName);
|
||||
|
||||
if (!columnsRes?.success) return;
|
||||
|
||||
dataList.value = columnsRes.data || [];
|
||||
|
||||
if (props.keyField && props.keyValue) {
|
||||
const objRes = await loadObjectApi({
|
||||
viewName: props.viewName,
|
||||
keyField: props.keyField,
|
||||
keyValue: props.keyValue,
|
||||
});
|
||||
|
||||
if (objRes?.success) {
|
||||
const rowData = objRes.data || {};
|
||||
|
||||
dataList.value.forEach((item) => {
|
||||
item.col_Value = rowData[item.col_FieldName] ?? "";
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("initData error:", e);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function displayValue(item) {
|
||||
if (item.col_Value == null) return "";
|
||||
return formatValueByFormat(item.col_Value, item.col_DisplayFormat);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-drawer v-model="visible" title="详细信息" @opened="initData">
|
||||
<template #default>
|
||||
<G3Grid v-if="dataList.length > 0">
|
||||
<G3GridGroup>
|
||||
<G3GridItem
|
||||
v-for="item in dataList"
|
||||
:key="item.col_FieldName"
|
||||
:label="item.col_Caption"
|
||||
:height="28"
|
||||
:label-width="100"
|
||||
>
|
||||
{{ displayValue(item) }}
|
||||
</G3GridItem>
|
||||
</G3GridGroup>
|
||||
</G3Grid>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -14,6 +14,11 @@ const props = defineProps({
|
||||
labelWidth: {
|
||||
type: [String, Number],
|
||||
},
|
||||
labelAlign: {
|
||||
type: String,
|
||||
default: "right",
|
||||
validator: (v) => ["left", "center", "right"].includes(v),
|
||||
},
|
||||
});
|
||||
|
||||
const gridStyle = computed(() => ({
|
||||
@@ -30,7 +35,11 @@ provide("G3GridGroupConfig", toRefs(props));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="g3-grid-group" :class="{ border: props.border }" :style="gridStyle">
|
||||
<div
|
||||
class="g3-grid-group"
|
||||
:class="{ border: props.border }"
|
||||
:style="gridStyle"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -36,7 +36,8 @@ const props = defineProps({
|
||||
const parentConfig = inject("G3GridGroupConfig", {});
|
||||
|
||||
const mergedBorder = computed(() => {
|
||||
const val = props.border !== undefined ? props.border : parentConfig.border?.value;
|
||||
const val =
|
||||
props.border !== undefined ? props.border : parentConfig.border?.value;
|
||||
|
||||
return val;
|
||||
});
|
||||
@@ -47,7 +48,8 @@ const gridItemStyle = computed(() => {
|
||||
};
|
||||
|
||||
if (props.height !== undefined && props.height !== null) {
|
||||
style.height = typeof props.height === "number" ? props.height + "px" : props.height;
|
||||
style.height =
|
||||
typeof props.height === "number" ? props.height + "px" : props.height;
|
||||
}
|
||||
|
||||
return style;
|
||||
@@ -55,8 +57,15 @@ const gridItemStyle = computed(() => {
|
||||
|
||||
const gridLabelStyle = computed(() => {
|
||||
const val = props.labelWidth ?? parentConfig.labelWidth?.value;
|
||||
const alignVal = props.labelAlign ?? parentConfig.labelAlign?.value ?? "right";
|
||||
|
||||
return {
|
||||
flex: `0 0 ${typeof val === "number" ? val + "px" : val}`,
|
||||
};
|
||||
});
|
||||
|
||||
const gridInnerLabelStyle = computed(() => {
|
||||
const alignVal =
|
||||
props.labelAlign ?? parentConfig.labelAlign?.value ?? "right";
|
||||
const justifyMap = {
|
||||
left: "flex-start",
|
||||
center: "center",
|
||||
@@ -64,33 +73,32 @@ const gridLabelStyle = computed(() => {
|
||||
};
|
||||
|
||||
return {
|
||||
flex: `0 0 ${typeof val === "number" ? val + "px" : val}`,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: justifyMap[alignVal],
|
||||
};
|
||||
});
|
||||
|
||||
const requiredStyle = computed(() => {
|
||||
const alignVal = props.labelAlign ?? parentConfig.labelAlign?.value ?? "right";
|
||||
|
||||
return alignVal === "right" ? { flexDirection: "row-reverse" } : {};
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="g3-grid-item" :class="{ border: mergedBorder }" :style="gridItemStyle">
|
||||
<div
|
||||
class="g3-grid-item"
|
||||
:class="{ border: mergedBorder }"
|
||||
:style="gridItemStyle"
|
||||
>
|
||||
<!-- label -->
|
||||
<div
|
||||
class="label"
|
||||
:class="{ required, 'is-slot': $slots.label }"
|
||||
:style="[gridLabelStyle, requiredStyle]"
|
||||
:class="{ 'is-slot': $slots.label }"
|
||||
:style="[gridLabelStyle]"
|
||||
>
|
||||
<span class="label-inner">
|
||||
<slot name="label">
|
||||
<span v-if="required" class="required-star">*</span>
|
||||
|
||||
<slot name="label">
|
||||
<span class="label-inner" :style="[gridInnerLabelStyle]">
|
||||
{{ label }}
|
||||
</slot>
|
||||
</span>
|
||||
</span>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<!-- content -->
|
||||
@@ -120,12 +128,12 @@ const requiredStyle = computed(() => {
|
||||
display: flex;
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
|
||||
&.required::before {
|
||||
content: "*";
|
||||
.required-star {
|
||||
color: #f56c6c;
|
||||
margin-right: 4px;
|
||||
font-weight: bold;
|
||||
margin-left: 4px;
|
||||
flex-shrink: 0; // 防止被压缩
|
||||
}
|
||||
|
||||
&.is-slot .label-inner {
|
||||
@@ -139,6 +147,8 @@ const requiredStyle = computed(() => {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
padding: 0px 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +185,7 @@ const requiredStyle = computed(() => {
|
||||
|
||||
:deep(.el-select) {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
height: var(--el-component-size) !important;
|
||||
.el-select__wrapper {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<script lang="js" setup>
|
||||
defineOptions({
|
||||
name: "G3RecursiveColumn",
|
||||
});
|
||||
|
||||
const isGroup = computed(() => {
|
||||
return Array.isArray(props.column.children) && props.column.children.length > 0;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<vxe-colgroup v-if="isGroup" :title="column.label" :field="column.field">
|
||||
|
||||
</vxe-colgroup>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,53 @@
|
||||
<script lang="js" setup>
|
||||
import { getAlignByFormat, formatValueByFormat } from "@/utils/columns";
|
||||
|
||||
defineOptions({
|
||||
name: "G3RecursiveColumn",
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
column: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const isGroup = computed(() => {
|
||||
return (
|
||||
Array.isArray(props.column.children) && props.column.children.length > 0
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<vxe-colgroup v-if="isGroup" :title="column.label" :field="column.field">
|
||||
<G3RecursiveColumn
|
||||
v-for="(child, index) in column.children"
|
||||
:key="index"
|
||||
:column="child"
|
||||
></G3RecursiveColumn>
|
||||
</vxe-colgroup>
|
||||
|
||||
<vxe-column
|
||||
v-else
|
||||
:field="column.col_FieldName"
|
||||
:title="column.col_Caption"
|
||||
:width="column.col_Width"
|
||||
:align="getAlignByFormat(column.col_DisplayFormat)"
|
||||
:params="column"
|
||||
:sortable="props.sortable"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{
|
||||
formatValueByFormat(row[column.col_FieldName], column.col_DisplayFormat)
|
||||
}}
|
||||
</template>
|
||||
<template v-if="column.showSummary === '1'" #footer="{ row }">
|
||||
{{
|
||||
formatValueByFormat(row[column.col_FieldName], column.col_DisplayFormat)
|
||||
}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -9,11 +9,13 @@ const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
onMounted(() => {
|
||||
loadDataApi("s_keyvalue", `b_key='bodycaption'`, ``, ["b_value"]).then((res) => {
|
||||
if (res.success) {
|
||||
title.value = res.data[0].b_value;
|
||||
}
|
||||
});
|
||||
loadDataApi("s_keyvalue", `b_key='bodycaption'`, ``, ["b_value"]).then(
|
||||
(res) => {
|
||||
if (res.success) {
|
||||
title.value = res.data[0].b_value;
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const menuList = ref([
|
||||
@@ -40,6 +42,19 @@ const menuList = ref([
|
||||
label: "系统管理",
|
||||
icon: "lucide:settings",
|
||||
children: [
|
||||
{
|
||||
label: "办公管理",
|
||||
children: [
|
||||
{
|
||||
label: "企业公告",
|
||||
path: "/system/oa/notice",
|
||||
},
|
||||
{
|
||||
label: "用户提醒",
|
||||
path: "/system/oa/reminder",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "字段配置",
|
||||
path: "/system/field",
|
||||
@@ -48,6 +63,10 @@ const menuList = ref([
|
||||
label: "权限配置",
|
||||
path: "/system/permission",
|
||||
},
|
||||
{
|
||||
label: "模块配置",
|
||||
path: "/system/module",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
@@ -154,13 +173,14 @@ function handleMenuChange(val) {
|
||||
border-radius: 6px;
|
||||
}
|
||||
.el-menu-item {
|
||||
padding-left: 48px !important;
|
||||
// padding-left: 48px !important;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
.el-sub-menu__title {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
// .el-sub-menu__title {
|
||||
// color: var(--el-color-primary);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useUserStore } from "@/store/user";
|
||||
import { useTabStore } from "@/store/tab";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
import UpdatePassword from "./update-password.vue";
|
||||
|
||||
const props = defineProps({
|
||||
collapse: {
|
||||
@@ -18,6 +19,8 @@ const router = useRouter();
|
||||
const tabStore = useTabStore();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const showUpdatePassword = ref(false);
|
||||
|
||||
function onCollapse() {
|
||||
emits("collapse");
|
||||
}
|
||||
@@ -37,7 +40,11 @@ function logout() {
|
||||
<div class="g3-topbar-left">
|
||||
<div class="g3-action-icon" @click="onCollapse()">
|
||||
<G3Icon
|
||||
:icon="props.collapse ? 'lucide:list-indent-increase' : 'lucide:list-indent-decrease'"
|
||||
:icon="
|
||||
props.collapse
|
||||
? 'lucide:list-indent-increase'
|
||||
: 'lucide:list-indent-decrease'
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -48,19 +55,29 @@ function logout() {
|
||||
<G3Icon icon="lucide:circle-user-round" size="18" />
|
||||
<div class="user-info">
|
||||
<span class="user-name">
|
||||
{{ userStore.userInfo.username || userStore.userInfo.userid || "空" }}
|
||||
{{
|
||||
userStore.userInfo.username || userStore.userInfo.userid || "空"
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item> 个人信息 </el-dropdown-item>
|
||||
<el-dropdown-item> 修改密码 </el-dropdown-item>
|
||||
<el-dropdown-item divided @click="logout"> 退出登录 </el-dropdown-item>
|
||||
<el-dropdown-item @click="router.push('/user-center')"
|
||||
>个人中心</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click="showUpdatePassword = !showUpdatePassword">
|
||||
修改密码
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided @click="logout">
|
||||
退出登录
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
|
||||
<UpdatePassword v-model="showUpdatePassword" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<script lang="js" setup>
|
||||
import { updatePasswordApi } from "@/request/api";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const confirmLoading = ref(false);
|
||||
const showDialog = defineModel(false);
|
||||
|
||||
const form = ref({
|
||||
oldPassword: "",
|
||||
newPassword: "",
|
||||
confirmPassword: "",
|
||||
});
|
||||
|
||||
function handleConfirm() {
|
||||
if (form.value.newPassword === "") {
|
||||
ElMessage.warning("新密码不能为空");
|
||||
return;
|
||||
}
|
||||
if (form.value.confirmPassword !== form.value.newPassword) {
|
||||
ElMessage.warning("新密码和确认密码不一致");
|
||||
return;
|
||||
}
|
||||
|
||||
confirmLoading.value = true;
|
||||
|
||||
updatePasswordApi({
|
||||
newPassword: form.value.newPassword,
|
||||
oldPassword: form.value.oldPassword,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
// 修改成功
|
||||
ElMessage.success("修改成功");
|
||||
showDialog.value = false;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
confirmLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
form.value.newPassword = "";
|
||||
form.value.oldPassword = "";
|
||||
form.value.confirmPassword = "";
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="showDialog"
|
||||
@closed="resetForm"
|
||||
title="修改密码"
|
||||
width="400"
|
||||
>
|
||||
<G3Grid>
|
||||
<G3GridGroup :label-width="80" :column="1" label-align="right">
|
||||
<G3GridItem label="旧密码" :span="1">
|
||||
<el-input v-model="form.oldPassword" type="password" show-password />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="新密码" :span="1" required>
|
||||
<el-input v-model="form.newPassword" type="password" show-password />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="确认密码" :span="1" required>
|
||||
<el-input
|
||||
v-model="form.confirmPassword"
|
||||
type="password"
|
||||
show-password
|
||||
/>
|
||||
</G3GridItem>
|
||||
</G3GridGroup>
|
||||
</G3Grid>
|
||||
|
||||
<div class="footer">
|
||||
<el-button @click="showDialog = false">取消</el-button>
|
||||
<el-button type="primary" :loading="confirmLoading" @click="handleConfirm"
|
||||
>确认</el-button
|
||||
>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.footer {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
@@ -9,6 +9,13 @@ export function loginApi(orgid, userid, password) {
|
||||
});
|
||||
}
|
||||
|
||||
export function updatePasswordApi({ oldPassword, newPassword }) {
|
||||
return httpPost(`/auth/updatePassword`, {
|
||||
oldPassword,
|
||||
newPassword,
|
||||
});
|
||||
}
|
||||
|
||||
// ============================= data =============================
|
||||
export function loadDataApi(viewName, searchCondition = "", orderField = "", searchColumns = []) {
|
||||
return httpPost("/data/loadData", {
|
||||
@@ -25,6 +32,22 @@ export function loadDataBySqlApi(sql) {
|
||||
});
|
||||
}
|
||||
|
||||
export function loadObjectApi({
|
||||
viewName,
|
||||
keyField,
|
||||
keyValue,
|
||||
searchCondition = "",
|
||||
orderField = "",
|
||||
}) {
|
||||
return httpPost("/data/loadObject", {
|
||||
viewName,
|
||||
keyField,
|
||||
keyValue,
|
||||
searchCondition,
|
||||
orderField,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页加载表格数据
|
||||
* @param {Object} params 参数对象
|
||||
@@ -71,6 +94,13 @@ export function saveObjectApi(data) {
|
||||
export function saveObjectKeysApi(data) {
|
||||
return httpPost("/data/saveObjectKeys", data);
|
||||
}
|
||||
|
||||
export function deleteModuleApi({ moduleId, Id }) {
|
||||
return httpPost("/data/deleteModule", {
|
||||
moduleId,
|
||||
Id,
|
||||
});
|
||||
}
|
||||
// ============================= data =============================
|
||||
|
||||
// ============================= column =============================
|
||||
@@ -92,9 +122,19 @@ export function loadAllColumnsApi(viewName) {
|
||||
});
|
||||
}
|
||||
|
||||
export function loadDetailColumnsApi(viewName) {
|
||||
return httpPost("/column/detail", {
|
||||
viewName,
|
||||
});
|
||||
}
|
||||
|
||||
export function checkColumnsApi(viewName) {
|
||||
return httpPost("/column/check", {
|
||||
viewName,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateColumnsApi(columns) {
|
||||
return httpPost("/column/updateColumns", columns);
|
||||
}
|
||||
// ============================= column =============================
|
||||
@@ -31,7 +31,7 @@ export const routes = [
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
path: "/system/permission",
|
||||
component: () => import("@/views/system/permission/index.vue"),
|
||||
meta: {
|
||||
@@ -39,6 +39,39 @@ export const routes = [
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/system/module",
|
||||
component: () => import("@/views/system/module/index.vue"),
|
||||
meta: {
|
||||
title: "模块设置",
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/system/oa/notice",
|
||||
component: () => import("@/views/system/oa/notice/index.vue"),
|
||||
meta: {
|
||||
title: "企业公告",
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/system/oa/reminder",
|
||||
component: () => import("@/views/system/oa/reminder/index.vue"),
|
||||
meta: {
|
||||
title: "用户提醒",
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
// ==================== 个人中心 ====================
|
||||
{
|
||||
path: "/user-center",
|
||||
component: () => import("@/views/user-center/index.vue"),
|
||||
meta: {
|
||||
title: "个人中心",
|
||||
keepAlive: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
import { updateColumnsApi } from "@/request/api";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
export function updateEditTableColumns(tableRef) {
|
||||
const $table = unref(tableRef);
|
||||
|
||||
if (!$table || typeof $table.getColumns !== "function") {
|
||||
ElMessage.warning("表格尚未初始化完成");
|
||||
return;
|
||||
}
|
||||
|
||||
const columns = $table.getColumns();
|
||||
if (!$table || $table.length === 0) {
|
||||
ElMessage.warning("未获取到栏位信息");
|
||||
return;
|
||||
}
|
||||
|
||||
const newColumns = columns
|
||||
.filter((item) => item.params.b_id !== undefined)
|
||||
.map((item, index) => ({
|
||||
b_id: item.params.b_id,
|
||||
col_edit_width: item.renderWidth,
|
||||
col_edit_position: index,
|
||||
}));
|
||||
|
||||
updateColumnsApi(newColumns).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("保存成功");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function updateViewTableColumns(tableRef) {
|
||||
const $table = unref(tableRef);
|
||||
|
||||
if (!$table || typeof $table.getColumns !== "function") {
|
||||
ElMessage.warning("表格尚未初始化完成");
|
||||
return;
|
||||
}
|
||||
|
||||
const columns = $table.getColumns();
|
||||
if (!$table || $table.length === 0) {
|
||||
ElMessage.warning("未获取到栏位信息");
|
||||
return;
|
||||
}
|
||||
|
||||
const newColumns = columns
|
||||
.filter((item) => item.params.b_id !== undefined)
|
||||
.map((item, index) => ({
|
||||
b_id: item.params.b_id,
|
||||
col_Width: item.renderWidth,
|
||||
col_Position: index,
|
||||
}));
|
||||
|
||||
updateColumnsApi(newColumns).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("保存成功");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 format 判断列对齐方式
|
||||
* @param {string} format
|
||||
*/
|
||||
export function getAlignByFormat(format) {
|
||||
if (!format || typeof format !== "string") return "left";
|
||||
|
||||
const fmt = format.toLowerCase();
|
||||
if (fmt.startsWith("money") || fmt.startsWith("number")) {
|
||||
return "right";
|
||||
}
|
||||
|
||||
if (fmt === "checkbox") {
|
||||
return "center";
|
||||
}
|
||||
|
||||
return "left";
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 format 格式化值
|
||||
* @param {any} value 原始值
|
||||
* @param {string} format 格式描述,如 money(2)、percent(1)
|
||||
*/
|
||||
export function formatValueByFormat(value, format) {
|
||||
if (value === null || value === undefined || value === "") return "";
|
||||
|
||||
if (!format || typeof format !== "string") return value;
|
||||
|
||||
const fmt = format.trim().toLowerCase();
|
||||
|
||||
// money(n)
|
||||
if (fmt.startsWith("money")) {
|
||||
const match = fmt.match(/^money\((\d+)\)$/);
|
||||
const decimals = match ? Number(match[1]) : 2;
|
||||
|
||||
const num = Number.parseFloat(value);
|
||||
if (Number.isNaN(num)) return value;
|
||||
|
||||
// 使用Intl.NumberFormat来处理千分位
|
||||
return new Intl.NumberFormat("en-US", {
|
||||
minimumFractionDigits: decimals,
|
||||
maximumFractionDigits: decimals,
|
||||
}).format(num);
|
||||
} else if (fmt.startsWith("date")) {
|
||||
return formatToDate(value);
|
||||
} else if (fmt.startsWith("number")) {
|
||||
return Number(value) || 0;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export function formatToDate(date) {
|
||||
const validDate = dayjs(date);
|
||||
if (!validDate.isValid()) {
|
||||
return date;
|
||||
}
|
||||
return validDate.format("YYYY-MM-DD");
|
||||
}
|
||||
|
||||
export function formatToDateTime(date) {
|
||||
const validDate = dayjs(date);
|
||||
if (!validDate.isValid()) {
|
||||
return date;
|
||||
}
|
||||
return validDate.format("YYYY-MM-DD HH:mm:ss");
|
||||
}
|
||||
|
||||
export function getDateTime() {
|
||||
return dayjs().format("YYYY-MM-DD HH:mm:ss");
|
||||
}
|
||||
@@ -7,9 +7,14 @@ import {
|
||||
saveObjectApi,
|
||||
} from "@/request/api";
|
||||
import { ElMessage } from "element-plus";
|
||||
import PreviewSql from "./preview-sql.vue";
|
||||
import { diffObject } from "@/utils/diff";
|
||||
|
||||
import PreviewSql from "./preview-sql.vue";
|
||||
import PreviewEdit from "./preview-edit.vue";
|
||||
import PreviewView from "./preview-view.vue";
|
||||
import TagUpdate from "./tag-update.vue";
|
||||
import { showDetailDrawer } from "@/components/g3-detail";
|
||||
|
||||
const tableRef = ref();
|
||||
|
||||
const loading = ref(false);
|
||||
@@ -19,7 +24,9 @@ const isCollapse = ref(false);
|
||||
|
||||
const showSql = ref(false);
|
||||
|
||||
const treeData = [{ b_id: "root", mc_b: "全部分类", isLeaf: false, children: [] }];
|
||||
const treeData = [
|
||||
{ b_id: "root", mc_b: "全部分类", isLeaf: false, children: [] },
|
||||
];
|
||||
|
||||
const treeProps = {
|
||||
children: "children",
|
||||
@@ -35,122 +42,152 @@ const currentRow = ref({});
|
||||
const isSelectedRow = computed(() => !!currentRow.value?.col_FieldName);
|
||||
let oldRowData = {};
|
||||
|
||||
const showPreview = ref(false);
|
||||
const showEditPreview = ref(false);
|
||||
const showTag = ref(false);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
prop: "col_field_name",
|
||||
prop: "col_FieldName",
|
||||
label: "字段",
|
||||
width: 140,
|
||||
fixed: "left",
|
||||
},
|
||||
{
|
||||
prop: "col_caption",
|
||||
prop: "col_Caption",
|
||||
label: "标题",
|
||||
width: 100,
|
||||
fixed: "left",
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_field_name_v",
|
||||
prop: "col_FieldName_v",
|
||||
label: "视图字段",
|
||||
width: 100,
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_group_index",
|
||||
prop: "col_BandIndex",
|
||||
label: "分组",
|
||||
width: 80,
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_visible",
|
||||
prop: "col_Visible",
|
||||
label: "显示",
|
||||
width: 80,
|
||||
type: "checkbox",
|
||||
},
|
||||
|
||||
{
|
||||
prop: "col_allow_readonly",
|
||||
prop: "col_Color",
|
||||
label: "不创建",
|
||||
width: 80,
|
||||
type: "checkbox",
|
||||
},
|
||||
{
|
||||
prop: "col_ReadOnly",
|
||||
label: "只读",
|
||||
width: 80,
|
||||
type: "checkbox",
|
||||
},
|
||||
{
|
||||
prop: "col_allow_null",
|
||||
prop: "col_AllowNull",
|
||||
label: "可不输",
|
||||
width: 80,
|
||||
type: "checkbox",
|
||||
},
|
||||
{
|
||||
prop: "col_allow_repeat",
|
||||
prop: "col_AllowRepeat",
|
||||
label: "可重复",
|
||||
width: 80,
|
||||
type: "checkbox",
|
||||
},
|
||||
{
|
||||
prop: "col_allow_reserve",
|
||||
prop: "col_NeedReserve",
|
||||
label: "可保留",
|
||||
width: 80,
|
||||
type: "checkbox",
|
||||
},
|
||||
{
|
||||
prop: "col_search",
|
||||
prop: "col_CanSearch",
|
||||
label: "可查找",
|
||||
width: 80,
|
||||
type: "checkbox",
|
||||
},
|
||||
{
|
||||
prop: "col_search_order",
|
||||
prop: "col_SearchOrder",
|
||||
label: "查找顺序",
|
||||
width: 100,
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_search_group_index",
|
||||
label: "查找分组",
|
||||
prop: "col_type",
|
||||
label: "查找类型",
|
||||
width: 100,
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_type",
|
||||
label: "字段类型",
|
||||
width: 100,
|
||||
type: "option",
|
||||
},
|
||||
{
|
||||
prop: "col_default_value",
|
||||
prop: "col_DefaultValue",
|
||||
label: "默认值",
|
||||
width: 80,
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_width",
|
||||
prop: "col_Width",
|
||||
label: "宽度",
|
||||
width: 80,
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_position",
|
||||
prop: "col_Position",
|
||||
label: "位置",
|
||||
width: 80,
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_show_summary",
|
||||
prop: "col_ShowSummary",
|
||||
label: "合计",
|
||||
width: 80,
|
||||
type: "checkbox",
|
||||
},
|
||||
{
|
||||
prop: "col_display_format",
|
||||
prop: "col_foot",
|
||||
label: "合计内容",
|
||||
width: 100,
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_DisplayFormat",
|
||||
label: "显示格式",
|
||||
width: 100,
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_SelectQuery",
|
||||
label: "下拉查询",
|
||||
width: 120,
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_SelectQueryDefault",
|
||||
label: "下拉查询默认",
|
||||
width: 80,
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_edit",
|
||||
label: "编辑",
|
||||
width: 80,
|
||||
type: "checkbox",
|
||||
},
|
||||
|
||||
{
|
||||
prop: "col_edit_type",
|
||||
label: "编辑格式",
|
||||
width: 100,
|
||||
type: "option",
|
||||
},
|
||||
{
|
||||
prop: "col_edit_position",
|
||||
label: "编辑位置",
|
||||
@@ -170,8 +207,14 @@ const columns = [
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_edit_source_data",
|
||||
label: "编辑数据",
|
||||
prop: "col_edit_total_column",
|
||||
label: "几列",
|
||||
width: 70,
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_edit_source_table",
|
||||
label: "编辑下拉表",
|
||||
width: 160,
|
||||
type: "input",
|
||||
},
|
||||
@@ -182,7 +225,7 @@ const columns = [
|
||||
type: "input",
|
||||
},
|
||||
{
|
||||
prop: "col_edit_group_index",
|
||||
prop: "col_edit_grouping",
|
||||
label: "编辑分组",
|
||||
width: 100,
|
||||
type: "input",
|
||||
@@ -194,7 +237,7 @@ const columns = [
|
||||
type: "checkbox",
|
||||
},
|
||||
{
|
||||
prop: "col_show_property",
|
||||
prop: "col_ShowDetail",
|
||||
label: "属性",
|
||||
width: 80,
|
||||
type: "checkbox",
|
||||
@@ -246,16 +289,6 @@ function getOptions(field) {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -317,7 +350,10 @@ async function handleTreeNodeClick(nodeData) {
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const res = await loadDataApi("s_module", `b_id = '${nodeData.b_moduleid}'`);
|
||||
const res = await loadDataApi(
|
||||
"s_module",
|
||||
`b_id = '${nodeData.b_moduleid}'`,
|
||||
);
|
||||
|
||||
if (res.success) {
|
||||
currentModule.value = res.data[0] || null;
|
||||
@@ -455,14 +491,49 @@ function handleCheckColumns() {
|
||||
<div class="content">
|
||||
<div class="content-head">
|
||||
<div class="content-head-left">
|
||||
<el-button type="primary" :disabled="!isSelectedNode" @click="handleCheckColumns"
|
||||
<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">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!isSelectedNode"
|
||||
@click="showPreview = true"
|
||||
>
|
||||
浏览栏位预览
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!isSelectedNode"
|
||||
@click="showEditPreview = true"
|
||||
>
|
||||
编辑栏位预览
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!isSelectedNode"
|
||||
@click="
|
||||
showDetailDrawer({
|
||||
viewName: currentModule?.b_ViewTable,
|
||||
})
|
||||
"
|
||||
>
|
||||
属性预览
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!isSelectedRow"
|
||||
@click="showTag = !showTag"
|
||||
>
|
||||
修改标签
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!isSelectedNode"
|
||||
@click="showSql = !showSql"
|
||||
>
|
||||
SQL语句
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -570,7 +641,23 @@ function handleCheckColumns() {
|
||||
</el-splitter-panel>
|
||||
</el-splitter>
|
||||
|
||||
<PreviewSql v-model="showSql" :data="tableData" :viewtable="currentModule?.b_ViewTable" />
|
||||
<PreviewSql
|
||||
v-model="showSql"
|
||||
:data="tableData"
|
||||
:view-name="currentModule?.b_ViewTable"
|
||||
/>
|
||||
|
||||
<PreviewEdit
|
||||
v-model="showEditPreview"
|
||||
:view-name="currentModule?.b_ViewTable"
|
||||
/>
|
||||
|
||||
<PreviewView
|
||||
v-model="showPreview"
|
||||
:view-name="currentModule?.b_ViewTable"
|
||||
/>
|
||||
|
||||
<TagUpdate v-model="showTag" :data="currentRow" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -699,7 +786,7 @@ function handleCheckColumns() {
|
||||
|
||||
.content-head-right {
|
||||
display: flex;
|
||||
@include flexGapWrap(4px, 0px);
|
||||
@include flexGapWrap(4px, 8px);
|
||||
flex-wrap: nowrap;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<script lang="js" setup>
|
||||
import { loadEditColumnsApi } from "@/request/api";
|
||||
import { updateEditTableColumns } from "@/utils/columns";
|
||||
const props = defineProps({
|
||||
viewName: {
|
||||
type: String,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(["change"]);
|
||||
|
||||
const tableRef = ref(null);
|
||||
|
||||
const loading = ref(false);
|
||||
const columns = ref([]);
|
||||
|
||||
async function initData() {
|
||||
loading.value = true;
|
||||
|
||||
const result = await loadEditColumnsApi(props.viewName);
|
||||
|
||||
if (result.success) {
|
||||
columns.value = result.data;
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
title="预览栏位排序效果"
|
||||
width="1200px"
|
||||
class="dialog"
|
||||
@opened="initData"
|
||||
>
|
||||
<div class="g3-dialog">
|
||||
<div class="g3-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="updateEditTableColumns(tableRef)"
|
||||
>
|
||||
栏位保存
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="g3-table">
|
||||
<vxe-table
|
||||
ref="tableRef"
|
||||
border
|
||||
header-align="center"
|
||||
:loading="loading"
|
||||
:column-drag-config="{
|
||||
showIcon: false,
|
||||
trigger: 'cell',
|
||||
}"
|
||||
:row-config="{
|
||||
useKey: true,
|
||||
isCurrent: true,
|
||||
isHover: true,
|
||||
}"
|
||||
:column-config="{
|
||||
resizable: true,
|
||||
drag: true,
|
||||
}"
|
||||
show-header-overflow
|
||||
height="100%"
|
||||
>
|
||||
<vxe-column
|
||||
v-for="column in columns"
|
||||
:key="column.b_id"
|
||||
:field="column.col_FieldName"
|
||||
:title="column.col_Caption"
|
||||
:width="column.col_edit_width"
|
||||
:params="column"
|
||||
align="center"
|
||||
/>
|
||||
</vxe-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.g3-dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.g3-table {
|
||||
flex: 1;
|
||||
margin-top: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -12,7 +12,7 @@ const props = defineProps({
|
||||
data: {
|
||||
type: null,
|
||||
},
|
||||
viewtable: {
|
||||
viewName: {
|
||||
type: null,
|
||||
},
|
||||
});
|
||||
@@ -20,25 +20,25 @@ const props = defineProps({
|
||||
const sqlStr = ref("");
|
||||
|
||||
function initData() {
|
||||
if (!props.viewtable) return;
|
||||
if (!props.viewName) return;
|
||||
|
||||
const vrows = props.data || [];
|
||||
|
||||
const sqlArr = [];
|
||||
|
||||
// 删除语句
|
||||
const deleteSql = `DELETE FROM s_columnlib WHERE col_TableName='${props.viewtable}';`;
|
||||
const deleteSql = `DELETE FROM s_columnLib WHERE col_TableName='${props.viewName}';`;
|
||||
sqlArr.push(deleteSql);
|
||||
|
||||
// 插入语句
|
||||
vrows.forEach((row) => {
|
||||
const insertSql =
|
||||
"INSERT INTO s_columnlib(col_TableName,col_FieldName,col_Caption,col_Width,col_Visible,col_Position," +
|
||||
"INSERT INTO s_columnLib(col_TableName,col_FieldName,col_Caption,col_Width,col_Visible,col_Position," +
|
||||
"col_ShowSummary,col_BandIndex,col_AllowNull,col_AllowRepeat,col_DefaultValue,col_ReadOnly,col_Color," +
|
||||
"col_CanSearch,col_SearchOrder,col_NeedReserve,col_sorted,col_DisplayFormat,col_type,col_datatype,col_foot)";
|
||||
|
||||
const valueSql =
|
||||
`VALUES('${props.viewtable}', '${row.col_FieldName}', '${row.col_Caption}', '${row.col_Width}', '${row.col_Visible}', '${row.col_Position}', ` +
|
||||
`VALUES('${props.viewName}', '${row.col_FieldName}', '${row.col_Caption}', '${row.col_Width}', '${row.col_Visible}', '${row.col_Position}', ` +
|
||||
`'${row.col_ShowSummary}', '${row.col_BandIndex}', '${row.col_AllowNull}', '${row.col_AllowRepeat}', '${row.col_DefaultValue}', '${row.col_ReadOnly}', '${row.col_Color}', ` +
|
||||
`'${row.col_CanSearch}', '${row.col_SearchOrder}', '${row.col_NeedReserve}', '${row.col_sorted}', '${row.col_DisplayFormat}', '${row.col_type}', '${row.col_datatype}', '${row.col_foot}');`;
|
||||
|
||||
|
||||
@@ -1,7 +1,131 @@
|
||||
<script lang="js" setup></script>
|
||||
<script lang="js" setup>
|
||||
import G3RecursiveColumn from "@/components/g3-table/g3-recursive-column.vue";
|
||||
import { loadViewColumnsApi } from "@/request/api";
|
||||
import { updateViewTableColumns } from "@/utils/columns";
|
||||
|
||||
const props = defineProps({
|
||||
viewName: {
|
||||
type: String,
|
||||
},
|
||||
});
|
||||
|
||||
const tableRef = ref(null);
|
||||
|
||||
const loading = ref(false);
|
||||
const columns = ref([]);
|
||||
|
||||
async function initData() {
|
||||
loading.value = true;
|
||||
|
||||
const result = await loadViewColumnsApi(props.viewName);
|
||||
|
||||
if (result.success) {
|
||||
const datas = result.data;
|
||||
|
||||
columns.value = [];
|
||||
|
||||
datas.forEach((item) => {
|
||||
if (item.col_BandIndex === 0) {
|
||||
columns.value.push(item);
|
||||
} else {
|
||||
let parent = columns.value.find(
|
||||
(col) => col.label === item.col_BandIndex,
|
||||
);
|
||||
|
||||
if (!parent) {
|
||||
parent = {
|
||||
label: item.col_BandIndex,
|
||||
children: [],
|
||||
};
|
||||
columns.value.push(parent);
|
||||
}
|
||||
|
||||
parent.children.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onClosed() {
|
||||
loading.value = true;
|
||||
|
||||
columns.value = [];
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class=""></div>
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
title="预览栏位排序效果"
|
||||
width="1200px"
|
||||
class="dialog"
|
||||
@opened="initData"
|
||||
@closed="onClosed"
|
||||
>
|
||||
<div class="g3-dialog">
|
||||
<div class="g3-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="updateViewTableColumns(tableRef)"
|
||||
>
|
||||
栏位保存
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="g3-table">
|
||||
<vxe-table
|
||||
ref="tableRef"
|
||||
border
|
||||
stripe
|
||||
header-align="center"
|
||||
:loading="loading"
|
||||
:filter-config="{
|
||||
showIcon: false,
|
||||
}"
|
||||
:column-drag-config="{
|
||||
showIcon: false,
|
||||
trigger: 'cell',
|
||||
}"
|
||||
:row-config="{
|
||||
useKey: true,
|
||||
isCurrent: true,
|
||||
isHover: true,
|
||||
}"
|
||||
size="small"
|
||||
:cell-config="{
|
||||
height: 26,
|
||||
}"
|
||||
:column-config="{
|
||||
resizable: true,
|
||||
drag: true,
|
||||
}"
|
||||
show-overflow
|
||||
show-header-overflow
|
||||
height="100%"
|
||||
>
|
||||
<g3-recursive-column
|
||||
v-for="(column, index) in columns"
|
||||
:key="index"
|
||||
:column="column"
|
||||
:sortable="false"
|
||||
/>
|
||||
</vxe-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
.g3-dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.g3-table {
|
||||
flex: 1;
|
||||
margin-top: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,140 @@
|
||||
<script lang="js" setup>
|
||||
import { loadDataApi, saveObjectApi, saveObjectKeysApi } from "@/request/api";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: null,
|
||||
},
|
||||
});
|
||||
|
||||
const tableRef = ref();
|
||||
|
||||
const visible = defineModel(false);
|
||||
|
||||
const loading = reactive({
|
||||
table: true,
|
||||
save: false,
|
||||
});
|
||||
|
||||
const tableData = ref([]);
|
||||
|
||||
async function initData() {
|
||||
const fieldName = props.data?.col_FieldName;
|
||||
console.log(props.data);
|
||||
|
||||
if (!fieldName) return;
|
||||
|
||||
loading.table = true;
|
||||
|
||||
const response = await loadDataApi(
|
||||
"s_columnLib",
|
||||
`col_FieldName = '${fieldName}'`,
|
||||
);
|
||||
|
||||
if (response.success) {
|
||||
tableData.value = (response.data || []).filter(
|
||||
(item) => item.col_TableName !== props.data.col_TableName,
|
||||
);
|
||||
loading.table = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onClosed() {
|
||||
tableData.value = [];
|
||||
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
const selectedRows = tableRef.value.getCheckboxRecords();
|
||||
|
||||
loading.save = true;
|
||||
|
||||
const caption = props.data?.col_Caption || "";
|
||||
// 构建更新数据
|
||||
const updates = selectedRows.map((row) => ({
|
||||
b_id: row.b_id,
|
||||
col_Caption: caption,
|
||||
}));
|
||||
|
||||
const requestData = [
|
||||
{
|
||||
tableName: "s_columnLib",
|
||||
keyField: "b_id",
|
||||
inserts: [],
|
||||
updates,
|
||||
deletes: [],
|
||||
log: "0",
|
||||
user: "0",
|
||||
},
|
||||
];
|
||||
|
||||
const response = await saveObjectApi(requestData);
|
||||
|
||||
if (response.success) {
|
||||
ElMessage.success("保存成功");
|
||||
onClosed();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
v-model="visible"
|
||||
title="修改栏位标签"
|
||||
width="1000px"
|
||||
class="dialog"
|
||||
@opened="initData"
|
||||
@closed="onClosed"
|
||||
>
|
||||
<vxe-table
|
||||
ref="tableRef"
|
||||
height="400"
|
||||
:data="tableData"
|
||||
:loading="loading.table"
|
||||
:row-config="{
|
||||
useKey: true,
|
||||
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
|
||||
>
|
||||
<vxe-column type="checkbox" width="50" align="center" fixed="left" />
|
||||
<vxe-column field="col_TableName" title="视图" align="center" />
|
||||
<vxe-column field="col_FieldName" title="字段" align="center" />
|
||||
<vxe-column field="col_Caption" title="标题" align="center" />
|
||||
<vxe-column field="col_Visible" title="显示" align="center" />
|
||||
<vxe-column field="col_Width" title="宽度" align="center" />
|
||||
</vxe-table>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="visible = false"> 取消 </el-button>
|
||||
<el-button type="primary" :loading="loading.save" @click="handleSave">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,177 @@
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
visible: Boolean,
|
||||
mode: { type: String, default: "add" }, // add | edit
|
||||
node: { type: Object, default: () => ({}) }, // 当前节点数据(edit时使用)
|
||||
parent: { type: Object, default: () => ({}) },
|
||||
});
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits(["update:visible", "submit"]);
|
||||
|
||||
// Local state
|
||||
const form = ref({
|
||||
p_id: "",
|
||||
p_name: "",
|
||||
bm_b: "",
|
||||
mc_b: "",
|
||||
b_xh: "",
|
||||
b_canuse: "",
|
||||
b_canmenu: "",
|
||||
});
|
||||
const visible = ref(props.visible);
|
||||
const isDisabled = ref(false); // 控制是否禁用
|
||||
|
||||
// 同步外部 v-model
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => (visible.value = val),
|
||||
);
|
||||
watch(visible, (val) => emit("update:visible", val));
|
||||
|
||||
//编辑时回填
|
||||
watch(
|
||||
() => props.node,
|
||||
(val) => {
|
||||
if (val) {
|
||||
if (props.mode == "add") {
|
||||
isDisabled.value = false;
|
||||
if (val.id != "all") {
|
||||
form.value.p_id = val.id;
|
||||
form.value.p_name = val.label;
|
||||
} else {
|
||||
form.value.p_id = "";
|
||||
form.value.p_name = "";
|
||||
}
|
||||
}
|
||||
if (props.mode == "edit") {
|
||||
isDisabled.value = true;
|
||||
if (val.id != "all") {
|
||||
if (props.parent.id != "all") {
|
||||
form.value.p_id = props.parent.id;
|
||||
form.value.p_name = props.parent.label;
|
||||
} else {
|
||||
form.value.p_id = "";
|
||||
form.value.p_name = "";
|
||||
}
|
||||
form.value.bm_b = val.id;
|
||||
form.value.mc_b = val.label;
|
||||
form.value.b_xh = val.b_xh;
|
||||
form.value.b_canuse = Number(val.b_canuse);
|
||||
form.value.b_canmenu = Number(val.b_canmenu);
|
||||
} else {
|
||||
form.value.p_id = "";
|
||||
form.value.p_name = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// 提交
|
||||
const handleSubmit = () => {
|
||||
if (!form.value.bm_b || !form.value.mc_b) {
|
||||
ElMessage.warning("本级编码、名称不能为空");
|
||||
return;
|
||||
}
|
||||
emit("submit", { ...form.value });
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
form.value.p_id = "";
|
||||
form.value.p_name = "";
|
||||
form.value.bm_b = "";
|
||||
form.value.mc_b = "";
|
||||
form.value.b_xh = "";
|
||||
form.value.b_canuse = "";
|
||||
form.value.b_canmenu = "";
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="mode === 'add' ? '新增分类' : '修改分类'"
|
||||
v-model="visible"
|
||||
width="400px"
|
||||
@close="resetForm"
|
||||
>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">上级编码:</label>
|
||||
<el-input type="text" v-model="form.p_id" style="width: 280px" disabled />
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">上级名称:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.p_name"
|
||||
style="width: 280px"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">本级编码:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.bm_b"
|
||||
placeholder="请输入本级编码"
|
||||
style="width: 280px"
|
||||
:disabled="isDisabled"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">本级名称:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.mc_b"
|
||||
placeholder="请输入本级名称"
|
||||
style="width: 280px"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">排序:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.b_xh"
|
||||
placeholder="请输入排序"
|
||||
style="width: 280px"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">主菜单显示:</label>
|
||||
<el-checkbox
|
||||
v-model="form.b_canmenu"
|
||||
:true-value="1"
|
||||
:false-value="0"
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">目录显示:</label>
|
||||
<el-checkbox
|
||||
v-model="form.b_canuse"
|
||||
:true-value="1"
|
||||
:false-value="0"
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.font-medium {
|
||||
font-weight: 500;
|
||||
width: 74px;
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,244 @@
|
||||
|
||||
<script lang="js" setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
visible: Boolean,
|
||||
mode: { type: String, default: "add" }, // add | edit
|
||||
node: { type: Object, default: () => ({}) }, // 当前节点数据(edit时使用)
|
||||
});
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits(["update:visible", "submit"]);
|
||||
|
||||
// Local state
|
||||
const form = ref({});
|
||||
const visible = ref(props.visible);
|
||||
const isDisabled = ref(false); // 控制是否禁用
|
||||
|
||||
// 同步外部 v-model
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => (visible.value = val),
|
||||
);
|
||||
watch(visible, (val) => emit("update:visible", val));
|
||||
|
||||
//编辑时回填
|
||||
watch(
|
||||
() => props.node,
|
||||
(val) => {
|
||||
if (val) {
|
||||
if (props.mode == "add") {
|
||||
isDisabled.value = false;
|
||||
form.value = val;
|
||||
}
|
||||
if (props.mode == "edit") {
|
||||
isDisabled.value = true;
|
||||
form.value = val;
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// 提交
|
||||
const handleSubmit = () => {
|
||||
if (!form.value.b_id || !form.value.b_Cate_ID) {
|
||||
ElMessage.warning("模块代码、类别不能为空");
|
||||
return;
|
||||
}
|
||||
emit("submit", { ...form.value });
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
form.value.p_id = "";
|
||||
form.value.p_name = "";
|
||||
form.value.bm_b = "";
|
||||
form.value.mc_b = "";
|
||||
form.value.b_xh = "";
|
||||
form.value.b_canuse = "";
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="mode === 'add' ? '新增分类' : '修改分类'"
|
||||
v-model="visible"
|
||||
width="500px"
|
||||
@close="resetForm"
|
||||
>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">模块代码:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.b_id"
|
||||
placeholder="请输入模块代码"
|
||||
style="width: 374px"
|
||||
:disabled="isDisabled"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">描述:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.b_ChineseName"
|
||||
placeholder="请输入描述"
|
||||
style="width: 374px"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">视图:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.b_ViewTable"
|
||||
placeholder="请输入视图"
|
||||
style="width: 374px"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">表名:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.b_SaveTable"
|
||||
placeholder="请输入表名"
|
||||
style="width: 374px"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">关联模块:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.b_LinkTable"
|
||||
placeholder="请输入关联模块"
|
||||
style="width: 374px"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">排序字段:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.b_OrderField"
|
||||
placeholder="请输入排序字段"
|
||||
style="width: 374px"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">删除条件:</label>
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="form.b_JudgeCondition"
|
||||
placeholder="请输入删除条件"
|
||||
style="width: 374px"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">提示信息:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.b_Message"
|
||||
placeholder="请输入提示信息"
|
||||
style="width: 374px"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">默认显示条件:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.b_DefaultCondition"
|
||||
placeholder="请输入默认显示条件"
|
||||
style="width: 374px"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">编码分类表:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.b_BMFL"
|
||||
placeholder="请输入编码分类表"
|
||||
style="width: 374px"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">类别:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.b_Cate_ID"
|
||||
placeholder="请输入类别"
|
||||
style="width: 374px"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">顺序:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.b_Order"
|
||||
placeholder="请输入顺序"
|
||||
style="width: 374px"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">编辑路径:</label>
|
||||
<el-input
|
||||
type="text"
|
||||
v-model="form.b_editfile"
|
||||
placeholder="请输入编辑路径"
|
||||
style="width: 374px"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">主模块</label>
|
||||
<el-checkbox
|
||||
v-model="form.b_isMainModule"
|
||||
:true-label="1"
|
||||
:false-label="0"
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">虚拟</label>
|
||||
<el-checkbox
|
||||
v-model="form.b_ISVirtualModule"
|
||||
:true-value="1"
|
||||
:false-value="0"
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">自动编码</label>
|
||||
<el-checkbox
|
||||
v-model="form.b_autoid"
|
||||
:true-value="1"
|
||||
:false-value="0"
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; margin-top: 3px; align-items: center">
|
||||
<label class="font-medium">审核中心</label>
|
||||
<el-checkbox
|
||||
v-model="form.b_SH"
|
||||
:true-value="1"
|
||||
:false-value="0"
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.font-medium {
|
||||
font-weight: 500;
|
||||
width: 90px;
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,473 @@
|
||||
<script setup>
|
||||
import { loadDataApi, saveObjectApi } from "@/request/api";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Boolean,
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const loading = reactive({
|
||||
table: true,
|
||||
save: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const visible = ref(props.modelValue);
|
||||
const tableData = ref([]);
|
||||
const selectedRows = ref([]);
|
||||
const deletedRows = ref([]);
|
||||
const activeGroup = ref(null);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(v) => (visible.value = v),
|
||||
);
|
||||
watch(visible, (v) => emit("update:modelValue", v));
|
||||
|
||||
// 计算属性:获取所有不重复的组名
|
||||
const groupList = computed(() => {
|
||||
const groups = new Set();
|
||||
tableData.value.forEach((row) => {
|
||||
groups.add(row.b_groupby || "");
|
||||
});
|
||||
return Array.from(groups).sort();
|
||||
});
|
||||
|
||||
// 计算属性:根据当前选中的组过滤数据
|
||||
const filteredTableData = computed(() => {
|
||||
if (!activeGroup.value) return tableData.value;
|
||||
return tableData.value.filter(
|
||||
(row) =>
|
||||
(row.b_groupby || "") ===
|
||||
(activeGroup.value === "" ? null : activeGroup.value),
|
||||
);
|
||||
});
|
||||
|
||||
// 获取某个组的记录数量
|
||||
const getGroupCount = (group) => {
|
||||
return tableData.value.filter(
|
||||
(row) => (row.b_groupby || "") === (group === "" ? null : group),
|
||||
).length;
|
||||
};
|
||||
|
||||
const handleSelectionChange = (rows) => {
|
||||
selectedRows.value = rows;
|
||||
};
|
||||
|
||||
// 按组过滤数据
|
||||
const filterByGroup = (group) => {
|
||||
activeGroup.value = group === activeGroup.value ? null : group;
|
||||
};
|
||||
|
||||
async function initData() {
|
||||
loading.table = true;
|
||||
|
||||
try {
|
||||
const response = await loadDataApi("s_keyvalue");
|
||||
|
||||
if (response.success) {
|
||||
tableData.value = (response.data || [])
|
||||
.filter((item) => !props.data?.b_key || item.b_key !== props.data.b_key)
|
||||
.map((item) => ({
|
||||
...item,
|
||||
isEditing: false,
|
||||
isModified: false,
|
||||
isNew: false,
|
||||
}));
|
||||
deletedRows.value = [];
|
||||
activeGroup.value = null;
|
||||
} else {
|
||||
tableData.value = [];
|
||||
ElMessage.error(response.data?.message || "加载列信息失败");
|
||||
}
|
||||
} catch (err) {
|
||||
tableData.value = [];
|
||||
} finally {
|
||||
loading.table = false;
|
||||
}
|
||||
}
|
||||
|
||||
const close = (resetData = true) => {
|
||||
if (resetData) {
|
||||
tableData.value = [];
|
||||
selectedRows.value = [];
|
||||
deletedRows.value = [];
|
||||
activeGroup.value = null;
|
||||
}
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
// 验证所有修改过的行
|
||||
const invalidRows = tableData.value.filter(
|
||||
(row) =>
|
||||
(row.isModified || row.isNew) &&
|
||||
(!row.b_key?.trim() || !row.b_value?.trim()),
|
||||
);
|
||||
|
||||
if (invalidRows.length > 0) {
|
||||
ElMessage.error("请填写完整的键和值");
|
||||
return;
|
||||
}
|
||||
|
||||
loading.save = true;
|
||||
|
||||
// 构建保存数据
|
||||
const updates = tableData.value
|
||||
.filter((row) => row.isModified && !row.isNew)
|
||||
.map((row) => ({
|
||||
b_key: row.b_key,
|
||||
b_value: row.b_value,
|
||||
b_groupby: row.b_groupby,
|
||||
b_bz: row.b_bz,
|
||||
}));
|
||||
|
||||
const inserts = tableData.value
|
||||
.filter((row) => row.isNew)
|
||||
.map((row) => ({
|
||||
b_key: row.b_key,
|
||||
b_value: row.b_value,
|
||||
b_groupby: row.b_groupby,
|
||||
b_bz: row.b_bz,
|
||||
}));
|
||||
|
||||
const deletes = deletedRows.value.map((row) => ({
|
||||
b_key: row.b_key,
|
||||
}));
|
||||
|
||||
const requestData = [
|
||||
{
|
||||
tableName: "s_keyvalue",
|
||||
keyField: "b_key",
|
||||
inserts,
|
||||
updates,
|
||||
deletes,
|
||||
},
|
||||
];
|
||||
|
||||
try {
|
||||
const response = await saveObjectApi(requestData);
|
||||
|
||||
if (response.success) {
|
||||
ElMessage.success("保存成功");
|
||||
await initData(); // 重新加载数据,这会更新组列表
|
||||
} else {
|
||||
throw new Error(response.message || "保存失败");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("保存出错:", err);
|
||||
} finally {
|
||||
loading.save = false;
|
||||
}
|
||||
};
|
||||
|
||||
function addRowAbove(index) {
|
||||
const newRow = {
|
||||
b_key: "",
|
||||
b_value: "",
|
||||
b_groupby:
|
||||
activeGroup.value === ""
|
||||
? null
|
||||
: activeGroup.value || props.data?.b_groupby || "",
|
||||
b_bz: "",
|
||||
isEditing: true,
|
||||
isModified: true,
|
||||
isNew: true,
|
||||
};
|
||||
|
||||
tableData.value.splice(index, 0, newRow);
|
||||
|
||||
nextTick(() => {
|
||||
const input = document.querySelector(
|
||||
`.el-table__body tr:nth-child(${index + 1}) .el-input__inner`,
|
||||
);
|
||||
input?.focus();
|
||||
});
|
||||
}
|
||||
|
||||
function toggleEdit(row) {
|
||||
row.isEditing = !row.isEditing;
|
||||
if (row.isEditing && !row.isNew) {
|
||||
row.isModified = true;
|
||||
}
|
||||
nextTick(() => {
|
||||
const input = document.querySelector(".el-table__body .el-input__inner");
|
||||
input?.focus();
|
||||
});
|
||||
}
|
||||
|
||||
const deleteRow = (row, index) => {
|
||||
ElMessageBox.confirm("确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(() => {
|
||||
if (!row.isNew) {
|
||||
deletedRows.value.push(row); // 记录已删除的行
|
||||
}
|
||||
tableData.value.splice(index, 1);
|
||||
ElMessage.success("删除成功");
|
||||
});
|
||||
};
|
||||
|
||||
function validateField(row, field) {
|
||||
if (field === "b_key" && !row.b_key.trim()) {
|
||||
ElMessage.warning("键不能为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (field === "b_value" && !row.b_value.trim()) {
|
||||
ElMessage.warning("值不能为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
v-model="visible"
|
||||
title="键值设置"
|
||||
width="1200px"
|
||||
class="dialog"
|
||||
@open="initData"
|
||||
>
|
||||
<div class="content-wrapper">
|
||||
<!-- 左侧组列表 -->
|
||||
<div class="group-list">
|
||||
<div class="group-header">组列表</div>
|
||||
<el-scrollbar class="group-scroll">
|
||||
<div
|
||||
v-for="group in groupList"
|
||||
:key="group"
|
||||
class="group-item"
|
||||
:class="{ active: activeGroup === group }"
|
||||
@click="filterByGroup(group)"
|
||||
>
|
||||
{{ group || "未分组" }}
|
||||
<span class="count-badge">{{ getGroupCount(group) }}</span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<!-- 右侧表格 -->
|
||||
<div class="table-container">
|
||||
<el-table
|
||||
v-loading="loading.table"
|
||||
:data="filteredTableData"
|
||||
border
|
||||
class="table"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="40" align="center" fixed />
|
||||
<el-table-column prop="b_key" label="键" align="center" width="180">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_key"
|
||||
size="small"
|
||||
@blur="validateField(row, 'b_key')"
|
||||
@click.stop
|
||||
/>
|
||||
<span v-else>{{ row.b_key }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="b_value" label="值" align="center" width="180">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_value"
|
||||
size="small"
|
||||
@blur="validateField(row, 'b_value')"
|
||||
@click.stop
|
||||
/>
|
||||
<span v-else>{{ row.b_value }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="b_bz" label="说明" align="center" width="200">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_bz"
|
||||
size="small"
|
||||
@click.stop
|
||||
/>
|
||||
<span v-else>{{ row.b_bz }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="b_groupby"
|
||||
label="组"
|
||||
align="center"
|
||||
width="150"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_groupby"
|
||||
size="small"
|
||||
@click.stop
|
||||
/>
|
||||
<span v-else>{{ row.b_groupby || "未分组" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column width="170" fixed="right" align="center">
|
||||
<template #header>
|
||||
<span class="action-label">操作</span>
|
||||
</template>
|
||||
<template #default="{ row, $index }">
|
||||
<div class="table-actions">
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="addRowAbove($index)"
|
||||
class="action-btn"
|
||||
>
|
||||
添加
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="toggleEdit(row)"
|
||||
class="action-btn"
|
||||
>
|
||||
{{ row.isEditing ? "完成" : "编辑" }}
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
type="danger"
|
||||
@click="deleteRow(row, $index)"
|
||||
class="action-btn"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" :loading="loading.save" @click="handleSave"
|
||||
>确认</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dialog {
|
||||
--el-dialog-margin-top: 5vh;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.group-list {
|
||||
width: 200px;
|
||||
border-right: 1px solid #ebeef5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.group-header {
|
||||
padding: 6px 16px;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
background-color: #f5f7fa;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.group-scroll {
|
||||
flex: 1;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.group-item {
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.group-item:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.group-item.active {
|
||||
background-color: #ecf5ff;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.count-badge {
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 10px;
|
||||
padding: 0 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table {
|
||||
font-size: 12px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.table ::v-deep(.el-table__header-wrapper th) {
|
||||
background-color: #f5f7fa !important;
|
||||
color: #303133 !important;
|
||||
}
|
||||
|
||||
.table ::v-deep(.cell) {
|
||||
line-height: normal;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.table ::v-deep(.el-table__cell) {
|
||||
padding: 4px 0 !important;
|
||||
}
|
||||
|
||||
.table-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 5px 8px;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.action-label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,560 @@
|
||||
<script setup>
|
||||
import { loadDataApi, saveObjectApi } from "@/request/api";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Boolean,
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const loading = reactive({
|
||||
table: true,
|
||||
save: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const visible = ref(props.modelValue);
|
||||
const tableData = ref([]);
|
||||
const selectedRows = ref([]);
|
||||
const deletedRows = ref([]);
|
||||
const activeGroup = ref(null);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(v) => (visible.value = v),
|
||||
);
|
||||
watch(visible, (v) => emit("update:modelValue", v));
|
||||
|
||||
// 计算属性:获取所有不重复的组名
|
||||
const groupList = computed(() => {
|
||||
const groups = new Set();
|
||||
tableData.value.forEach((row) => {
|
||||
groups.add(row.b_groupby || "");
|
||||
});
|
||||
return Array.from(groups).sort();
|
||||
});
|
||||
|
||||
// 计算属性:根据当前选中的组过滤数据
|
||||
const filteredTableData = computed(() => {
|
||||
if (activeGroup.value === null || activeGroup.value === undefined) {
|
||||
return tableData.value; // 显示所有数据
|
||||
}
|
||||
return tableData.value.filter((row) => {
|
||||
const rowGroup = row.b_groupby || "";
|
||||
return rowGroup === (activeGroup.value === "" ? null : activeGroup.value);
|
||||
});
|
||||
});
|
||||
|
||||
// 获取某个组的记录数量
|
||||
const getGroupCount = (group) => {
|
||||
return tableData.value.filter(
|
||||
(row) => (row.b_groupby || "") === (group === "" ? null : group),
|
||||
).length;
|
||||
};
|
||||
|
||||
const handleSelectionChange = (rows) => {
|
||||
selectedRows.value = rows;
|
||||
};
|
||||
|
||||
// 显示所有组的数据
|
||||
const showAllGroups = () => {
|
||||
activeGroup.value = null;
|
||||
};
|
||||
|
||||
// 按组过滤数据
|
||||
const filterByGroup = (group) => {
|
||||
activeGroup.value = group === activeGroup.value ? null : group;
|
||||
};
|
||||
|
||||
const handleSwitchChange = (row) => {
|
||||
if (!row.isNew) {
|
||||
row.isModified = true;
|
||||
}
|
||||
};
|
||||
|
||||
async function initData() {
|
||||
loading.table = true;
|
||||
try {
|
||||
const response = await loadDataApi("b_other_bmfl");
|
||||
|
||||
if (response.success) {
|
||||
tableData.value = (response.data || []).map((item) => ({
|
||||
...item,
|
||||
b_canuse: Number(item.b_canuse) === 1 ? 1 : 0,
|
||||
isEditing: false,
|
||||
isModified: false,
|
||||
isNew: false,
|
||||
}));
|
||||
|
||||
console.log((await response).data)
|
||||
|
||||
deletedRows.value = [];
|
||||
activeGroup.value = null; // 初始状态显示所有数据
|
||||
} else {
|
||||
tableData.value = [];
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("加载数据出错:", err);
|
||||
tableData.value = [];
|
||||
} finally {
|
||||
loading.table = false;
|
||||
}
|
||||
}
|
||||
|
||||
const close = (resetData = true) => {
|
||||
if (resetData) {
|
||||
tableData.value = [];
|
||||
selectedRows.value = [];
|
||||
deletedRows.value = [];
|
||||
activeGroup.value = null;
|
||||
}
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
// 验证所有修改过的行
|
||||
const invalidRows = tableData.value.filter(
|
||||
(row) =>
|
||||
(row.isModified || row.isNew) && (!row.b_id?.trim() || !row.mc_b?.trim()),
|
||||
);
|
||||
|
||||
if (invalidRows.length > 0) {
|
||||
ElMessage.error("请填写完整的键和值");
|
||||
return;
|
||||
}
|
||||
|
||||
loading.save = true;
|
||||
|
||||
try {
|
||||
// 构建保存数据
|
||||
const updates = tableData.value
|
||||
.filter((row) => row.isModified && !row.isNew)
|
||||
.map((row) => ({
|
||||
b_id: row.b_id,
|
||||
mc_b: row.mc_b,
|
||||
b_groupby: row.b_groupby,
|
||||
b_tablename: row.b_tablename,
|
||||
b_canuse: row.b_canuse,
|
||||
b_xh: row.b_xh,
|
||||
}));
|
||||
|
||||
const inserts = tableData.value
|
||||
.filter((row) => row.isNew)
|
||||
.map((row) => ({
|
||||
b_id: row.b_id,
|
||||
mc_b: row.mc_b,
|
||||
b_groupby: row.b_groupby,
|
||||
b_tablename: row.b_tablename,
|
||||
b_canuse: row.b_canuse,
|
||||
b_xh: row.b_xh,
|
||||
}));
|
||||
|
||||
const deletes = deletedRows.value.map((row) => ({
|
||||
b_id: row.b_id,
|
||||
}));
|
||||
|
||||
const requestData = [
|
||||
{
|
||||
tableName: "b_other_bmfl",
|
||||
keyField: "b_id",
|
||||
inserts,
|
||||
updates,
|
||||
deletes,
|
||||
},
|
||||
];
|
||||
|
||||
const response = await saveObjectApi(requestData)
|
||||
|
||||
if (response.success) {
|
||||
ElMessage.success("保存成功");
|
||||
// 重置修改状态
|
||||
tableData.value.forEach((row) => {
|
||||
if (row.isNew) row.isNew = false;
|
||||
if (row.isModified) row.isModified = false;
|
||||
});
|
||||
deletedRows.value = [];
|
||||
// 重新加载数据
|
||||
await initData();
|
||||
} else {
|
||||
throw new Error(response.data?.message || "保存失败");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("保存出错:", err);
|
||||
} finally {
|
||||
loading.save = false;
|
||||
}
|
||||
};
|
||||
|
||||
function addRowAbove(index) {
|
||||
const newRow = {
|
||||
b_id: "",
|
||||
mc_b: "",
|
||||
b_groupby:
|
||||
activeGroup.value === ""
|
||||
? null
|
||||
: activeGroup.value || props.data?.b_groupby || "",
|
||||
b_tablename: "",
|
||||
b_canuse: 1,
|
||||
b_xh: 0,
|
||||
isEditing: true,
|
||||
isModified: true,
|
||||
isNew: true,
|
||||
};
|
||||
|
||||
tableData.value.splice(index, 0, newRow);
|
||||
|
||||
nextTick(() => {
|
||||
const input = document.querySelector(
|
||||
`.el-table__body tr:nth-child(${index + 1}) .el-input__inner`,
|
||||
);
|
||||
input?.focus();
|
||||
});
|
||||
}
|
||||
|
||||
function toggleEdit(row) {
|
||||
row.isEditing = !row.isEditing;
|
||||
if (row.isEditing && !row.isNew) {
|
||||
row.isModified = true;
|
||||
}
|
||||
nextTick(() => {
|
||||
const input = document.querySelector(".el-table__body .el-input__inner");
|
||||
input?.focus();
|
||||
});
|
||||
}
|
||||
|
||||
const deleteRow = (row, index) => {
|
||||
ElMessageBox.confirm("确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(() => {
|
||||
if (!row.isNew) {
|
||||
deletedRows.value.push(row);
|
||||
}
|
||||
tableData.value.splice(index, 1);
|
||||
ElMessage.success("删除成功");
|
||||
});
|
||||
};
|
||||
|
||||
function validateField(row, field) {
|
||||
if (field === "b_id" && !row.b_id.trim()) {
|
||||
ElMessage.warning("键不能为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (field === "mc_b" && !row.mc_b.trim()) {
|
||||
ElMessage.warning("值不能为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
v-model="visible"
|
||||
title="基础数据目录"
|
||||
width="1200px"
|
||||
class="dialog"
|
||||
@opened="initData"
|
||||
>
|
||||
<div class="content-wrapper">
|
||||
<!-- 左侧组列表 -->
|
||||
<div class="group-list">
|
||||
<div
|
||||
class="group-header"
|
||||
:class="{ 'all-groups-active': activeGroup === null }"
|
||||
@click="showAllGroups"
|
||||
>
|
||||
组列表
|
||||
<el-tooltip effect="dark" content="点击显示所有数据" placement="top">
|
||||
<el-icon><G3Icon icon="lucide:info" /></el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-scrollbar class="group-scroll">
|
||||
<div
|
||||
v-for="group in groupList"
|
||||
:key="group"
|
||||
class="group-item"
|
||||
:class="{ active: activeGroup === group }"
|
||||
@click="filterByGroup(group)"
|
||||
>
|
||||
{{ group || "未分组" }}
|
||||
<span class="count-badge">{{ getGroupCount(group) }}</span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<!-- 右侧表格 -->
|
||||
<div class="table-container">
|
||||
<el-table
|
||||
v-loading="loading.table"
|
||||
:data="filteredTableData"
|
||||
border
|
||||
class="table"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="40" align="center" fixed />
|
||||
<el-table-column prop="b_id" label="ID" align="center" width="150">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_id"
|
||||
size="small"
|
||||
@blur="validateField(row, 'b_id')"
|
||||
@click.stop
|
||||
/>
|
||||
<span v-else>{{ row.b_id }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="mc_b" label="名称" align="center" width="150">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.mc_b"
|
||||
size="small"
|
||||
@blur="validateField(row, 'mc_b')"
|
||||
@click.stop
|
||||
/>
|
||||
<span v-else>{{ row.mc_b }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="b_tablename"
|
||||
label="表名"
|
||||
align="center"
|
||||
width="180"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_tablename"
|
||||
size="small"
|
||||
@click.stop
|
||||
/>
|
||||
<span v-else>{{ row.b_tablename }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="b_groupby"
|
||||
label="分组"
|
||||
align="center"
|
||||
width="120"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_groupby"
|
||||
size="small"
|
||||
@click.stop
|
||||
/>
|
||||
<span v-else>{{ row.b_groupby || "未分组" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="b_xh" label="排序" align="center" width="60">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_xh"
|
||||
size="small"
|
||||
@click.stop
|
||||
/>
|
||||
<span v-else>{{ row.b_xh }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="b_canuse"
|
||||
label="启用"
|
||||
align="center"
|
||||
width="100"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-switch
|
||||
v-model="row.b_canuse"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
@change="handleSwitchChange(row)"
|
||||
@click.stop
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="170" fixed="right" align="center">
|
||||
<template #header>
|
||||
<span class="action-label">操作</span>
|
||||
</template>
|
||||
<template #default="{ row, $index }">
|
||||
<div class="table-actions">
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="addRowAbove($index)"
|
||||
class="action-btn"
|
||||
>
|
||||
添加
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="toggleEdit(row)"
|
||||
class="action-btn"
|
||||
>
|
||||
{{ row.isEditing ? "完成" : "编辑" }}
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
type="danger"
|
||||
@click="deleteRow(row, $index)"
|
||||
class="action-btn"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" :loading="loading.save" @click="handleSave"
|
||||
>确认</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.dialog {
|
||||
--el-dialog-margin-top: 5vh;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.group-list {
|
||||
width: 200px;
|
||||
border-right: 1px solid #ebeef5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.group-header {
|
||||
padding: 6px 16px;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
background-color: #f5f7fa;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.group-header:hover {
|
||||
background-color: #e6e9ed;
|
||||
}
|
||||
|
||||
.group-header.all-groups-active {
|
||||
background-color: #ecf5ff;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.group-scroll {
|
||||
flex: 1;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.group-item {
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.group-item:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.group-item.active {
|
||||
background-color: #ecf5ff;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.count-badge {
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 10px;
|
||||
padding: 0 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table {
|
||||
font-size: 12px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.table ::v-deep(.el-table__header-wrapper th) {
|
||||
background-color: #f5f7fa !important;
|
||||
color: #303133 !important;
|
||||
}
|
||||
|
||||
.table ::v-deep(.cell) {
|
||||
line-height: normal;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.table ::v-deep(.el-table__cell) {
|
||||
padding: 4px 0 !important;
|
||||
}
|
||||
|
||||
.table-actions {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 5px 8px;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.action-label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.el-switch {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,899 @@
|
||||
<script setup>
|
||||
import { loadDataBySqlApi, saveObjectApi } from "@/request/api";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Boolean,
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const loading = reactive({
|
||||
table: true,
|
||||
save: false,
|
||||
qxtable: false,
|
||||
Powertable: false,
|
||||
current_moduleid: null,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const visible = ref(props.modelValue);
|
||||
const tableData = ref([]);
|
||||
const selectedRows = ref([]);
|
||||
const deletedRows = ref([]);
|
||||
const activeGroup = ref(null);
|
||||
|
||||
const qxData = ref([]);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(v) => (visible.value = v),
|
||||
);
|
||||
watch(visible, (v) => emit("update:modelValue", v));
|
||||
|
||||
const currentEditRow = ref(null);
|
||||
|
||||
const handleSelectionChange = (rows) => {
|
||||
selectedRows.value = rows;
|
||||
};
|
||||
const handlemoduleClick = (row, column, event) => {
|
||||
console.log("handlemoduleClick");
|
||||
console.log(row.b_id);
|
||||
loading.current_moduleid = row.b_id;
|
||||
LoadpowerData(row.b_id);
|
||||
};
|
||||
|
||||
const handleRowClick = (row, column, event) => {
|
||||
// 如果点击的是当前正在编辑的行,则不处理
|
||||
if (currentEditRow.value === row.id) {
|
||||
return;
|
||||
}
|
||||
// 关闭之前行的编辑状态
|
||||
if (currentEditRow.value !== null) {
|
||||
const prevRow = qxData.value.find(
|
||||
(item) => item.id === currentEditRow.value,
|
||||
);
|
||||
if (prevRow) {
|
||||
console.log(prevRow);
|
||||
const oldrow = prevRow.originalData;
|
||||
// 检查数据是否有变动
|
||||
prevRow.isModified = isDataModified(prevRow, oldrow);
|
||||
prevRow.isEditing = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置新行的编辑状态
|
||||
row.isEditing = true;
|
||||
row.isModified = true;
|
||||
currentEditRow.value = row.id;
|
||||
};
|
||||
// 简单的对象比较函数
|
||||
const isDataModified = (obj1, obj2) => {
|
||||
// 排除不需要比较的字段
|
||||
const excludeFields = ["isEditing", "isModified"];
|
||||
const keys1 = Object.keys(obj1).filter((key) => !excludeFields.includes(key));
|
||||
const keys2 = Object.keys(obj2).filter((key) => !excludeFields.includes(key));
|
||||
|
||||
if (keys1.length !== keys2.length) return true;
|
||||
|
||||
for (let key of keys2) {
|
||||
if (typeof obj1[key] === "object" && typeof obj2[key] === "object") {
|
||||
// 如果是对象,递归比较
|
||||
if (JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])) {
|
||||
return true;
|
||||
}
|
||||
} else if (obj1[key] !== obj2[key]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// 新增记录
|
||||
const addNewRow = () => {
|
||||
// 如果当前有编辑中的行,先处理
|
||||
if (currentEditRow.value !== null) {
|
||||
const prevRow = qxData.value.find(
|
||||
(item) => item.id === currentEditRow.value,
|
||||
);
|
||||
if (prevRow) {
|
||||
// handleRowChange(prevRow)
|
||||
prevRow.isEditing = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算新的排序号(最大值+1)
|
||||
const maxSort =
|
||||
qxData.value.length > 0
|
||||
? Math.max(...qxData.value.map((item) => item.b_xh || 0))
|
||||
: 0;
|
||||
|
||||
const newRow = {
|
||||
id: generateId(),
|
||||
b_xh: maxSort + 1,
|
||||
ModuleID: loading.current_moduleid,
|
||||
b_ivalue: "",
|
||||
b_caption: "",
|
||||
b_des: "",
|
||||
isEditing: true, // 新增行默认进入编辑状态
|
||||
isModified: false,
|
||||
isNew: true, // 标记为新增行
|
||||
isSaving: false, // 保存状态
|
||||
};
|
||||
newRow.originalData = { ...newRow };
|
||||
// 添加到表格开头
|
||||
qxData.value.push(newRow);
|
||||
|
||||
// 设置当前编辑行
|
||||
currentEditRow.value = newRow.id;
|
||||
|
||||
// 滚动到顶部
|
||||
nextTick(() => {
|
||||
const tableWrapper = document.querySelector(".el-table__body-wrapper");
|
||||
if (tableWrapper) {
|
||||
tableWrapper.scrollTop = 0;
|
||||
}
|
||||
});
|
||||
|
||||
console.log("新增记录:", newRow);
|
||||
};
|
||||
// 生成唯一ID
|
||||
let idCounter = 2;
|
||||
const generateId = () => {
|
||||
return ++idCounter;
|
||||
};
|
||||
|
||||
async function initData() {
|
||||
loading.table = true;
|
||||
|
||||
try {
|
||||
Load_moduleData();
|
||||
Load_powerData();
|
||||
} catch (err) {
|
||||
tableData.value = [];
|
||||
} finally {
|
||||
loading.table = false;
|
||||
}
|
||||
}
|
||||
const moduleData = ref([]);
|
||||
async function Load_moduleData() {
|
||||
try {
|
||||
moduleData.value = [];
|
||||
|
||||
let sql = `
|
||||
select b_id,b_ChineseName from s_module where b_isMainModule='1'
|
||||
order by b_order
|
||||
`;
|
||||
console.log("原始sql:", sql);
|
||||
const response = await loadDataBySqlApi(sql);
|
||||
|
||||
if (response.success) {
|
||||
const records = response.data || [];
|
||||
// 转换数据
|
||||
moduleData.value = records.map((item, index) => {
|
||||
return {
|
||||
id: item.id || `temp_${Date.now()}_${index}`,
|
||||
b_id: item.b_id,
|
||||
b_ChineseName: item.b_ChineseName,
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
moduleData.value = [];
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
const formPowerData = ref([]);
|
||||
async function Load_powerData() {
|
||||
try {
|
||||
formPowerData.value = [];
|
||||
|
||||
let sql = `
|
||||
select b_ivalue,max(b_caption) b_caption,max(b_des) b_des from s_module_power
|
||||
group by b_ivalue order by b_ivalue
|
||||
`;
|
||||
console.log("原始sql:", sql);
|
||||
const response = await loadDataBySqlApi(sql);
|
||||
|
||||
if (response.success) {
|
||||
const records = response.data || [];
|
||||
// 转换数据
|
||||
formPowerData.value = records.map((item, index) => {
|
||||
return {
|
||||
id: item.id || `temp_${Date.now()}_${index}`,
|
||||
b_ivalue: item.b_ivalue || "0",
|
||||
b_caption: item.b_caption || "",
|
||||
b_des: item.b_des || "",
|
||||
isEditing: false,
|
||||
isModified: false,
|
||||
isNew: false,
|
||||
originalData: { ...item },
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
formPowerData.value = [];
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
async function gethispower() {
|
||||
try {
|
||||
const selectedData = selectedRows.value;
|
||||
|
||||
if (selectedData.length === 0) {
|
||||
// ElMessage.success('请先勾选记录')
|
||||
ElMessage.error("请先勾选记录");
|
||||
return;
|
||||
}
|
||||
var bxh = qxData.value.length;
|
||||
selectedData.forEach((item, index) => {
|
||||
bxh++;
|
||||
const newRecord = {
|
||||
ModuleID: loading.current_moduleid,
|
||||
b_xh: bxh, // 默认为空
|
||||
b_ivalue: item.b_ivalue,
|
||||
b_caption: item.b_caption,
|
||||
b_des: item.b_des,
|
||||
// isEditing: true, // 添加时直接进入编辑状态
|
||||
isNew: true, // 标记为新记录
|
||||
originalData: { ...item },
|
||||
};
|
||||
qxData.value.push(newRecord);
|
||||
});
|
||||
} catch (err) {
|
||||
qxData.value = [];
|
||||
ElMessage.error(err?.message || "加载列信息失败");
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
async function LoadpowerData(moduleid) {
|
||||
loading.qxtable = true;
|
||||
try {
|
||||
let sql =
|
||||
`
|
||||
SELECT * FROM s_module_power
|
||||
where ModuleID = '` +
|
||||
moduleid +
|
||||
`'
|
||||
ORDER BY b_xh
|
||||
`;
|
||||
|
||||
console.log("原始sql:", sql);
|
||||
const response = await loadDataBySqlApi(sql);
|
||||
|
||||
if (response.success) {
|
||||
const records = response.data || [];
|
||||
// 转换数据
|
||||
qxData.value = records.map((item, index) => {
|
||||
// console.log(`处理第 ${index} 条数据:`, item)
|
||||
return {
|
||||
id: item.id || `temp_${Date.now()}_${index}`,
|
||||
b_id: item.b_id || "",
|
||||
b_xh: item.b_xh || "",
|
||||
ModuleID: item.ModuleID || "",
|
||||
b_ivalue: item.b_ivalue || "0",
|
||||
b_caption: item.b_caption || "",
|
||||
b_des: item.b_des || "",
|
||||
isEditing: false,
|
||||
isModified: false,
|
||||
isNew: false,
|
||||
originalData: { ...item },
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
qxData.value = [];
|
||||
} finally {
|
||||
loading.qxtable = false;
|
||||
}
|
||||
}
|
||||
|
||||
const close = (resetData = true) => {
|
||||
if (resetData) {
|
||||
tableData.value = [];
|
||||
selectedRows.value = [];
|
||||
deletedRows.value = [];
|
||||
activeGroup.value = null;
|
||||
}
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
// 验证所有修改过的行
|
||||
const invalidRows = qxData.value.filter((row) => row.isModified || row.isNew);
|
||||
/*
|
||||
if (invalidRows.length > 0) {
|
||||
ElMessage.error('请填写完整的模块和Tag值')
|
||||
return
|
||||
}
|
||||
*/
|
||||
loading.save = true;
|
||||
|
||||
// 构建保存数据
|
||||
const updates = qxData.value
|
||||
.filter((row) => row.isModified && !row.isNew)
|
||||
.map((row) => ({
|
||||
b_id: row.b_id,
|
||||
ModuleID: row.ModuleID,
|
||||
b_ivalue: row.b_ivalue,
|
||||
b_xh: row.b_xh,
|
||||
b_caption: row.b_caption,
|
||||
b_des: row.b_des,
|
||||
}));
|
||||
|
||||
const inserts = qxData.value
|
||||
.filter((row) => row.isNew)
|
||||
.map((row) => ({
|
||||
b_id: row.b_id,
|
||||
ModuleID: row.ModuleID,
|
||||
b_ivalue: row.b_ivalue,
|
||||
b_xh: row.b_xh,
|
||||
b_caption: row.b_caption,
|
||||
b_des: row.b_des,
|
||||
}));
|
||||
|
||||
const deletes = deletedRows.value.map((row) => ({
|
||||
b_id: row.b_id,
|
||||
}));
|
||||
|
||||
const requestData = [
|
||||
{
|
||||
tableName: "s_module_power",
|
||||
keyField: "b_id",
|
||||
inserts,
|
||||
updates,
|
||||
deletes,
|
||||
},
|
||||
];
|
||||
|
||||
try {
|
||||
const response = await saveObjectApi(requestData);
|
||||
|
||||
if (response.data?.success) {
|
||||
ElMessage.success("保存成功");
|
||||
await initData(); // 重新加载数据,这会更新组列表
|
||||
} else {
|
||||
throw new Error(response.data?.message || "保存失败");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("保存出错:", err);
|
||||
ElMessage.error(err.message || "保存失败");
|
||||
} finally {
|
||||
loading.save = false;
|
||||
}
|
||||
};
|
||||
/*
|
||||
const deleteRow = (row, index) => {
|
||||
ElMessageBox.confirm('确定要删除吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
if (!row.isNew) {
|
||||
deletedRows.value.push(row) // 记录已删除的行
|
||||
}
|
||||
qxData.value.splice(index, 1)
|
||||
ElMessage.success('删除成功')
|
||||
})
|
||||
} */
|
||||
const deleteRow = (row) => {
|
||||
ElMessageBox.confirm("确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(() => {
|
||||
if (!row.isNew) {
|
||||
deletedRows.value.push(row);
|
||||
}
|
||||
|
||||
// 通过 findIndex 查找当前行的索引
|
||||
const index = qxData.value.findIndex((item) => item.id === row.id); // 假设有唯一标识如 id
|
||||
if (index !== -1) {
|
||||
qxData.value.splice(index, 1);
|
||||
ElMessage.success("删除成功");
|
||||
}
|
||||
});
|
||||
};
|
||||
//////////===== 获取 SQL语句预览 -------------
|
||||
const dialogSQLVisible = ref(false);
|
||||
const dialogTitle = ref("SQL语句预览");
|
||||
const sqlContent = ref("");
|
||||
const showqxSql = (row) => {
|
||||
dialogSQLVisible.value = true;
|
||||
try {
|
||||
let sql =
|
||||
`delete from s_module_power where ModuleID=' ` +
|
||||
loading.current_moduleid +
|
||||
`';\n\n`;
|
||||
// 使用 reduce 或 forEach 拼接
|
||||
qxData.value.forEach((item, index) => {
|
||||
// 建议添加数据校验
|
||||
if (item.ModuleID && item.b_ivalue !== undefined) {
|
||||
const insertSql =
|
||||
`insert into s_module_power(ModuleID,b_ivalue,b_xh,b_caption,b_des)\n` +
|
||||
`values('${item.ModuleID}','${item.b_ivalue}','${item.b_xh || ""}','${item.b_caption || ""}','${item.b_des || ""}');`;
|
||||
sql += insertSql + "\n";
|
||||
}
|
||||
});
|
||||
|
||||
sqlContent.value = sql;
|
||||
} catch (err) {
|
||||
} finally {
|
||||
}
|
||||
};
|
||||
const handleDialogClose = () => {
|
||||
dialogSQLVisible.value = false;
|
||||
sqlContent.value = ""; // 清空内容
|
||||
};
|
||||
|
||||
// 复制SQL到剪贴板
|
||||
const handleCopySql = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(sqlContent.value);
|
||||
ElMessage.success("SQL已复制到剪贴板");
|
||||
} catch (err) {
|
||||
// 降级方案
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = sqlContent.value;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
try {
|
||||
document.execCommand("copy");
|
||||
ElMessage.success("SQL已复制到剪贴板");
|
||||
} catch (e) {
|
||||
ElMessage.error("复制失败");
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
};
|
||||
//////////===== 获取 SQL语句预览 ------结束-------
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
v-model="visible"
|
||||
title="模块-权限设置"
|
||||
width="1200px"
|
||||
class="dialog"
|
||||
@open="initData"
|
||||
>
|
||||
<div class="content-wrapper">
|
||||
<!-- 左侧组列表 -->
|
||||
<div class="g3-container-left">
|
||||
<el-table
|
||||
v-loading="loading.moduletable"
|
||||
:data="moduleData"
|
||||
border
|
||||
class="table"
|
||||
height="560px"
|
||||
:max-height="tableHeight"
|
||||
@row-click="handlemoduleClick"
|
||||
>
|
||||
<el-table-column
|
||||
prop="b_id"
|
||||
label="模块ID"
|
||||
align="center"
|
||||
width="150"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input v-if="row.isEditing" v-model="row.b_id" size="small" />
|
||||
<span v-else>{{ row.b_id }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="b_ChineseName"
|
||||
label="模块名称"
|
||||
align="center"
|
||||
width="120"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_ChineseName"
|
||||
size="small"
|
||||
/>
|
||||
<span v-else>{{ row.b_ChineseName }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<div class="g3-row-main">
|
||||
<div style="width: 100%; margin-bottom: 4px">
|
||||
<div style="display: flex">
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="loading.save"
|
||||
@click="addNewRow"
|
||||
>新增</el-button
|
||||
>
|
||||
<el-button type="primary" @click="handleSave">保存</el-button>
|
||||
<el-button type="primary" @click="showqxSql">SQL语句</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style="
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
"
|
||||
>
|
||||
<el-table
|
||||
v-loading="loading.qxtable"
|
||||
:data="qxData"
|
||||
border
|
||||
class="table"
|
||||
height="560px"
|
||||
:max-height="tableHeight"
|
||||
@row-click="handleRowClick"
|
||||
>
|
||||
<el-table-column
|
||||
prop="b_xh"
|
||||
label="排序"
|
||||
align="center"
|
||||
width="50"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_xh"
|
||||
size="small"
|
||||
/>
|
||||
<span v-else>{{ row.b_xh }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="false"
|
||||
prop="b_id"
|
||||
label="流水号"
|
||||
align="center"
|
||||
width="0"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_id"
|
||||
size="small"
|
||||
/>
|
||||
<span v-else>{{ row.b_id }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="ModuleID"
|
||||
label="对应模块"
|
||||
align="center"
|
||||
width="120"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.ModuleID"
|
||||
size="small"
|
||||
/>
|
||||
<span v-else>{{ row.ModuleID }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="b_ivalue"
|
||||
label="Tag值"
|
||||
align="center"
|
||||
width="60"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_ivalue"
|
||||
size="small"
|
||||
@blur="validateField(row, 'b_ivalue')"
|
||||
@click.stop
|
||||
/>
|
||||
<span v-else>{{ row.b_ivalue }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="b_caption"
|
||||
label="Caption"
|
||||
align="center"
|
||||
width="130"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_caption"
|
||||
size="small"
|
||||
@blur="validateField(row, 'b_caption')"
|
||||
@click.stop
|
||||
/>
|
||||
<span v-else>{{ row.b_caption }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="b_des"
|
||||
label="描述"
|
||||
align="center"
|
||||
width="170"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_des"
|
||||
size="small"
|
||||
@blur="validateField(row, 'b_des')"
|
||||
@click.stop
|
||||
/>
|
||||
<span v-else>{{ row.b_des }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
fixed="right"
|
||||
label="操作"
|
||||
align="center"
|
||||
width="100"
|
||||
>
|
||||
<template #default="{ row, $index }">
|
||||
<el-button
|
||||
:disabled="false"
|
||||
type="primary"
|
||||
link
|
||||
@click="deleteRow(row, $index)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group-right">
|
||||
<div class="g3-row-main">
|
||||
<div style="width: 100%; margin-bottom: 4px">
|
||||
<div style="display: flex">
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="loading.save"
|
||||
@click="gethispower"
|
||||
>←导</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style="
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
"
|
||||
>
|
||||
<el-table
|
||||
v-loading="loading.powertable"
|
||||
:data="formPowerData"
|
||||
border
|
||||
class="table"
|
||||
height="560px"
|
||||
:max-height="tableHeight"
|
||||
ref="powerTableRef"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column
|
||||
type="selection"
|
||||
width="40"
|
||||
align="center"
|
||||
fixed
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
prop="b_ivalue"
|
||||
label="Tag值"
|
||||
align="center"
|
||||
width="60"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_ivalue"
|
||||
size="small"
|
||||
/>
|
||||
<span v-else>{{ row.b_ivalue }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="b_caption"
|
||||
label="Caption"
|
||||
align="center"
|
||||
width="100"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="row.isEditing"
|
||||
v-model="row.b_caption"
|
||||
size="small"
|
||||
/>
|
||||
<span v-else>{{ row.b_caption }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 对话框内容 -->
|
||||
<el-dialog
|
||||
v-model="dialogSQLVisible"
|
||||
:title="dialogTitle"
|
||||
width="650px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div class="sql-preview-container">
|
||||
<el-input
|
||||
v-model="sqlContent"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 8, maxRows: 20 }"
|
||||
placeholder="SQL语句将在此处显示..."
|
||||
readonly
|
||||
class="sql-textarea"
|
||||
>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleDialogClose">关闭</el-button>
|
||||
<el-button type="primary" @click="handleCopySql">复制SQL</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.dialog {
|
||||
--el-dialog-margin-top: 5vh;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.group-list {
|
||||
width: 160px;
|
||||
border-right: 1px solid #ebeef5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.group-header {
|
||||
padding: 6px 16px;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
background-color: #f5f7fa;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.group-scroll {
|
||||
flex: 1;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.group-item {
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.group-item:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.group-item.active {
|
||||
background-color: #ecf5ff;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.count-badge {
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 10px;
|
||||
padding: 0 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.g3-container-left {
|
||||
width: 190px;
|
||||
min-width: 170px;
|
||||
margin-right: 10px;
|
||||
background-color: #ffffff;
|
||||
border-right: 1px solid #e4e7ed;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 0 12px #0000000d;
|
||||
padding: 10px;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
width: 300px;
|
||||
}
|
||||
.table-container {
|
||||
width: 260px;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table {
|
||||
font-size: 12px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.table ::v-deep(.el-table__header-wrapper th) {
|
||||
background-color: #f5f7fa !important;
|
||||
color: #303133 !important;
|
||||
}
|
||||
|
||||
.table ::v-deep(.cell) {
|
||||
line-height: normal;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.table ::v-deep(.el-table__cell) {
|
||||
padding: 4px 0 !important;
|
||||
}
|
||||
|
||||
.table-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.group-right {
|
||||
width: 220px;
|
||||
border-right: 1px solid #ebeef5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 5px 8px;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.action-label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 16px;
|
||||
}
|
||||
/* 自定义选中行样式 */
|
||||
.table :deep(.selected-row) {
|
||||
background-color: #f0f9ff !important;
|
||||
}
|
||||
|
||||
/* Element Plus 自带的高亮行样式 */
|
||||
.table :deep(.el-table__row.current-row) {
|
||||
background-color: #f0f9ff !important;
|
||||
}
|
||||
|
||||
.table :deep(.el-table__row:hover) {
|
||||
background-color: #f5f7fa !important;
|
||||
}
|
||||
|
||||
/* 高亮当前节点背景色 */
|
||||
.el-tree-node.is-current > .el-tree-node__content {
|
||||
background-color: #e6f7ff !important;
|
||||
color: #1890ff !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,88 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
viewtable: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
b_id: {
|
||||
// 确保 b_id 在 props 里定义
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const sqlStr = ref("");
|
||||
|
||||
function initData() {
|
||||
console.log(props.b_id);
|
||||
|
||||
sqlStr.value = "SQL语句:";
|
||||
|
||||
// if (!props.viewtable) return
|
||||
|
||||
const vrows = props.data || [];
|
||||
const sqlArr = [];
|
||||
|
||||
// 删除语句
|
||||
const deleteSql = `DELETE FROM s_module WHERE b_id='${props.b_id}';`;
|
||||
sqlArr.push(deleteSql);
|
||||
console.log(deleteSql);
|
||||
// 插入语句
|
||||
vrows.forEach((row) => {
|
||||
const insertSql = `
|
||||
INSERT INTO s_module(
|
||||
b_id, b_ChineseName, b_ViewTable, b_SaveTable, b_JudgeCondition,
|
||||
b_Message, b_BMFL, b_Order, b_ISVirtualModule, b_LinkTable,
|
||||
b_DefaultCondition, b_OrderField, b_SH, b_Cate_ID, b_autoid,
|
||||
b_searchsql, b_haveownerid
|
||||
)
|
||||
VALUES(
|
||||
'${props.b_id}', '${row.b_ChineseName}', '${row.b_ViewTable}', '${row.b_SaveTable}', '${row.b_JudgeCondition}',
|
||||
'${row.b_Message}','${row.b_BMFL}', '${row.b_Order}', '${row.b_ISVirtualModule}','${row.b_LinkTable}',
|
||||
'${row.b_DefaultCondition}', '${row.b_OrderField}','${row.b_SH}', '${row.b_Cate_ID}', '${row.b_autoid}',
|
||||
'${row.b_searchsql}', '${row.b_haveownerid}'
|
||||
);
|
||||
`;
|
||||
sqlArr.push(insertSql);
|
||||
});
|
||||
|
||||
// 更新 sqlStr(注意:使用 .value 而不是 this)
|
||||
sqlStr.value = sqlArr.join("\n");
|
||||
// console.log(sqlArr.join('\n'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
title="获取SQL语句"
|
||||
width="1000px"
|
||||
class="dialog"
|
||||
@open="initData"
|
||||
>
|
||||
<div class="sql-preview">
|
||||
{{ sqlStr }}
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.sql-preview {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
font-family: monospace;
|
||||
padding: 8px;
|
||||
border: 1px solid #e0e0e0;
|
||||
background-color: #fafafa;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
File renamed without changes.
@@ -0,0 +1,7 @@
|
||||
<script lang="js" setup></script>
|
||||
|
||||
<template>
|
||||
<div class=""></div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,208 @@
|
||||
<script lang="js" setup>
|
||||
import { loadObjectApi, saveObjectApi } from "@/request/api";
|
||||
import { diffObject } from "@/utils/diff";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const props = defineProps({
|
||||
// 员工ID,用于获取数据
|
||||
employeeId: {
|
||||
type: [String, Number],
|
||||
default: "",
|
||||
},
|
||||
employeeName: {
|
||||
type: [String],
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
|
||||
const showDialog = defineModel(false);
|
||||
|
||||
const isNew = ref(true);
|
||||
|
||||
const maintableOld = ref({});
|
||||
const maintable = ref({});
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
function onOpened() {
|
||||
loadFormData();
|
||||
}
|
||||
|
||||
function loadFormData() {
|
||||
loading.value = true;
|
||||
|
||||
if (!props.employeeId) {
|
||||
ElMessage.warning("加载失败");
|
||||
showDialog.value = false;
|
||||
}
|
||||
|
||||
loadObjectApi({
|
||||
viewName: "v_b_employee",
|
||||
keyField: "b_id",
|
||||
keyValue: props.employeeId,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
if (res.data) {
|
||||
maintable.value = res.data;
|
||||
maintableOld.value = structuredClone(maintable.value);
|
||||
isNew.value = false;
|
||||
} else {
|
||||
maintable.value.b_id = props.employeeId;
|
||||
maintable.value.b_name_ch = props.employeeName || "";
|
||||
|
||||
maintableOld.value = structuredClone(maintable.value);
|
||||
|
||||
isNew.value = true;
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function handleConfirm() {
|
||||
let obj = {};
|
||||
|
||||
if (isNew.value) {
|
||||
maintable.value.b_customer_id = "b_employee";
|
||||
maintable.value.b_state = "草拟";
|
||||
|
||||
obj = maintable.value;
|
||||
} else {
|
||||
const diffResult = diffObject(
|
||||
maintable.value,
|
||||
maintableOld.value,
|
||||
["b_id"],
|
||||
[],
|
||||
);
|
||||
|
||||
if (!diffResult.isChanged) {
|
||||
ElMessage.success("保存成功");
|
||||
|
||||
loadFormData();
|
||||
return;
|
||||
}
|
||||
|
||||
obj = diffResult.result;
|
||||
}
|
||||
|
||||
const requestData = [
|
||||
{
|
||||
tableName: "b_contact",
|
||||
keyField: "b_id",
|
||||
inserts: isNew.value ? [obj] : [],
|
||||
updates: isNew.value ? [] : [obj],
|
||||
deletes: [],
|
||||
},
|
||||
];
|
||||
|
||||
loading.value = true;
|
||||
|
||||
saveObjectApi(requestData)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("保存成功");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
maintable.value = {};
|
||||
maintableOld.value = {};
|
||||
|
||||
loading.value = true;
|
||||
isNew.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
title="员工资料"
|
||||
width="400"
|
||||
v-model="showDialog"
|
||||
@closed="resetForm"
|
||||
@opened="onOpened"
|
||||
>
|
||||
<div style="width: 100%" v-loading="loading">
|
||||
<G3Grid>
|
||||
<G3GridGroup :label-width="80" :column="1" label-align="right">
|
||||
<G3GridItem label="账号" :span="1" required>
|
||||
<el-input v-model="maintable.b_id" disabled />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="姓名" :span="1">
|
||||
<el-input v-model="maintable.b_name_ch" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="手机电话" :span="1">
|
||||
<el-input v-model="maintable.b_phone" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="QQ" :span="1">
|
||||
<el-input v-model="maintable.b_fax" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="邮箱" :span="1">
|
||||
<el-input v-model="maintable.b_email" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="性别" :span="1">
|
||||
<el-select v-model="maintable.b_sex">
|
||||
<el-option label="男" value="男" />
|
||||
<el-option label="女" value="女" />
|
||||
</el-select>
|
||||
</G3GridItem>
|
||||
<G3GridItem label="部门" :span="1">
|
||||
<el-input v-model="maintable.b_departmentname" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="职位" :span="1">
|
||||
<el-input v-model="maintable.b_duty" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="合同开始" :span="1">
|
||||
<el-date-picker
|
||||
v-model="maintable.b_htbegindate"
|
||||
type="date"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</G3GridItem>
|
||||
<G3GridItem label="合同到期" :span="1">
|
||||
<el-date-picker
|
||||
v-model="maintable.b_htenddate"
|
||||
type="date"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</G3GridItem>
|
||||
<G3GridItem label="紧急电话" :span="1">
|
||||
<el-input v-model="maintable.b_homephone" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="详细地址" :span="1">
|
||||
<el-input v-model="maintable.b_homeaddr" type="textarea" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="备注" :span="1">
|
||||
<el-input v-model="maintable.b_bz" type="textarea" />
|
||||
</G3GridItem>
|
||||
</G3GridGroup>
|
||||
</G3Grid>
|
||||
|
||||
<div class="footer">
|
||||
<el-button @click="showDialog = false">取消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="handleConfirm">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.footer {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
@@ -4,6 +4,7 @@ import { useUserStore } from "@/store/user";
|
||||
import { diffObject } from "@/utils/diff";
|
||||
import PermissionModel from "./permission-model.vue";
|
||||
import PermissionCopyModal from "./permission-copy-modal.vue";
|
||||
import Employee from "./employee.vue";
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
@@ -38,6 +39,9 @@ const dialogData = ref({});
|
||||
// 复制权限
|
||||
const copyDialogVisible = ref(false);
|
||||
|
||||
// 员工资料
|
||||
const showEmployee = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
getTableData();
|
||||
});
|
||||
@@ -205,7 +209,9 @@ async function deleteRows(rows = []) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedIds = rows.filter((row) => row.b_id !== "g3soft").map((row) => row.b_id);
|
||||
const selectedIds = rows
|
||||
.filter((row) => row.b_id !== "g3soft")
|
||||
.map((row) => row.b_id);
|
||||
|
||||
const delectedIds = rows
|
||||
.filter((row) => !$table.isInsertByRow(row) && row.b_id !== "g3soft")
|
||||
@@ -274,7 +280,8 @@ async function handleSaveRow(row) {
|
||||
|
||||
// 1. 检查表格内重复(本地数据)
|
||||
const duplicate = tableData.value.some(
|
||||
(item) => item.b_id?.trim().toUpperCase() === editId && item.b_id !== row.b_id, // 排除自己
|
||||
(item) =>
|
||||
item.b_id?.trim().toUpperCase() === editId && item.b_id !== row.b_id, // 排除自己
|
||||
);
|
||||
if (duplicate) {
|
||||
ElMessage.warning("账号重复,请修改");
|
||||
@@ -385,7 +392,9 @@ function handleShowBatchCopyDialog() {
|
||||
if ($table) {
|
||||
const records = $table.getCheckboxRecords();
|
||||
|
||||
const filteredRecords = records.filter((item) => !$table.isInsertByRow(item));
|
||||
const filteredRecords = records.filter(
|
||||
(item) => !$table.isInsertByRow(item),
|
||||
);
|
||||
|
||||
const bIds = filteredRecords.map((item) => item.b_id);
|
||||
|
||||
@@ -416,12 +425,26 @@ function handleShowBatchCopyDialog() {
|
||||
</el-icon>
|
||||
批量删除
|
||||
</el-button>
|
||||
<el-button type="primary" @click="showPasswordRow = !showPasswordRow">查看密码</el-button>
|
||||
<el-button type="primary" @click="handleShowBatchCopyDialog">批量复制权限</el-button>
|
||||
<el-button type="primary" @click="handleShowCopyDialog" :disabled="!isSelectedRow">
|
||||
<el-button type="primary" @click="showPasswordRow = !showPasswordRow"
|
||||
>查看密码</el-button
|
||||
>
|
||||
<el-button type="primary" @click="handleShowBatchCopyDialog"
|
||||
>批量复制权限</el-button
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleShowCopyDialog"
|
||||
:disabled="!isSelectedRow"
|
||||
>
|
||||
复制权限
|
||||
</el-button>
|
||||
<el-button type="primary" :disabled="!isSelectedRow">员工资料</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!isSelectedRow"
|
||||
@click="showEmployee = true"
|
||||
>
|
||||
员工资料
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="header-right">
|
||||
@@ -538,10 +561,18 @@ function handleShowBatchCopyDialog() {
|
||||
}"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-checkbox v-model="row.b_canuse" true-value="1" false-value="0" />
|
||||
<el-checkbox
|
||||
v-model="row.b_canuse"
|
||||
true-value="1"
|
||||
false-value="0"
|
||||
/>
|
||||
</template>
|
||||
<template #edit="{ row }">
|
||||
<el-checkbox v-model="row.b_canuse" true-value="1" false-value="0" />
|
||||
<el-checkbox
|
||||
v-model="row.b_canuse"
|
||||
true-value="1"
|
||||
false-value="0"
|
||||
/>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
@@ -585,7 +616,12 @@ function handleShowBatchCopyDialog() {
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
<el-button link @click="cancelEditRow(row)" @click.stop style="margin-left: 6px">
|
||||
<el-button
|
||||
link
|
||||
@click="cancelEditRow(row)"
|
||||
@click.stop
|
||||
style="margin-left: 6px"
|
||||
>
|
||||
取消
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -639,6 +675,11 @@ function handleShowBatchCopyDialog() {
|
||||
|
||||
<PermissionModel v-model="dialogVisible" :data="dialogData" />
|
||||
<PermissionCopyModal v-model="copyDialogVisible" :ids="selectedIds" />
|
||||
<Employee
|
||||
v-model="showEmployee"
|
||||
:employee-id="selectCurrentRow?.b_id"
|
||||
:employee-name="selectCurrentRow?.b_name"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
<script lang="js" setup>
|
||||
import { loadObjectApi, saveObjectApi } from "@/request/api";
|
||||
import { useUserStore } from "@/store/user";
|
||||
import { diffObject } from "@/utils/diff";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
const isNew = ref(true);
|
||||
|
||||
const maintableOld = ref({});
|
||||
const maintable = ref({});
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const userid = ref(userStore.userInfo.userid);
|
||||
|
||||
onMounted(() => {
|
||||
loadFormData();
|
||||
});
|
||||
|
||||
function loadFormData() {
|
||||
loading.value = true;
|
||||
|
||||
loadObjectApi({
|
||||
viewName: "v_b_employee",
|
||||
keyField: "b_id",
|
||||
keyValue: userid.value,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
if (res.data) {
|
||||
maintable.value = res.data;
|
||||
maintableOld.value = structuredClone(maintable.value);
|
||||
isNew.value = false;
|
||||
} else {
|
||||
maintable.value.b_id = userid.value;
|
||||
maintable.value.b_name_ch = userStore.userInfo.username || "";
|
||||
|
||||
maintableOld.value = structuredClone(maintable.value);
|
||||
|
||||
isNew.value = true;
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function handleConfirm() {
|
||||
let obj = {};
|
||||
|
||||
if (isNew.value) {
|
||||
maintable.value.b_customer_id = "b_employee";
|
||||
maintable.value.b_state = "草拟";
|
||||
|
||||
obj = maintable.value;
|
||||
} else {
|
||||
const diffResult = diffObject(
|
||||
maintable.value,
|
||||
maintableOld.value,
|
||||
["b_id"],
|
||||
[],
|
||||
);
|
||||
|
||||
if (!diffResult.isChanged) {
|
||||
ElMessage.success("保存成功");
|
||||
|
||||
loadFormData();
|
||||
return;
|
||||
}
|
||||
|
||||
obj = diffResult.result;
|
||||
}
|
||||
|
||||
const requestData = [
|
||||
{
|
||||
tableName: "b_contact",
|
||||
keyField: "b_id",
|
||||
inserts: isNew.value ? [obj] : [],
|
||||
updates: isNew.value ? [] : [obj],
|
||||
deletes: [],
|
||||
},
|
||||
];
|
||||
|
||||
loading.value = true;
|
||||
|
||||
saveObjectApi(requestData)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("保存成功");
|
||||
isNew.value = false;
|
||||
loadFormData();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="width: 360px" v-loading="loading">
|
||||
<G3Grid>
|
||||
<G3GridGroup :label-width="80" :column="1" label-align="right">
|
||||
<G3GridItem label="账号" :span="1" required>
|
||||
<el-input v-model="maintable.b_id" disabled />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="姓名" :span="1">
|
||||
<el-input v-model="maintable.b_name_ch" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="手机电话" :span="1">
|
||||
<el-input v-model="maintable.b_phone" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="QQ" :span="1">
|
||||
<el-input v-model="maintable.b_fax" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="邮箱" :span="1">
|
||||
<el-input v-model="maintable.b_email" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="性别" :span="1">
|
||||
<el-select v-model="maintable.b_sex">
|
||||
<el-option label="男" value="男" />
|
||||
<el-option label="女" value="女" />
|
||||
</el-select>
|
||||
</G3GridItem>
|
||||
<G3GridItem label="部门" :span="1">
|
||||
<el-input v-model="maintable.b_departmentname" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="职位" :span="1">
|
||||
<el-input v-model="maintable.b_duty" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="合同开始" :span="1">
|
||||
<el-date-picker
|
||||
v-model="maintable.b_htbegindate"
|
||||
type="date"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</G3GridItem>
|
||||
<G3GridItem label="合同到期" :span="1">
|
||||
<el-date-picker
|
||||
v-model="maintable.b_htenddate"
|
||||
type="date"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</G3GridItem>
|
||||
<G3GridItem label="紧急电话" :span="1">
|
||||
<el-input v-model="maintable.b_homephone" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="详细地址" :span="1">
|
||||
<el-input v-model="maintable.b_homeaddr" type="textarea" />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="备注" :span="1">
|
||||
<el-input v-model="maintable.b_bz" type="textarea" />
|
||||
</G3GridItem>
|
||||
</G3GridGroup>
|
||||
</G3Grid>
|
||||
|
||||
<div class="footer">
|
||||
<el-button :loading="loading">取消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="handleConfirm">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.footer {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,25 @@
|
||||
<script lang="js" setup>
|
||||
import UpdatePassword from "./update-password.vue";
|
||||
import Employee from "./employee.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-tabs tab-position="left" class="tabs">
|
||||
<el-tab-pane label="个人信息">
|
||||
<Employee />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="修改密码">
|
||||
<UpdatePassword />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tabs {
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
:deep(.el-tabs__content) {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,8 @@
|
||||
<script setup>
|
||||
<script lang="js" setup>
|
||||
import { updatePasswordApi } from "@/request/api";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
import { ref } from "vue";
|
||||
|
||||
const loading = ref(false);
|
||||
const confirmLoading = ref(false);
|
||||
|
||||
const form = ref({
|
||||
oldPassword: "",
|
||||
@@ -11,115 +10,75 @@ const form = ref({
|
||||
confirmPassword: "",
|
||||
});
|
||||
|
||||
function resetFrom() {
|
||||
form.value.oldPassword = "";
|
||||
form.value.newPassword = "";
|
||||
form.value.confirmPassword = "";
|
||||
}
|
||||
|
||||
async function handleConfirm() {
|
||||
if (form.value.newPassword !== form.value.confirmPassword) {
|
||||
function handleConfirm() {
|
||||
if (form.value.newPassword === "") {
|
||||
ElMessage.warning("新密码不能为空");
|
||||
return;
|
||||
}
|
||||
if (form.value.confirmPassword !== form.value.newPassword) {
|
||||
ElMessage.warning("新密码和确认密码不一致");
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
confirmLoading.value = true;
|
||||
|
||||
const requestData = {
|
||||
oldPassword: form.value.oldPassword,
|
||||
updatePasswordApi({
|
||||
newPassword: form.value.newPassword,
|
||||
};
|
||||
|
||||
try {
|
||||
const saveRes = await axios.post("/updatePassword", requestData, {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
oldPassword: form.value.oldPassword,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
// 修改成功
|
||||
ElMessage.success("修改成功");
|
||||
resetForm();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
confirmLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
if (saveRes.data.success) {
|
||||
ElMessage.success("密码更新成功");
|
||||
resetFrom();
|
||||
} else {
|
||||
ElMessage.error(`密码更新失败:${saveRes.data.message}`);
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(`密码更新失败:${error.message || "未知错误"}`);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
function resetForm() {
|
||||
form.value.newPassword = "";
|
||||
form.value.oldPassword = "";
|
||||
form.value.confirmPassword = "";
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="g3-container">
|
||||
<div class="g3-grid">
|
||||
<div class="g3-cell">
|
||||
<div class="g3-cell-label">旧密码:</div>
|
||||
<el-input v-model="form.oldPassword" type="password" />
|
||||
</div>
|
||||
<div class="g3-cell">
|
||||
<div class="g3-cell-label">新密码:</div>
|
||||
<div class="g3-cell-value">
|
||||
<el-input v-model="form.newPassword" type="password" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="g3-cell">
|
||||
<div class="g3-cell-label">确认密码:</div>
|
||||
<el-input v-model="form.confirmPassword" type="password" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="g3-actions">
|
||||
<el-button type="primary" :loading="loading" @click="handleConfirm"> 确认 </el-button>
|
||||
<el-button :loading="loading" @click="resetFrom"> 重置 </el-button>
|
||||
<div style="width: 360px">
|
||||
<G3Grid>
|
||||
<G3GridGroup :label-width="80" :column="1" label-align="right">
|
||||
<G3GridItem label="旧密码" :span="1">
|
||||
<el-input v-model="form.oldPassword" type="password" show-password />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="新密码" :span="1" required>
|
||||
<el-input v-model="form.newPassword" type="password" show-password />
|
||||
</G3GridItem>
|
||||
<G3GridItem label="确认密码" :span="1" required>
|
||||
<el-input
|
||||
v-model="form.confirmPassword"
|
||||
type="password"
|
||||
show-password
|
||||
/>
|
||||
</G3GridItem>
|
||||
</G3GridGroup>
|
||||
</G3Grid>
|
||||
|
||||
<div class="footer">
|
||||
<el-button @click="resetForm">取消</el-button>
|
||||
<el-button type="primary" :loading="confirmLoading" @click="handleConfirm"
|
||||
>确认</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.g3-container {
|
||||
padding: 16px;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.g3-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
gap: 1px;
|
||||
border: 1px solid #ddd;
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.g3-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.g3-cell-label {
|
||||
padding: 8px;
|
||||
border-right: 1px solid #ddd;
|
||||
.footer {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
width: 95px;
|
||||
flex-shrink: 0;
|
||||
background: #f9f9f9;
|
||||
height: 100%;
|
||||
}
|
||||
.g3-cell-value {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.g3-actions {
|
||||
display: flex;
|
||||
margin-top: 12px;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
:deep(.el-input__wrapper) {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
border-radius: 0px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user