u
This commit is contained in:
1 parent
c330e52fd3
commit
87d63b6542
341 files changed
+128714
-48
No files matched your search
@@ -10,4 +10,4 @@ PUBLIC_SERVER_PORT=3000
|
||||
|
||||
# API
|
||||
# 后端 API 基础地址
|
||||
PUBLIC_API_BASE_URL=http://127.0.0.1:8084/api
|
||||
PUBLIC_API_BASE_URL=http://127.0.0.1:8085/api
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"editor.cursorSmoothCaretAnimation": "on",
|
||||
"oxc.fmt.configPath": ".oxfmtrc.json",
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
|
||||
+9
@@ -22,11 +22,17 @@ declare module 'vue' {
|
||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElSplitter: typeof import('element-plus/es')['ElSplitter']
|
||||
ElSplitterPanel: typeof import('element-plus/es')['ElSplitterPanel']
|
||||
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||
ElText: typeof import('element-plus/es')['ElText']
|
||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||
ElTree: typeof import('element-plus/es')['ElTree']
|
||||
@@ -42,4 +48,7 @@ declare module 'vue' {
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
export interface GlobalDirectives {
|
||||
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,6 @@
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^3.0.4",
|
||||
"pinia-plugin-persistedstate": "^4.7.1",
|
||||
"unplugin-auto-import": "^21.0.0",
|
||||
"unplugin-vue-components": "^31.0.0",
|
||||
"uuid": "^13.0.0",
|
||||
"vue": "^3.5.26",
|
||||
"vue-router": "^5.0.3",
|
||||
@@ -37,6 +35,8 @@
|
||||
"@rsbuild/plugin-vue-jsx": "^1.1.2",
|
||||
"oxfmt": "^0.40.0",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"unplugin-icons": "^23.0.1"
|
||||
"unplugin-icons": "^23.0.1",
|
||||
"unplugin-auto-import": "^21.0.0",
|
||||
"unplugin-vue-components": "^31.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
<template>
|
||||
<router-view />
|
||||
<el-config-provider :locale="zhCn">
|
||||
<router-view />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script lang="js" setup></script>
|
||||
<script lang="js" setup>
|
||||
import { ElConfigProvider } from "element-plus";
|
||||
import zhCn from "element-plus/es/locale/lang/zh-cn";
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -183,8 +183,6 @@ function _fetchOptions(searchTitle) {
|
||||
: condition;
|
||||
}
|
||||
|
||||
if (!inputChange.value) searchCondition = "";
|
||||
|
||||
loadPageDataApi({
|
||||
page: pagination.page,
|
||||
size: pagination.size,
|
||||
|
||||
@@ -44,6 +44,10 @@ const menuList = ref([
|
||||
label: "字段配置",
|
||||
path: "/system/field",
|
||||
},
|
||||
{
|
||||
label: "权限配置",
|
||||
path: "/system/permission",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
@@ -151,6 +155,7 @@ function handleMenuChange(val) {
|
||||
}
|
||||
.el-menu-item {
|
||||
padding-left: 48px !important;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
&.is-active {
|
||||
.el-sub-menu__title {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
<script lang="js" setup>
|
||||
import { useUserStore } from "@/store/user";
|
||||
import { useTabStore } from "@/store/tab";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const props = defineProps({
|
||||
collapse: {
|
||||
type: Boolean,
|
||||
@@ -8,9 +13,23 @@ const props = defineProps({
|
||||
|
||||
const emits = defineEmits(["collapse"]);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const tabStore = useTabStore();
|
||||
const userStore = useUserStore();
|
||||
|
||||
function onCollapse() {
|
||||
emits("collapse");
|
||||
}
|
||||
|
||||
function logout() {
|
||||
userStore.resetUserInfo();
|
||||
tabStore.resetTabs();
|
||||
|
||||
ElMessage.success("已成功退出");
|
||||
|
||||
router.push("/login");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -18,24 +37,29 @@ function onCollapse() {
|
||||
<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>
|
||||
|
||||
<div class="g3-topbar-right">
|
||||
<div class="user-card compact">
|
||||
<G3Icon icon="lucide:circle-user-round" size="18" />
|
||||
<div class="user-info">
|
||||
<span class="user-name">001</span>
|
||||
<!-- <span class="divider">·</span>
|
||||
<span class="user-org">g3cd</span> -->
|
||||
<el-dropdown>
|
||||
<div class="user-card compact">
|
||||
<G3Icon icon="lucide:circle-user-round" size="18" />
|
||||
<div class="user-info">
|
||||
<span class="user-name">
|
||||
{{ userStore.userInfo.username || userStore.userInfo.userid || "空" }}
|
||||
</span>
|
||||
</div>
|
||||
</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-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -33,6 +33,9 @@ VxeUITable.setConfig({
|
||||
footerCellConfig: {
|
||||
height: 32,
|
||||
},
|
||||
columnDragConfig: {
|
||||
showIcon: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -30,6 +30,14 @@ export const routes = [
|
||||
title: "字段配置",
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/system/permission",
|
||||
component: () => import("@/views/system/permission/index.vue"),
|
||||
meta: {
|
||||
title: "权限管理",
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -20,6 +20,7 @@ export const useUserStore = defineStore(
|
||||
|
||||
function resetUserInfo() {
|
||||
userInfo.value.token = "";
|
||||
userInfo.value.username = "";
|
||||
}
|
||||
|
||||
function isLogined() {
|
||||
|
||||
@@ -19,7 +19,3 @@
|
||||
.el-button--large {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.el-button + .el-button {
|
||||
margin-left: 0px ;
|
||||
}
|
||||
@@ -1,23 +1,26 @@
|
||||
.vxe-table {
|
||||
.el-checkbox {
|
||||
height: 100%;
|
||||
}
|
||||
.row--current {
|
||||
.col--edit {
|
||||
.vxe-cell {
|
||||
padding: 1px !important;
|
||||
|
||||
.col--edit {
|
||||
.vxe-cell {
|
||||
padding: 1px !important;
|
||||
.vxe-cell--wrapper {
|
||||
height: 100%;
|
||||
|
||||
.el-input,
|
||||
.el-select {
|
||||
.vxe-cell--wrapper {
|
||||
height: 100%;
|
||||
line-height: 100%;
|
||||
|
||||
.el-input__wrapper,
|
||||
.el-select__wrapper {
|
||||
.el-input,
|
||||
.el-select {
|
||||
height: 100%;
|
||||
line-height: 100%;
|
||||
|
||||
.el-input__wrapper,
|
||||
.el-select__wrapper {
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.el-checkbox {
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,12 +694,12 @@ function handleCheckColumns() {
|
||||
|
||||
.content-head-left {
|
||||
display: flex;
|
||||
@include flexGapWrap(4px, 8px);
|
||||
@include flexGapWrap(4px, 0px);
|
||||
}
|
||||
|
||||
.content-head-right {
|
||||
display: flex;
|
||||
@include flexGapWrap(4px, 8px);
|
||||
@include flexGapWrap(4px, 0px);
|
||||
flex-wrap: nowrap;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,700 @@
|
||||
<script lang="js" setup>
|
||||
import { loadPageDataApi, saveObjectApi, loadDataApi } from "@/request/api";
|
||||
import { useUserStore } from "@/store/user";
|
||||
import { diffObject } from "@/utils/diff";
|
||||
import PermissionModel from "./permission-model.vue";
|
||||
import PermissionCopyModal from "./permission-copy-modal.vue";
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
const loading = reactive({
|
||||
table: false,
|
||||
save: false,
|
||||
});
|
||||
|
||||
const tableRef = ref();
|
||||
const tableData = ref([]);
|
||||
|
||||
let oldRowData = {};
|
||||
const selectCurrentRow = ref(null);
|
||||
const isSelectedRow = computed(() => selectCurrentRow.value !== null);
|
||||
|
||||
const selectedIds = ref([]);
|
||||
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
size: 20,
|
||||
total: 0,
|
||||
});
|
||||
const searchForm = ref({
|
||||
name: "",
|
||||
});
|
||||
const showPasswordRow = ref(false);
|
||||
|
||||
// 授予权限
|
||||
const dialogVisible = ref(false);
|
||||
const dialogData = ref({});
|
||||
|
||||
// 复制权限
|
||||
const copyDialogVisible = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
getTableData();
|
||||
});
|
||||
|
||||
function getTableData() {
|
||||
loading.table = true;
|
||||
|
||||
const userid = userStore.userInfo.userid;
|
||||
|
||||
let searchCondition = "";
|
||||
|
||||
if (searchForm.value.name !== "") {
|
||||
searchCondition = ` (b_id LIKE '%${searchForm.value.name}%' OR b_name LIKE '%${searchForm.value.name}%')`;
|
||||
}
|
||||
|
||||
if (userid !== "g3soft") {
|
||||
searchCondition += `AND b_id != 'g3soft' `;
|
||||
}
|
||||
|
||||
loadPageDataApi({
|
||||
page: pagination.page,
|
||||
size: pagination.size,
|
||||
viewName: "b_user",
|
||||
searchCondition,
|
||||
orderField: "b_id asc",
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
tableData.value = res.data.rows;
|
||||
pagination.total = res.data.total;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.table = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 分页变化
|
||||
function handleSizeChange(val) {
|
||||
pagination.page = val;
|
||||
|
||||
getTableData();
|
||||
}
|
||||
function handleCurrentChange(val) {
|
||||
pagination.size = val;
|
||||
|
||||
getTableData();
|
||||
}
|
||||
|
||||
// 进入单行编辑
|
||||
async function startEditRow(row) {
|
||||
const $table = tableRef.value;
|
||||
if ($table) {
|
||||
oldRowData = JSON.parse(JSON.stringify(row));
|
||||
await $table.setEditRow(row);
|
||||
await $table.setCurrentRow(row);
|
||||
|
||||
selectCurrentRow.value = row;
|
||||
}
|
||||
}
|
||||
|
||||
// 取消单行编辑
|
||||
async function cancelEditRow(row) {
|
||||
const $table = tableRef.value;
|
||||
if ($table) {
|
||||
await $table.clearEdit();
|
||||
// 还原数据
|
||||
await $table.revertData(row);
|
||||
|
||||
selectCurrentRow.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否在编辑态
|
||||
function isRowEditing(row) {
|
||||
const $table = tableRef.value;
|
||||
if ($table) {
|
||||
return $table.isEditByRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑关闭事件
|
||||
async function handleEditClosed({ row, rowIndex, column, columnIndex }) {
|
||||
const $table = tableRef.value;
|
||||
if ($table) {
|
||||
await $table.clearEdit();
|
||||
// 还原数据
|
||||
await $table.revertData(row);
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑激活事件
|
||||
function handleEditActivated({ row }) {
|
||||
startEditRow(row);
|
||||
}
|
||||
|
||||
// 保存本地提交
|
||||
async function commitEditRow(row) {
|
||||
const $table = tableRef.value;
|
||||
if ($table) {
|
||||
// 重新加载行
|
||||
await $table.reloadRow(row, {});
|
||||
await $table.clearEdit();
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAddRow() {
|
||||
const $table = tableRef.value;
|
||||
|
||||
if ($table) {
|
||||
if ($table.getInsertRecords().length > 0) {
|
||||
$table.setEditRow($table.getInsertRecords()[0], true);
|
||||
return;
|
||||
}
|
||||
|
||||
const record = {
|
||||
b_canuse: "1",
|
||||
b_paylevel: "5",
|
||||
b_receivelevel: "5",
|
||||
b_logintime: "1",
|
||||
b_id: "",
|
||||
b_name: "",
|
||||
b_department: "",
|
||||
b_salesman: "0",
|
||||
b_dealman: "0",
|
||||
b_docman: "0",
|
||||
b_password: "",
|
||||
b_subcode: "",
|
||||
b_bz: "",
|
||||
b_levelvisible: "",
|
||||
};
|
||||
|
||||
const { row: newRow } = await $table.insertAt(record, -1);
|
||||
$table.setEditRow(newRow, true);
|
||||
$table.setCurrentRow(newRow);
|
||||
}
|
||||
}
|
||||
|
||||
// 删除单行
|
||||
async function handleDeleteRow(row) {
|
||||
if (!row) return;
|
||||
const $table = tableRef.value;
|
||||
|
||||
if ($table) {
|
||||
deleteRows([row]);
|
||||
}
|
||||
}
|
||||
|
||||
// 删除多选行
|
||||
async function handleDeleteRows() {
|
||||
const $table = tableRef.value;
|
||||
if ($table) {
|
||||
deleteRows($table.getCheckboxRecords());
|
||||
}
|
||||
}
|
||||
|
||||
// 删除(传入行数组)
|
||||
async function deleteRows(rows = []) {
|
||||
const $table = tableRef.value;
|
||||
if (!$table) {
|
||||
return;
|
||||
}
|
||||
if (!rows.length) {
|
||||
ElMessage.warning("请先选择要删除的行");
|
||||
return;
|
||||
}
|
||||
|
||||
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")
|
||||
.map((row) => row.b_id);
|
||||
|
||||
const tipMessage = `确定要删除以下账号吗?<br/><strong>${selectedIds.join(", ")}</strong>`;
|
||||
|
||||
await ElMessageBox.confirm(tipMessage, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
dangerouslyUseHTMLString: true,
|
||||
});
|
||||
|
||||
if (delectedIds.length === 0) {
|
||||
ElMessage.success("删除成功");
|
||||
|
||||
getTableData();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const requestData = [
|
||||
{
|
||||
tableName: "b_user",
|
||||
keyField: "b_id",
|
||||
inserts: [],
|
||||
updates: [],
|
||||
deletes: delectedIds.map((id) => ({ b_id: id })),
|
||||
},
|
||||
{
|
||||
tableName: "b_user_qx",
|
||||
keyField: "father",
|
||||
deletes: delectedIds.map((id) => ({ father: id })),
|
||||
inserts: [],
|
||||
updates: [],
|
||||
},
|
||||
{
|
||||
tableName: "b_user_power",
|
||||
keyField: "userid",
|
||||
deletes: delectedIds.map((id) => ({ userid: id })),
|
||||
inserts: [],
|
||||
updates: [],
|
||||
},
|
||||
];
|
||||
|
||||
await saveObjectApi(requestData).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
|
||||
getTableData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 保存行(新增或更新)
|
||||
async function handleSaveRow(row) {
|
||||
const $table = tableRef.value;
|
||||
const isNew = $table.isInsertByRow(row);
|
||||
|
||||
const editId = row.b_id.trim().toUpperCase() || "";
|
||||
if (!editId) {
|
||||
ElMessage.warning("账号不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 检查表格内重复(本地数据)
|
||||
const duplicate = tableData.value.some(
|
||||
(item) => item.b_id?.trim().toUpperCase() === editId && item.b_id !== row.b_id, // 排除自己
|
||||
);
|
||||
if (duplicate) {
|
||||
ElMessage.warning("账号重复,请修改");
|
||||
return;
|
||||
}
|
||||
|
||||
loading.save = true;
|
||||
|
||||
try {
|
||||
// 2. 检查后端重复
|
||||
let whereClause = `b_id = '${editId}'`;
|
||||
if (!isNew) {
|
||||
// 更新时排除当前记录
|
||||
whereClause += ` AND b_id <> '${editId || ""}'`;
|
||||
}
|
||||
|
||||
const idResponse = await loadDataApi("b_user", whereClause);
|
||||
|
||||
if (idResponse.success && idResponse.data.length > 0) {
|
||||
ElMessage.warning("编号重复,请修改");
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 组装请求
|
||||
let requestData = [];
|
||||
if (isNew) {
|
||||
// 新增
|
||||
const editedData = { ...toRaw(row) };
|
||||
|
||||
delete editedData._X_ROW_KEY;
|
||||
|
||||
requestData = [
|
||||
{
|
||||
tableName: "b_user",
|
||||
keyField: "b_id",
|
||||
inserts: [editedData],
|
||||
updates: [],
|
||||
deletes: [],
|
||||
},
|
||||
];
|
||||
} else {
|
||||
// 更新
|
||||
const diffResult = diffObject(row, oldRowData, ["b_id"], []);
|
||||
|
||||
if (!diffResult.isChanged) {
|
||||
ElMessage.info("没有数据变更");
|
||||
return;
|
||||
}
|
||||
|
||||
requestData = [
|
||||
{
|
||||
tableName: "b_user",
|
||||
keyField: "b_id",
|
||||
inserts: [],
|
||||
updates: [diffResult.result],
|
||||
deletes: [],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const saveRes = await saveObjectApi(requestData);
|
||||
|
||||
if (saveRes.success) {
|
||||
ElMessage.success("保存成功");
|
||||
|
||||
const $table = tableRef.value;
|
||||
if ($table) {
|
||||
if (isNew) {
|
||||
const currentData = tableData.value;
|
||||
currentData.push(row);
|
||||
|
||||
commitEditRow(row);
|
||||
} else {
|
||||
commitEditRow(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
loading.save = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleShowDialog(row) {
|
||||
dialogVisible.value = true;
|
||||
dialogData.value = row;
|
||||
}
|
||||
|
||||
function handleShowCopyDialog() {
|
||||
const $table = tableRef.value;
|
||||
if ($table) {
|
||||
if (!selectCurrentRow.value) {
|
||||
ElMessage.warning("请选中需要复制的行");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($table.isInsertByRow(selectCurrentRow.value)) {
|
||||
ElMessage.warning("请保存后再复制权限");
|
||||
return;
|
||||
}
|
||||
|
||||
selectedIds.value = [selectCurrentRow.value.b_id];
|
||||
copyDialogVisible.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleShowBatchCopyDialog() {
|
||||
const $table = tableRef.value;
|
||||
if ($table) {
|
||||
const records = $table.getCheckboxRecords();
|
||||
|
||||
const filteredRecords = records.filter((item) => !$table.isInsertByRow(item));
|
||||
|
||||
const bIds = filteredRecords.map((item) => item.b_id);
|
||||
|
||||
if (bIds.length === 0) {
|
||||
ElMessage.warning("请选择需要复制的用户");
|
||||
return;
|
||||
}
|
||||
|
||||
selectedIds.value = bIds;
|
||||
copyDialogVisible.value = true;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="g3-container">
|
||||
<div class="header">
|
||||
<div class="header-left">
|
||||
<el-button type="primary" @click="handleAddRow()">
|
||||
<el-icon class="el-icon--left">
|
||||
<G3Icon icon="lucide:plus" />
|
||||
</el-icon>
|
||||
新建
|
||||
</el-button>
|
||||
<el-button type="danger" @click="handleDeleteRows()">
|
||||
<el-icon class="el-icon--left">
|
||||
<G3Icon icon="lucide:trash-2" />
|
||||
</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>
|
||||
<el-button type="primary" :disabled="!isSelectedRow">员工资料</el-button>
|
||||
</div>
|
||||
|
||||
<div class="header-right">
|
||||
<el-input placeholder="搜索账号、用户名" v-model="searchForm.name" />
|
||||
<el-button type="primary" @click="getTableData()">
|
||||
<el-icon class="el-icon--left">
|
||||
<G3Icon icon="lucide:search" />
|
||||
</el-icon>
|
||||
搜索
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<vxe-table
|
||||
ref="tableRef"
|
||||
border
|
||||
stripe
|
||||
header-align="center"
|
||||
keep-source
|
||||
:loading="loading.table"
|
||||
:data="tableData"
|
||||
:sort-config="{
|
||||
trigger: 'cell',
|
||||
}"
|
||||
:row-config="{
|
||||
useKey: true,
|
||||
// keyField: 'b_id',
|
||||
isCurrent: true,
|
||||
isHover: true,
|
||||
}"
|
||||
:column-config="{
|
||||
resizable: true,
|
||||
drag: true,
|
||||
}"
|
||||
:edit-config="{
|
||||
trigger: 'click',
|
||||
mode: 'row',
|
||||
autoClear: false,
|
||||
showStatus: true,
|
||||
showIcon: false,
|
||||
}"
|
||||
height="100%"
|
||||
show-overflow
|
||||
show-header-overflow
|
||||
@edit-closed="handleEditClosed"
|
||||
@edit-activated="handleEditActivated"
|
||||
>
|
||||
<vxe-column type="checkbox" width="50" align="center" />
|
||||
<vxe-column
|
||||
field="b_id"
|
||||
title="账号"
|
||||
align="center"
|
||||
sortable
|
||||
width="100"
|
||||
:edit-render="{
|
||||
autoFocus: true,
|
||||
}"
|
||||
>
|
||||
<template #edit="{ row }">
|
||||
<el-input v-model="row.b_id" size="small" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="b_name"
|
||||
title="用户名"
|
||||
align="center"
|
||||
sortable
|
||||
width="100"
|
||||
:edit-render="{
|
||||
autoFocus: true,
|
||||
}"
|
||||
>
|
||||
<template #edit="{ row }">
|
||||
<el-input v-model="row.b_name" size="small" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="b_department"
|
||||
title="部门"
|
||||
align="center"
|
||||
sortable
|
||||
width="100"
|
||||
:edit-render="{
|
||||
autoFocus: true,
|
||||
}"
|
||||
>
|
||||
<template #edit="{ row }">
|
||||
<el-input v-model="row.b_department" size="small" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="b_subcode"
|
||||
title="公司"
|
||||
align="center"
|
||||
sortable
|
||||
width="100"
|
||||
:edit-render="{
|
||||
autoFocus: true,
|
||||
}"
|
||||
>
|
||||
<template #edit="{ row }">
|
||||
<el-input v-model="row.b_subcode" size="small" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="b_canuse"
|
||||
title="启用"
|
||||
align="center"
|
||||
sortable
|
||||
width="100"
|
||||
:edit-render="{
|
||||
autoFocus: true,
|
||||
}"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<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" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
v-if="showPasswordRow"
|
||||
field="b_password"
|
||||
title="密码"
|
||||
align="center"
|
||||
sortable
|
||||
width="100"
|
||||
:edit-render="{
|
||||
autoFocus: true,
|
||||
}"
|
||||
>
|
||||
<template #edit="{ row }">
|
||||
<el-input v-model="row.b_password" size="small" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="b_bz"
|
||||
title="备注"
|
||||
align="center"
|
||||
sortable
|
||||
width="100"
|
||||
:edit-render="{
|
||||
autoFocus: true,
|
||||
}"
|
||||
>
|
||||
<template #edit="{ row }">
|
||||
<el-input v-model="row.b_bz" size="small" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
v-if="showPasswordRow"
|
||||
field="b_password"
|
||||
title="密码"
|
||||
align="center"
|
||||
sortable
|
||||
width="100"
|
||||
:edit-render="{
|
||||
autoFocus: true,
|
||||
}"
|
||||
>
|
||||
<template #edit="{ row }">
|
||||
<el-input v-model="row.b_password" size="small" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column title="操作" width="180" align="center">
|
||||
<template #default="{ row }">
|
||||
<template v-if="isRowEditing(row)">
|
||||
<el-button
|
||||
type="success"
|
||||
link
|
||||
:loading="loading.save"
|
||||
@click="handleSaveRow(row)"
|
||||
@click.stop
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
<el-button link @click="cancelEditRow(row)" @click.stop style="margin-left: 6px">
|
||||
取消
|
||||
</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button
|
||||
:disabled="loading.save"
|
||||
type="primary"
|
||||
link
|
||||
@click.stop
|
||||
@click="startEditRow(row)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
:disabled="loading.save"
|
||||
type="primary"
|
||||
link
|
||||
@click="handleShowDialog(row)"
|
||||
@click.stop
|
||||
style="margin-left: 6px"
|
||||
>
|
||||
分配权限
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
style="margin-left: 6px"
|
||||
:disabled="loading.save"
|
||||
type="danger"
|
||||
link
|
||||
@click="handleDeleteRow(row)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<el-pagination
|
||||
:current-page="pagination.page"
|
||||
:page-size="pagination.size"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[20, 50, 100, 500]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PermissionModel v-model="dialogVisible" :data="dialogData" />
|
||||
<PermissionCopyModal v-model="copyDialogVisible" :ids="selectedIds" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "@/styles/mixins.scss" as *;
|
||||
|
||||
.g3-container {
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
height: 100%;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include flexGapColumn(8px);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
@include flexGapWrap(4px, 0px);
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
@include flexGapWrap(4px, 8px);
|
||||
flex-wrap: nowrap;
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,179 @@
|
||||
<script setup>
|
||||
import { loadDataApi, saveObjectKeysApi } from "@/request/api";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
import { reactive, ref, watch } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Boolean,
|
||||
ids: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const formData = ref({
|
||||
b_id: "",
|
||||
b_name: "",
|
||||
});
|
||||
|
||||
const visible = ref(props.modelValue);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(v) => (visible.value = v),
|
||||
);
|
||||
watch(visible, (v) => emit("update:modelValue", v));
|
||||
|
||||
const loading = reactive({
|
||||
save: false,
|
||||
});
|
||||
|
||||
function close() {
|
||||
formData.value.b_id = "";
|
||||
formData.value.b_name = "";
|
||||
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
function handleClosed() {
|
||||
close();
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
if (formData.value.b_id === "") {
|
||||
ElMessage.warning("请选择需要复制的权限");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 确保目标用户的 ids 数组不为空
|
||||
if (props.ids && props.ids.length === 0) {
|
||||
ElMessage.warning("请选择目标用户");
|
||||
return;
|
||||
}
|
||||
|
||||
await ElMessageBox.confirm("原帐号权限将被清空,是否复制权限?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
dangerouslyUseHTMLString: true,
|
||||
});
|
||||
|
||||
loading.save = true;
|
||||
const fromUserId = formData.value.b_id;
|
||||
const toUserIds = props.ids; // 目标用户的 IDs 数组
|
||||
|
||||
// 1. 删除目标用户的原有权限(循环处理所有目标用户)
|
||||
const deleteRequests = toUserIds
|
||||
.map((toUserId) => ({
|
||||
tableName: "b_user_qx",
|
||||
keyFields: ["father"],
|
||||
deletes: [{ father: toUserId }],
|
||||
inserts: [],
|
||||
updates: [],
|
||||
}))
|
||||
.concat(
|
||||
toUserIds.map((toUserId) => ({
|
||||
tableName: "b_user_power",
|
||||
keyFields: ["userid"],
|
||||
deletes: [{ userid: toUserId }],
|
||||
inserts: [],
|
||||
updates: [],
|
||||
})),
|
||||
);
|
||||
|
||||
const res1 = await saveObjectKeysApi(deleteRequests);
|
||||
|
||||
if (!res1.success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 查询来源用户的权限数据
|
||||
const [qxRes, powerRes] = await Promise.all([
|
||||
loadDataApi("b_user_qx", `father='${fromUserId}'`),
|
||||
loadDataApi("b_user_power", `userid='${fromUserId}'`),
|
||||
]);
|
||||
|
||||
// 3. 构造批量插入数据(循环处理目标用户)
|
||||
const insertRequests = toUserIds
|
||||
.map((toUserId) => [
|
||||
{
|
||||
tableName: "b_user_qx",
|
||||
keyFields: ["father", "ModuleID"],
|
||||
inserts: (qxRes.data || []).map((r) => {
|
||||
const { b_id, ...rest } = r;
|
||||
return { ...rest, father: toUserId };
|
||||
}),
|
||||
updates: [],
|
||||
deletes: [],
|
||||
},
|
||||
{
|
||||
tableName: "b_user_power",
|
||||
keyFields: ["UserID", "ModuleID", "b_ivalue"],
|
||||
inserts: (powerRes.data || []).map((r) => {
|
||||
const { b_id, ...rest } = r;
|
||||
return { ...rest, UserID: toUserId };
|
||||
}),
|
||||
updates: [],
|
||||
deletes: [],
|
||||
},
|
||||
])
|
||||
.flat();
|
||||
|
||||
const res2 = await saveObjectKeysApi(insertRequests);
|
||||
|
||||
if (!res2.success) {
|
||||
return;
|
||||
}
|
||||
|
||||
close();
|
||||
ElMessage.success("复制成功");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
ElMessage.error("复制失败");
|
||||
} finally {
|
||||
loading.save = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
v-model="visible"
|
||||
title="权限复制"
|
||||
width="400px"
|
||||
class="dialog"
|
||||
@closed="handleClosed"
|
||||
>
|
||||
<div class="dialog-content">
|
||||
<span>请选择复制账号</span>
|
||||
<G3Select
|
||||
v-model="formData.b_id"
|
||||
v-model:label="formData.b_name"
|
||||
search-condition="b_id <> 'g3soft'"
|
||||
view-name="b_user"
|
||||
value-key="b_id"
|
||||
label-key="b_name"
|
||||
style="margin-top: 4px"
|
||||
/>
|
||||
</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-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,679 @@
|
||||
<script setup>
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import PermissionUserModel from "./permission-user-model.vue";
|
||||
import { useUserStore } from "@/store/user";
|
||||
import { loadDataBySqlApi, loadDataApi, saveObjectKeysApi } from "@/request/api";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: null,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
const treeRef = ref();
|
||||
|
||||
const userQx = reactive({
|
||||
moduleId: "",
|
||||
has: false,
|
||||
QxType: "", // userModuleQx
|
||||
ShareType: "", // userShareQx
|
||||
});
|
||||
|
||||
const userVisible = ref(false);
|
||||
const tableData = ref([]);
|
||||
const loading = reactive({
|
||||
table: false,
|
||||
actions: false,
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
children: "children",
|
||||
label: "mc_b",
|
||||
};
|
||||
async function handleNodeClick(nodeData) {
|
||||
loadNodeData(nodeData);
|
||||
}
|
||||
|
||||
async function loadNode(node, resolve) {
|
||||
try {
|
||||
const { level, data: nodeData } = node;
|
||||
let sql = "";
|
||||
|
||||
// 根节点查询
|
||||
if (level === 0) {
|
||||
sql = `SELECT * FROM s_module_bmfl WHERE js='1' AND b_canuse='1' ORDER BY b_xh`;
|
||||
} else {
|
||||
if (!nodeData) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const parentField = `bm_${level}_b`;
|
||||
const parentCondition = ` AND ${parentField}='${nodeData.b_id}'`;
|
||||
const childLevel = level + 1;
|
||||
|
||||
sql = `
|
||||
SELECT s_module_bmfl.*, s_module.b_id AS b_moduleid
|
||||
FROM s_module_bmfl
|
||||
LEFT JOIN s_module ON s_module.b_cate_id = s_module_bmfl.b_id
|
||||
WHERE s_module_bmfl.b_canuse='1'
|
||||
AND js='${childLevel}'
|
||||
${parentCondition}
|
||||
AND ISNULL(s_module.b_ISVirtualModule,'')<>'1'
|
||||
ORDER BY b_xh
|
||||
`;
|
||||
}
|
||||
|
||||
const res = await loadDataBySqlApi(sql);
|
||||
if (res.success) {
|
||||
const records = res.data || [];
|
||||
if (records.length === 0) {
|
||||
loadNodeData(nodeData);
|
||||
}
|
||||
|
||||
resolve(records);
|
||||
} else {
|
||||
resolve([]);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("loadNode 异常", err);
|
||||
resolve([]);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadNodeData(nodeData) {
|
||||
if (!nodeData) return;
|
||||
|
||||
// 防止重复加载
|
||||
// if (loading.table) {
|
||||
// ElMessage.warning('请不要点击过于频繁!')
|
||||
// return
|
||||
// }
|
||||
|
||||
loading.table = true;
|
||||
userQx.moduleId = nodeData.b_moduleid;
|
||||
|
||||
try {
|
||||
// 并行请求
|
||||
const [userQxReq, userPowerReq] = await Promise.all([
|
||||
loadDataApi(
|
||||
"b_user_qx",
|
||||
`father = '${props.data.b_id}' and ModuleID = '${nodeData.b_moduleid}'`,
|
||||
),
|
||||
loadDataApi(
|
||||
"b_user_power",
|
||||
`userid = '${props.data.b_id}' and moduleid='${nodeData.b_moduleid}'`,
|
||||
),
|
||||
]);
|
||||
|
||||
// 处理 userQx
|
||||
if (userQxReq.success && userQxReq.data.length > 0) {
|
||||
const userQxData = userQxReq.data[0];
|
||||
userQx.QxType = userQxData.QxType;
|
||||
userQx.ShareType = userQxData.ShareType;
|
||||
userQx.has = true;
|
||||
} else {
|
||||
userQx.QxType = "";
|
||||
userQx.ShareType = "";
|
||||
userQx.has = false;
|
||||
}
|
||||
|
||||
// 处理 userPower
|
||||
if (userPowerReq.success) {
|
||||
tableData.value = userPowerReq.data || [];
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
loading.table = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateShareTypeApi(value, userId, moduleId, hasQx) {
|
||||
const requestData = [
|
||||
{
|
||||
tableName: "b_user_qx",
|
||||
keyFields: ["father", "ModuleID"],
|
||||
inserts: [],
|
||||
updates: [],
|
||||
deletes: [],
|
||||
},
|
||||
];
|
||||
|
||||
const payload = {
|
||||
father: userId,
|
||||
ModuleID: moduleId,
|
||||
ShareType: value,
|
||||
};
|
||||
|
||||
if (hasQx) {
|
||||
requestData[0].updates.push(payload);
|
||||
} else {
|
||||
requestData[0].inserts.push(payload);
|
||||
}
|
||||
|
||||
const response = await axios.post("/saveobjectV3", requestData, {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
|
||||
if (!response.data?.success) throw new Error(response.data?.message || "保存失败");
|
||||
userStore.initPermission();
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
async function handleUpdateShareType(value) {
|
||||
if (!userQx.moduleId) {
|
||||
ElMessage.warning("请选择模块");
|
||||
return;
|
||||
}
|
||||
loading.actions = true;
|
||||
|
||||
try {
|
||||
const payload = await updateShareTypeApi(value, props.data.b_id, userQx.moduleId, userQx.has);
|
||||
userQx.ShareType = payload.ShareType;
|
||||
userQx.has = true;
|
||||
ElMessage.success("保存成功");
|
||||
|
||||
userStore.initPermission();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
ElMessage.error(err.message || "保存失败");
|
||||
} finally {
|
||||
loading.actions = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateQxTypeApi(value, userId, moduleId, hasQx) {
|
||||
const requestData = [
|
||||
{
|
||||
tableName: "b_user_qx",
|
||||
keyFields: ["father", "ModuleID"],
|
||||
inserts: [],
|
||||
updates: [],
|
||||
deletes: [],
|
||||
},
|
||||
];
|
||||
|
||||
if (hasQx) {
|
||||
// 更新
|
||||
requestData[0].updates.push({
|
||||
father: userId,
|
||||
ModuleID: moduleId,
|
||||
QxType: value,
|
||||
});
|
||||
} else {
|
||||
// 插入
|
||||
requestData[0].inserts.push({
|
||||
father: userId,
|
||||
ModuleID: moduleId,
|
||||
QxType: value,
|
||||
});
|
||||
}
|
||||
|
||||
const response = await saveObjectKeysApi(requestData);
|
||||
|
||||
if (!response.success) {
|
||||
userStore.initPermission();
|
||||
|
||||
throw new Error(response.data?.message || "保存失败");
|
||||
}
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function handleUpdateQxType(value) {
|
||||
if (!userQx.moduleId) {
|
||||
ElMessage.warning("请选择模块");
|
||||
return;
|
||||
}
|
||||
loading.actions = true;
|
||||
try {
|
||||
await updateQxTypeApi(value, props.data.b_id, userQx.moduleId, userQx.has);
|
||||
ElMessage.success("保存成功");
|
||||
userQx.QxType = value;
|
||||
userQx.has = true;
|
||||
|
||||
// userStore.initPermission();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
loading.actions = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 单条更新
|
||||
async function updateQxCanuseApi(value, userId, row) {
|
||||
const queryRes = await loadDataApi(
|
||||
"b_user_power",
|
||||
`UserID = '${userId}' and ModuleID = '${row.ModuleID}' and b_ivalue = '${row.b_ivalue}'`,
|
||||
);
|
||||
|
||||
if (!queryRes.success) {
|
||||
return;
|
||||
}
|
||||
|
||||
const records = queryRes.data || [];
|
||||
const isUpdate = records.length > 0;
|
||||
|
||||
const requestData = [
|
||||
{
|
||||
tableName: "b_user_power",
|
||||
keyFields: ["UserID", "ModuleID", "b_ivalue"],
|
||||
inserts: [],
|
||||
updates: [],
|
||||
deletes: [],
|
||||
},
|
||||
];
|
||||
|
||||
const payload = {
|
||||
b_canuse: value,
|
||||
UserID: userId,
|
||||
ModuleID: row.ModuleID,
|
||||
b_ivalue: row.b_ivalue,
|
||||
b_xh: row.b_xh,
|
||||
b_caption: row.b_caption,
|
||||
b_des: row.b_des,
|
||||
};
|
||||
|
||||
if (isUpdate) requestData[0].updates.push(payload);
|
||||
else requestData[0].inserts.push(payload);
|
||||
|
||||
const saveRes = await saveObjectKeysApi(requestData);
|
||||
|
||||
if (!saveRes.success) throw new Error(saveRes.message || "保存失败");
|
||||
|
||||
return payload; // 返回本次更新的数据
|
||||
}
|
||||
|
||||
// 单条更新
|
||||
async function handleUpdateQxCanuse(value, row) {
|
||||
try {
|
||||
const payload = await updateQxCanuseApi(value, props.data.b_id, row);
|
||||
|
||||
row.b_canuse = payload.b_canuse;
|
||||
userQx.has = true;
|
||||
|
||||
ElMessage.success("保存成功");
|
||||
|
||||
// userStore.initPermission();
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 批量更新
|
||||
async function handleUpdateCheckAllCanuse(value) {
|
||||
loading.actions = true;
|
||||
try {
|
||||
const strValue = value ? "1" : "0";
|
||||
|
||||
// 找出 b_canuse 与 strValue 不一致的行
|
||||
const mismatchedRows = tableData.value.filter((item) => item.b_canuse !== strValue);
|
||||
|
||||
if (mismatchedRows.length === 0) {
|
||||
ElMessage.warning("暂无更新");
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建更新数据
|
||||
const updates = mismatchedRows.map((item) => ({
|
||||
UserID: item.UserID,
|
||||
ModuleID: item.ModuleID,
|
||||
b_ivalue: item.b_ivalue,
|
||||
b_canuse: strValue,
|
||||
}));
|
||||
|
||||
const requestData = [
|
||||
{
|
||||
tableName: "b_user_power",
|
||||
keyFields: ["UserID", "ModuleID", "b_ivalue"],
|
||||
inserts: [],
|
||||
updates,
|
||||
deletes: [],
|
||||
},
|
||||
];
|
||||
|
||||
// 发起请求
|
||||
const response = await saveObjectKeysApi(requestData);
|
||||
|
||||
if (response.data?.success) {
|
||||
// 本地更新 tableData
|
||||
mismatchedRows.forEach((item) => (item.b_canuse = strValue));
|
||||
ElMessage.success("批量更新成功");
|
||||
|
||||
// userStore.initPermission();
|
||||
} else {
|
||||
throw new Error(response.data?.message || "更新失败");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
loading.actions = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleUpdateAllQx(qxtype) {
|
||||
try {
|
||||
loading.actions = true;
|
||||
const userid = props.data.b_id;
|
||||
const canuse = qxtype === "完全" ? "1" : "0";
|
||||
|
||||
const requestData = [
|
||||
{
|
||||
tableName: "b_user_qx",
|
||||
keyFields: ["father", "ModuleID"],
|
||||
inserts: [],
|
||||
updates: [],
|
||||
deletes: [],
|
||||
},
|
||||
{
|
||||
tableName: "b_user_power",
|
||||
keyFields: ["UserID", "ModuleID", "b_ivalue"],
|
||||
inserts: [],
|
||||
updates: [],
|
||||
deletes: [],
|
||||
},
|
||||
];
|
||||
|
||||
// 查询 b_user_qx 和 b_user_power
|
||||
const [qxRes, powerRes] = await Promise.all([
|
||||
loadDataBySqlApi(`SELECT * FROM b_user_qx WHERE father='${userid}'`),
|
||||
loadDataBySqlApi(`SELECT * FROM b_user_power WHERE UserID='${userid}'`),
|
||||
]);
|
||||
|
||||
const existingQx = qxRes.success ? qxRes.data || [] : [];
|
||||
const existingPower = powerRes.success ? powerRes.data || [] : [];
|
||||
|
||||
// 处理 b_user_qx
|
||||
const moduleSql = `SELECT b_id AS ModuleID FROM s_module WHERE isnull(b_ISVirtualModule,'')<>''`;
|
||||
const moduleRes = await loadDataBySqlApi(moduleSql);
|
||||
const moduleIds = moduleRes.data.map((r) => r.ModuleID) || [];
|
||||
|
||||
moduleIds.forEach((mid) => {
|
||||
const exist = existingQx.find((r) => r.ModuleID === mid);
|
||||
if (exist) {
|
||||
requestData[0].updates.push({
|
||||
father: userid,
|
||||
ModuleID: mid,
|
||||
QxType: qxtype,
|
||||
ShareType: exist.ShareType || "所有可见",
|
||||
ControlType: exist.ControlType || "所有控制",
|
||||
});
|
||||
} else {
|
||||
requestData[0].inserts.push({
|
||||
father: userid,
|
||||
ModuleID: mid,
|
||||
QxType: qxtype,
|
||||
ShareType: "所有可见",
|
||||
ControlType: "所有控制",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 处理 b_user_power
|
||||
const powerListRes = await loadDataBySqlApi(
|
||||
`SELECT ModuleID, b_ivalue, b_xh, b_caption,b_des FROM s_module_power`,
|
||||
);
|
||||
|
||||
const modulePowerList = powerListRes.data || [];
|
||||
|
||||
modulePowerList.forEach((mp) => {
|
||||
const exist = existingPower.find(
|
||||
(p) => p.ModuleID === mp.ModuleID && p.b_ivalue === mp.b_ivalue,
|
||||
);
|
||||
if (exist) {
|
||||
requestData[1].updates.push({
|
||||
UserID: userid,
|
||||
ModuleID: mp.ModuleID,
|
||||
b_ivalue: mp.b_ivalue,
|
||||
b_canuse: canuse,
|
||||
});
|
||||
} else {
|
||||
requestData[1].inserts.push({
|
||||
UserID: userid,
|
||||
ModuleID: mp.ModuleID,
|
||||
b_ivalue: mp.b_ivalue,
|
||||
b_xh: mp.b_xh,
|
||||
b_caption: mp.b_caption,
|
||||
b_canuse: canuse,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 调用增删改接口
|
||||
const saveRes = await saveObjectKeysApi(requestData);
|
||||
|
||||
if (!saveRes?.success) throw new Error(saveRes.message || "保存失败");
|
||||
|
||||
// 更新本地 tableData
|
||||
tableData.value.forEach((item) => (item.b_canuse = canuse));
|
||||
|
||||
ElMessage.success("操作成功");
|
||||
|
||||
// userStore.initPermission();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
loading.actions = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDeleteAllQx() {
|
||||
try {
|
||||
await ElMessageBox.confirm("确定要将这个帐号的权限清空吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "error",
|
||||
dangerouslyUseHTMLString: true,
|
||||
});
|
||||
|
||||
loading.actions = true;
|
||||
|
||||
const userid = props.data.b_id;
|
||||
|
||||
const requestData = [
|
||||
{
|
||||
tableName: "b_user_qx",
|
||||
keyFields: ["father"],
|
||||
inserts: [],
|
||||
updates: [],
|
||||
deletes: [{ father: userid }],
|
||||
},
|
||||
{
|
||||
tableName: "b_user_power",
|
||||
keyFields: ["userid"],
|
||||
inserts: [],
|
||||
updates: [],
|
||||
deletes: [{ userid }],
|
||||
},
|
||||
];
|
||||
|
||||
const saveRes = await saveObjectKeysApi(requestData);
|
||||
|
||||
if (saveRes.success) {
|
||||
ElMessage.success("清理成功");
|
||||
tableData.value = [];
|
||||
|
||||
// userStore.initPermission();
|
||||
} else {
|
||||
throw new Error(saveRes.data?.message || "清理失败");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
loading.actions = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleClosed() {
|
||||
userQx.moduleId = "";
|
||||
userQx.has = false;
|
||||
userQx.QxType = "";
|
||||
userQx.moduleId = "";
|
||||
|
||||
tableData.value = [];
|
||||
|
||||
loading.table = false;
|
||||
loading.actions = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog v-bind="$attrs" title="权限设置" width="1000px" class="dialog" @closed="handleClosed">
|
||||
<div class="dialog-header">
|
||||
<div>
|
||||
<div>账号:{{ props.data.b_id }}</div>
|
||||
<div>用户名:{{ props.data.b_name }}</div>
|
||||
<div>部门:{{ props.data.b_department }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
:loading="loading.actions"
|
||||
@click="handleUpdateAllQx('完全')"
|
||||
>
|
||||
全授权
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
type="danger"
|
||||
:loading="loading.actions"
|
||||
@click="handleUpdateAllQx('0')"
|
||||
>
|
||||
全禁用
|
||||
</el-button>
|
||||
<el-button size="small" type="danger" :loading="loading.actions" @click="handleDeleteAllQx">
|
||||
清理权限
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-content">
|
||||
<div class="dialog-left">
|
||||
<div class="left-header">功能模块</div>
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
class="tree"
|
||||
:props="defaultProps"
|
||||
:load="loadNode"
|
||||
lazy
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</div>
|
||||
<div class="dialog-right">
|
||||
<div v-if="userQx.moduleId !== ''" class="actions">
|
||||
<div class="actions-item">
|
||||
<el-checkbox
|
||||
v-model="userQx.QxType"
|
||||
label="使用权限"
|
||||
true-value="完全"
|
||||
false-value="禁用"
|
||||
:disabled="loading.actions"
|
||||
@change="handleUpdateQxType"
|
||||
/>
|
||||
|
||||
<el-checkbox label="全选" @change="handleUpdateCheckAllCanuse" />
|
||||
</div>
|
||||
|
||||
<el-radio-group
|
||||
v-model="userQx.ShareType"
|
||||
:disabled="loading.actions"
|
||||
@change="handleUpdateShareType"
|
||||
>
|
||||
<el-radio value="所有可见"> 所有可见 </el-radio>
|
||||
<el-radio value="公司可见"> 公司可见 </el-radio>
|
||||
<el-radio value="部门可见"> 部门可见 </el-radio>
|
||||
<el-radio value="仅本人"> 仅本人 </el-radio>
|
||||
</el-radio-group>
|
||||
<el-button type="primary" link @click="userVisible = !userVisible"> 同账户 </el-button>
|
||||
</div>
|
||||
<el-table v-loading="loading.table" :data="tableData" style="width: 100%" stripe>
|
||||
<el-table-column type="index" width="50" align="center" fixed />
|
||||
<el-table-column prop="b_canuse" label="操作授权" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-checkbox
|
||||
v-model="row.b_canuse"
|
||||
true-value="1"
|
||||
false-value="0"
|
||||
:disabled="loading.actions"
|
||||
@change="(value) => handleUpdateQxCanuse(value, row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="b_caption" label="功能说明" width="200" align="center" />
|
||||
<el-table-column prop="b_des" label="功能描述" align="center" />
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<PermissionUserModel v-model="userVisible" :qx-table-list="tableData" :user-qx-data="userQx" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tree {
|
||||
overflow: auto;
|
||||
}
|
||||
.dialog-content {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.dialog-header div {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.dialog-left {
|
||||
width: 30%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.left-header {
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.module-tree {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.dialog-right {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.dialog-right .actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.dialog-right .actions .actions-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.el-radio {
|
||||
margin-right: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,235 @@
|
||||
<script setup lang="js">
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useUserStore } from "@/store/user";
|
||||
import { loadDataApi, saveObjectKeysApi } from "@/request/api";
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Boolean,
|
||||
userQxData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
qxTableList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const visible = ref(props.modelValue);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(v) => (visible.value = v),
|
||||
);
|
||||
|
||||
watch(visible, (v) => emit("update:modelValue", v));
|
||||
|
||||
const tableData = ref([]);
|
||||
const selectedRows = ref([]);
|
||||
|
||||
const loading = reactive({
|
||||
table: true,
|
||||
save: false,
|
||||
});
|
||||
|
||||
// 选择行变化
|
||||
const handleSelectionChange = (rows) => {
|
||||
selectedRows.value = rows;
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
tableData.value = [];
|
||||
selectedRows.value = [];
|
||||
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const handleOpen = async () => {
|
||||
loading.table = true;
|
||||
try {
|
||||
const response = await loadDataApi("b_user", `b_id <> 'g3soft'`);
|
||||
|
||||
if (response.success) {
|
||||
tableData.value = response.data || [];
|
||||
} else {
|
||||
ElMessage.error(response.message || "加载节点失败");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("加载节点异常:", err);
|
||||
} finally {
|
||||
loading.table = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleClosed = () => {
|
||||
tableData.value = [];
|
||||
selectedRows.value = [];
|
||||
|
||||
loading.table = true;
|
||||
loading.save = false;
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
if (selectedRows.value.length === 0) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
loading.save = true; // 开始保存
|
||||
|
||||
try {
|
||||
const moduleId = props.userQxData.moduleId;
|
||||
const ivalueList = props.qxTableList.map((item) => item.b_ivalue);
|
||||
|
||||
for (const user of selectedRows.value) {
|
||||
const qxResponse = await loadDataApi(
|
||||
"b_user_qx",
|
||||
`father = '${user.b_id}' and ModuleID = '${moduleId}'`,
|
||||
);
|
||||
|
||||
const qxExisting = qxResponse.data || [];
|
||||
const hasQX = qxExisting.length > 0;
|
||||
|
||||
const inserts1 = [];
|
||||
const updates1 = [];
|
||||
|
||||
if (!hasQX) {
|
||||
inserts1.push({
|
||||
father: user.b_id,
|
||||
ModuleID: moduleId,
|
||||
ShareType: props.userQxData.ShareType,
|
||||
QxType: props.userQxData.QxType,
|
||||
});
|
||||
} else {
|
||||
const updateRecord = { father: user.b_id, ModuleID: moduleId };
|
||||
let needUpdate = false;
|
||||
|
||||
if (props.userQxData.ShareType !== qxExisting[0].ShareType) {
|
||||
updateRecord.ShareType = props.userQxData.ShareType;
|
||||
needUpdate = true;
|
||||
}
|
||||
if (props.userQxData.QxType !== qxExisting[0].QxType) {
|
||||
updateRecord.QxType = props.userQxData.QxType;
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
if (needUpdate) updates1.push(updateRecord);
|
||||
}
|
||||
|
||||
// 查询用户操作权限表 b_user_power
|
||||
const powerResponse = await loadDataApi(
|
||||
"b_user_power",
|
||||
`UserID = '${user.b_id}' and ModuleID = '${moduleId}' and b_ivalue in ('${ivalueList.join("','")}')`,
|
||||
);
|
||||
const powerExisting = powerResponse.data || [];
|
||||
|
||||
const inserts2 = [];
|
||||
const updates2 = [];
|
||||
|
||||
props.qxTableList.forEach((qxItem) => {
|
||||
const found = powerExisting.find((r) => r.b_ivalue === qxItem.b_ivalue);
|
||||
const targetCanuse = qxItem.b_canuse || "0";
|
||||
|
||||
if (found) {
|
||||
if (found.b_canuse !== targetCanuse) {
|
||||
updates2.push({
|
||||
UserID: user.b_id,
|
||||
ModuleID: moduleId,
|
||||
b_ivalue: qxItem.b_ivalue,
|
||||
b_canuse: targetCanuse,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
inserts2.push({
|
||||
UserID: user.b_id,
|
||||
ModuleID: moduleId,
|
||||
b_ivalue: qxItem.b_ivalue,
|
||||
b_canuse: targetCanuse,
|
||||
b_xh: qxItem.b_xh,
|
||||
b_caption: qxItem.b_caption,
|
||||
b_des: qxItem.b_des,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const requestData = [
|
||||
{
|
||||
tableName: "b_user_qx",
|
||||
keyFields: ["father", "ModuleID"],
|
||||
inserts: inserts1,
|
||||
updates: updates1,
|
||||
deletes: [],
|
||||
},
|
||||
{
|
||||
tableName: "b_user_power",
|
||||
keyFields: ["UserID", "ModuleID", "b_ivalue"],
|
||||
inserts: inserts2,
|
||||
updates: updates2,
|
||||
deletes: [],
|
||||
},
|
||||
];
|
||||
|
||||
const response = await saveObjectKeysApi(requestData);
|
||||
|
||||
if (!response.success) {
|
||||
throw new Error(response.message || "保存失败");
|
||||
}
|
||||
}
|
||||
|
||||
ElMessage.success("保存成功");
|
||||
close();
|
||||
|
||||
// userStore.initPermission();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
loading.save = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
v-model="visible"
|
||||
title="同账号权限分配"
|
||||
width="600px"
|
||||
class="dialog"
|
||||
@open="handleOpen"
|
||||
@closed="handleClosed"
|
||||
>
|
||||
<div class="dialog-content">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
height="400"
|
||||
stripe
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="b_id" label="账号" width="100" align="center" />
|
||||
<el-table-column prop="b_name" label="用户名" width="100" align="center" />
|
||||
<el-table-column prop="b_department" label="部门" align="center" />
|
||||
</el-table>
|
||||
</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-content {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,125 @@
|
||||
<script setup>
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
import { ref } from "vue";
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const form = ref({
|
||||
oldPassword: "",
|
||||
newPassword: "",
|
||||
confirmPassword: "",
|
||||
});
|
||||
|
||||
function resetFrom() {
|
||||
form.value.oldPassword = "";
|
||||
form.value.newPassword = "";
|
||||
form.value.confirmPassword = "";
|
||||
}
|
||||
|
||||
async function handleConfirm() {
|
||||
if (form.value.newPassword !== form.value.confirmPassword) {
|
||||
ElMessage.warning("新密码和确认密码不一致");
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
|
||||
const requestData = {
|
||||
oldPassword: form.value.oldPassword,
|
||||
newPassword: form.value.newPassword,
|
||||
};
|
||||
|
||||
try {
|
||||
const saveRes = await axios.post("/updatePassword", requestData, {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
|
||||
if (saveRes.data.success) {
|
||||
ElMessage.success("密码更新成功");
|
||||
resetFrom();
|
||||
} else {
|
||||
ElMessage.error(`密码更新失败:${saveRes.data.message}`);
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(`密码更新失败:${error.message || "未知错误"}`);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
</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>
|
||||
</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;
|
||||
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