完善报价单填表与币种回填,并补齐账号末次登录、角色按钮权限与陆运全线匹配。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
linmianxun
2026-09-19 22:03:45 +08:00
co-authored by Cursor
parent 1a2e1d6cb2
commit e4743541f7
25 changed files with 2262 additions and 163 deletions
@@ -1,7 +1,7 @@
<script setup lang="ts">
/**
* 角色管理:对接 Jeecg /sys/role 与菜单权限树。
* 数据权限用线路标签写入角色 description,避免伪造第二套 RBAC。
* 数据权限按 Excel 线路目录树勾选,写入角色 description 的 [dataPerms:…],不另造 RBAC。
*/
import { computed, onMounted, reactive, ref } from "vue";
import { message, Modal } from "ant-design-vue";
@@ -15,6 +15,14 @@ import {
saveRolePermission,
type RoleRow,
} from "/@/views/inquiry/role/role.api";
import {
DATA_PERM_TREE,
SYSTEM_EXCEPTION_NOTIFY_KEY,
SYSTEM_EXCEPTION_NOTIFY_TITLE,
dataPermTitle,
leafDataPermKeys,
normalizeDataPermKeys,
} from "/@/views/inquiry/shared/dataPermCatalog";
import ProtoIcon from "/@/views/inquiry/components/ProtoIcon.vue";
import AdminModal from "/@/views/inquiry/components/AdminModal.vue";
@@ -30,19 +38,12 @@ const form = reactive({
status: "active" as RoleRow["status"],
checkedKeys: [] as string[],
dataPerms: [] as string[],
/** 旧后台独立项:系统异常消息通知 */
systemExceptionNotify: false,
});
const dataOptions = [
"空运·全部线路",
"空运·CGO-KUL",
"空运·HKG-KUL",
"海运·全部线路",
"海运·海运全线",
"陆运·全部线路",
"陆运·中港/中亚/中欧",
"陆运·东南亚",
"系统异常·消息通知",
];
const dataPermTree = DATA_PERM_TREE;
const systemExceptionNotifyTitle = SYSTEM_EXCEPTION_NOTIFY_TITLE;
const modalTitle = computed(() => (editing.value ? "编辑角色" : "新增角色"));
const modalOkText = computed(() => "保存");
@@ -68,15 +69,29 @@ function parseDataPerms(desc: string | undefined): string[] {
if (!desc) return [];
const m = desc.match(/\[dataPerms:(.+?)\]/);
if (!m) return [];
return m[1]
.split("|")
.map((s) => s.trim())
.filter(Boolean);
return normalizeDataPermKeys(
m[1]
.split("|")
.map((s) => s.trim())
.filter(Boolean),
);
}
function buildDescription(dataPerms: string[]) {
if (!dataPerms.length) return "";
return `[dataPerms:${dataPerms.join("|")}]`;
function buildDescription(dataPerms: string[], systemExceptionNotify: boolean) {
const keys = leafDataPermKeys(dataPerms);
if (systemExceptionNotify && !keys.includes(SYSTEM_EXCEPTION_NOTIFY_KEY)) {
keys.push(SYSTEM_EXCEPTION_NOTIFY_KEY);
}
if (!keys.length) return "";
return `[dataPerms:${keys.join("|")}]`;
}
function displayDataPerms(keys: string[]): string[] {
return keys.map((k) => dataPermTitle(k));
}
function routeDataPermsOnly(keys: string[]): string[] {
return keys.filter((k) => k !== SYSTEM_EXCEPTION_NOTIFY_KEY);
}
async function loadMenuTree() {
@@ -143,18 +158,21 @@ function openCreate() {
status: "active",
checkedKeys: [],
dataPerms: [],
systemExceptionNotify: false,
});
modalOpen.value = true;
}
async function openEdit(row: RoleRow) {
editing.value = row;
const allPerms = [...row.dataPerms];
Object.assign(form, {
name: row.name,
roleCode: row.roleCode,
status: row.status,
checkedKeys: [] as string[],
dataPerms: [...row.dataPerms],
dataPerms: routeDataPermsOnly(allPerms),
systemExceptionNotify: allPerms.includes(SYSTEM_EXCEPTION_NOTIFY_KEY),
});
try {
const perms: any = await queryRolePermission({ roleId: row.id });
@@ -179,7 +197,7 @@ async function save() {
id: editing.value?.id,
roleName: form.name.trim(),
roleCode,
description: buildDescription(form.dataPerms),
description: buildDescription(form.dataPerms, form.systemExceptionNotify),
},
Boolean(editing.value),
);
@@ -214,6 +232,12 @@ function removeRole(row: RoleRow) {
},
});
}
/** a-tree 可能回传对象或数组;统一成叶子 key 列表 */
function onDataPermCheck(checked: string[] | { checked: string[] }) {
const keys = Array.isArray(checked) ? checked : checked?.checked || [];
form.dataPerms = leafDataPermKeys(keys.map(String));
}
</script>
<template>
@@ -252,7 +276,7 @@ function removeRole(row: RoleRow) {
</td>
<td>
<div class="role-perm-summary">
<span v-for="d in row.dataPerms" :key="d" class="tag">{{ d }}</span>
<span v-for="d in displayDataPerms(row.dataPerms)" :key="d" class="tag">{{ d }}</span>
<span v-if="!row.dataPerms.length" style="color: #94a3b8">—</span>
</div>
</td>
@@ -306,14 +330,20 @@ function removeRole(row: RoleRow) {
<div class="admin-modal-field">
<label class="admin-modal-label">数据权限</label>
<a-select
v-model:value="form.dataPerms"
mode="multiple"
style="width: 100%"
:options="dataOptions.map((d) => ({ value: d, label: d }))"
placeholder="选择线路 / 通知范围"
:max-tag-count="3"
/>
<div class="perm-tree-box">
<a-tree
:checkedKeys="form.dataPerms"
checkable
default-expand-all
:tree-data="dataPermTree"
:field-names="{ title: 'title', key: 'key', children: 'children' }"
@check="onDataPermCheck"
/>
</div>
<label class="notify-check">
<input v-model="form.systemExceptionNotify" type="checkbox" />
{{ systemExceptionNotifyTitle }}
</label>
</div>
<div class="admin-modal-field">
@@ -340,4 +370,14 @@ function removeRole(row: RoleRow) {
align-items: center;
gap: 10px;
}
.notify-check {
display: flex;
align-items: center;
gap: 8px;
margin-top: 10px;
font-size: 13px;
color: #0f172a;
cursor: pointer;
}
</style>