598 lines
21 KiB
Vue
598 lines
21 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* 组别详情(查看 / 新增 / 修改):旧 WebVue views/security/GroupOperation.vue 的移动端等价物
|
|
*
|
|
* 三个子表(旧页用 dragModal 内嵌 Enquiry + provide/inject,移动端改成真跳选择页再回传):
|
|
* 1. 权限明细 SecurityGroupPermissionViews —— 选 permission-enquiry(多选),
|
|
* 每行带 查看/新增/修改/删除 四个勾选(对齐旧 GroupPermissionActions 的四个 checkbox);
|
|
* 删除权限行会连同其扩展权限明细一起剔除(旧 deletePermission 同款)。
|
|
* 2. 扩展权限明细 SecurityGroupPermissionDependentViews —— 由权限行的"扩展名称"点开
|
|
* permission-dependent-enquiry(多选、按 dependent 过滤)补入;
|
|
* 3. 组别内角色群 SecurityActorGroups —— 选 actor-enquiry(多选)。
|
|
* 新增权限/扩展权限行时四个勾选全部置 false(旧页面 ent.IsAmend=...=false)。
|
|
* 保存按 CreatedBy 有无决定 Update / Insert(SecurityGroupData)。
|
|
*/
|
|
import type { AdminFormAction, AdminFormField, AdminFormSection } from "@/admin/admin-types";
|
|
import { onLoad } from "@dcloudio/uni-app";
|
|
import { useLocale } from "uview-pro";
|
|
import { computed, ref } from "vue";
|
|
import { useAdminPick, useAuthGuard } from "@/composables";
|
|
import { SecurityActorGroupViewEntity } from "@/data/auto/securityActorGroupViewEntity";
|
|
import { SecurityGroupData } from "@/data/auto/securityGroupData";
|
|
import { SecurityGroupPermissionDependentViewDisplayData } from "@/data/auto/securityGroupPermissionDependentViewDisplayData";
|
|
import { SecurityGroupPermissionViewDisplayData } from "@/data/auto/securityGroupPermissionViewDisplayData";
|
|
import { usePagePermission } from "@/permission/permission-context";
|
|
import { smartBack } from "@/router/routes";
|
|
import securityGroupService from "@/service/security/securityGroupService";
|
|
|
|
const { t } = useLocale();
|
|
const perm = usePagePermission("groupOperation");
|
|
|
|
const data = ref<SecurityGroupData>(new SecurityGroupData());
|
|
const group = computed(() => data.value.SecurityGroup);
|
|
const readonly = ref(true);
|
|
const routeId = ref("");
|
|
|
|
const isNew = computed(() => !String(group.value.CreatedBy ?? "").trim());
|
|
|
|
const heroTitle = computed(
|
|
() => String(group.value.GroupLocalName || group.value.GroupName || ""),
|
|
);
|
|
const heroChips = computed(() => {
|
|
const chips: string[] = [];
|
|
if (group.value.GroupName) {
|
|
chips.push(`${t("GroupPage.groupName")}: ${group.value.GroupName}`);
|
|
}
|
|
return chips;
|
|
});
|
|
|
|
const sections = computed<AdminFormSection[]>(() => {
|
|
const fields: AdminFormField[] = [
|
|
{
|
|
label: t("GroupPage.groupName"),
|
|
prop: "GroupName",
|
|
required: true,
|
|
lock: routeId.value !== "",
|
|
},
|
|
{ label: t("GroupPage.groupLocalName"), prop: "GroupLocalName", required: true },
|
|
{ label: t("GroupPage.groupEngName"), prop: "GroupEngName" },
|
|
];
|
|
return [{ title: t("GroupPage.detailTitle"), fields }];
|
|
});
|
|
|
|
const actions = computed<AdminFormAction[]>(() => {
|
|
const list: AdminFormAction[] = [];
|
|
if (perm.canAmend.value) {
|
|
list.push({ key: "amend", label: t("AdminPage.amendBut"), when: "view" });
|
|
}
|
|
if (perm.canDelete.value) {
|
|
list.push({
|
|
key: "delete",
|
|
label: t("AdminPage.deleteBut"),
|
|
tone: "error",
|
|
confirm: true,
|
|
when: "view",
|
|
});
|
|
}
|
|
list.push({ key: "save", label: t("AdminPage.saveBut"), when: "edit" });
|
|
list.push({ key: "cancel", label: t("AdminPage.cancelBut"), when: "edit" });
|
|
return list;
|
|
});
|
|
|
|
function ensureLists(): void {
|
|
if (!data.value.SecurityGroupPermissionViews) {
|
|
data.value.SecurityGroupPermissionViews = [];
|
|
}
|
|
if (!data.value.SecurityGroupPermissionDependentViews) {
|
|
data.value.SecurityGroupPermissionDependentViews = [];
|
|
}
|
|
if (!data.value.SecurityActorGroups) {
|
|
data.value.SecurityActorGroups = [];
|
|
}
|
|
}
|
|
|
|
async function load(): Promise<void> {
|
|
const result = await securityGroupService.FindByKey(routeId.value);
|
|
if (!result?.SecurityGroup?.GroupName) {
|
|
uni.showToast({ title: t("AdminPage.notExistTip"), icon: "none" });
|
|
return;
|
|
}
|
|
data.value = result;
|
|
ensureLists();
|
|
}
|
|
|
|
function onFieldChange(prop: string, value: unknown): void {
|
|
(group.value as Record<string, any>)[prop] = value;
|
|
}
|
|
|
|
async function onSave(): Promise<void> {
|
|
if (!String(group.value.GroupName ?? "").trim()) {
|
|
uni.showToast({ title: t("GroupPage.nameRequired"), icon: "none" });
|
|
return;
|
|
}
|
|
if (!String(group.value.GroupLocalName ?? "").trim()) {
|
|
uni.showToast({ title: t("GroupPage.localNameRequired"), icon: "none" });
|
|
return;
|
|
}
|
|
ensureLists();
|
|
const isUpdate = !isNew.value;
|
|
const result = await (isUpdate
|
|
? securityGroupService.Update(data.value)
|
|
: securityGroupService.Insert(data.value));
|
|
if (result?.SecurityGroup) {
|
|
data.value = result;
|
|
ensureLists();
|
|
}
|
|
routeId.value = String(group.value.GroupName ?? "");
|
|
readonly.value = true;
|
|
uni.showToast({ title: t("AdminPage.savedTip"), icon: "none" });
|
|
}
|
|
|
|
async function onDelete(): Promise<void> {
|
|
await securityGroupService.Delete(routeId.value);
|
|
uni.showToast({ title: t("AdminPage.deletedTip"), icon: "none" });
|
|
setTimeout(() => smartBack("mineTab"), 600);
|
|
}
|
|
|
|
function onAction(action: AdminFormAction): void {
|
|
switch (action.key) {
|
|
case "amend":
|
|
readonly.value = false;
|
|
break;
|
|
case "save":
|
|
onSave();
|
|
break;
|
|
case "delete":
|
|
onDelete();
|
|
break;
|
|
case "cancel":
|
|
if (isNew.value || !routeId.value) {
|
|
smartBack("mineTab");
|
|
return;
|
|
}
|
|
readonly.value = true;
|
|
load();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
function onBack(): void {
|
|
smartBack("mineTab");
|
|
}
|
|
|
|
// 权限勾选:四个 boolean 直接落在 DisplayData 上(与旧 GroupPermissionActions 一致)
|
|
function toggleAction(record: SecurityGroupPermissionViewDisplayData, key: "IsView" | "IsCreate" | "IsAmend" | "IsDelete"): void {
|
|
if (readonly.value || !perm.canAmend.value) {
|
|
return;
|
|
}
|
|
record[key] = !record[key];
|
|
}
|
|
|
|
function toggleDependentAction(
|
|
record: SecurityGroupPermissionDependentViewDisplayData,
|
|
key: "IsView" | "IsCreate" | "IsAmend" | "IsDelete",
|
|
): void {
|
|
if (readonly.value || !perm.canAmend.value) {
|
|
return;
|
|
}
|
|
record[key] = !record[key];
|
|
}
|
|
|
|
const ACTION_KEYS: Array<"IsView" | "IsCreate" | "IsAmend" | "IsDelete"> = [
|
|
"IsView",
|
|
"IsCreate",
|
|
"IsAmend",
|
|
"IsDelete",
|
|
];
|
|
|
|
function actionLabel(key: string): string {
|
|
const map: Record<string, string> = {
|
|
IsView: t("GroupPage.actionView"),
|
|
IsCreate: t("GroupPage.actionCreate"),
|
|
IsAmend: t("GroupPage.actionAmend"),
|
|
IsDelete: t("GroupPage.actionDelete"),
|
|
};
|
|
return map[key] ?? key;
|
|
}
|
|
|
|
// 权限选择:跳 permission-enquiry(多选),按 PermissionName 去重
|
|
const permissionPicker = useAdminPick("permission");
|
|
|
|
function addPermission(): void {
|
|
permissionPicker.open({ page: "permissionEnquiry", params: { multiple: "1" } }, (rows) => {
|
|
ensureLists();
|
|
const exists = new Set(data.value.SecurityGroupPermissionViews.map(item => item.PermissionName));
|
|
for (const row of rows) {
|
|
const permissionName = String(row.PermissionName ?? "");
|
|
if (!permissionName || exists.has(permissionName)) {
|
|
continue;
|
|
}
|
|
const ent = new SecurityGroupPermissionViewDisplayData();
|
|
ent.GroupName = String(group.value.GroupName ?? "");
|
|
ent.PermissionName = permissionName;
|
|
ent.PermissionDescription = String(row.Description ?? "");
|
|
ent.Dependent = String(row.Dependent ?? "");
|
|
ent.IsView = ent.IsCreate = ent.IsAmend = ent.IsDelete = false;
|
|
data.value.SecurityGroupPermissionViews.push(ent);
|
|
exists.add(permissionName);
|
|
}
|
|
});
|
|
}
|
|
|
|
function deletePermission(permissionName: string): void {
|
|
data.value.SecurityGroupPermissionViews = data.value.SecurityGroupPermissionViews.filter(
|
|
item => item.PermissionName !== permissionName,
|
|
);
|
|
// 旧 deletePermission:删权限行时连带删掉它下面的扩展权限明细
|
|
data.value.SecurityGroupPermissionDependentViews = data.value.SecurityGroupPermissionDependentViews.filter(
|
|
item => item.PermissionName !== permissionName,
|
|
);
|
|
}
|
|
|
|
// 扩展权限选择:跳 permission-dependent-enquiry(多选、按当前权限行的 dependent 过滤)
|
|
const dependentPicker = useAdminPick("dependent");
|
|
|
|
function addPermissionDependent(dependent: string): void {
|
|
if (readonly.value || !dependent) {
|
|
return;
|
|
}
|
|
dependentPicker.open(
|
|
{ page: "permissionDependentEnquiry", params: { multiple: "1", dependent } },
|
|
(rows) => {
|
|
ensureLists();
|
|
const exists = new Set(
|
|
data.value.SecurityGroupPermissionDependentViews.map(item => item.DependentDetailName),
|
|
);
|
|
for (const row of rows) {
|
|
const detailName = String(row.DependentDetailName ?? "");
|
|
if (!detailName || exists.has(detailName)) {
|
|
continue;
|
|
}
|
|
const ent = new SecurityGroupPermissionDependentViewDisplayData();
|
|
ent.GroupName = String(group.value.GroupName ?? "");
|
|
ent.PermissionName = String(row.PermissionName ?? "");
|
|
ent.Dependent = String(row.Dependent ?? "");
|
|
ent.DependentDetailName = detailName;
|
|
ent.DependentDetailDescription = String(row.DependentDetailDescription ?? "");
|
|
ent.IsView = ent.IsCreate = ent.IsAmend = ent.IsDelete = false;
|
|
data.value.SecurityGroupPermissionDependentViews.push(ent);
|
|
exists.add(detailName);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
|
|
function deletePermissionDependent(detailName: string): void {
|
|
data.value.SecurityGroupPermissionDependentViews = data.value.SecurityGroupPermissionDependentViews.filter(
|
|
item => item.DependentDetailName !== detailName,
|
|
);
|
|
}
|
|
|
|
// 角色选择:跳 actor-enquiry(多选),按 ActorName 去重
|
|
const actorPicker = useAdminPick("actor");
|
|
|
|
function addActor(): void {
|
|
actorPicker.open({ page: "actorEnquiry", params: { multiple: "1" } }, (rows) => {
|
|
ensureLists();
|
|
const exists = new Set(data.value.SecurityActorGroups.map(item => item.ActorName));
|
|
for (const row of rows) {
|
|
const actorName = String(row.ActorName ?? "");
|
|
if (!actorName || exists.has(actorName)) {
|
|
continue;
|
|
}
|
|
const ent = new SecurityActorGroupViewEntity();
|
|
ent.GroupName = String(group.value.GroupName ?? "");
|
|
ent.ActorName = actorName;
|
|
ent.ActorLocalName = String(row.ActorLocalName ?? "");
|
|
data.value.SecurityActorGroups.push(ent);
|
|
exists.add(actorName);
|
|
}
|
|
});
|
|
}
|
|
|
|
function deleteActor(actorName: string): void {
|
|
data.value.SecurityActorGroups = data.value.SecurityActorGroups.filter(
|
|
item => item.ActorName !== actorName,
|
|
);
|
|
}
|
|
|
|
onLoad((options) => {
|
|
routeId.value = String(options?.id ?? "");
|
|
const mode = String(options?.mode ?? "view");
|
|
readonly.value = mode !== "create" && mode !== "amend";
|
|
});
|
|
|
|
let booted = false;
|
|
useAuthGuard(() => {
|
|
if (booted) {
|
|
return;
|
|
}
|
|
booted = true;
|
|
if (routeId.value) {
|
|
load();
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<admin-form :nav-title="t('GroupPage.detailTitle')" :show-nav-back="true" :hero-title="heroTitle"
|
|
:hero-chips="heroChips" :sections="sections" :model="group" :readonly="readonly" :actions="actions"
|
|
@back="onBack" @action="onAction" @field-change="onFieldChange">
|
|
<template #extra="{ readonly: ro }">
|
|
<!-- 权限明细 -->
|
|
<view class="group">
|
|
<view class="group-head">
|
|
<text class="group-title">
|
|
{{ t("GroupPage.permissionTitle") }}
|
|
</text>
|
|
<view v-if="!ro && perm.canAmend.value" class="group-add" @click="addPermission">
|
|
<u-icon name="plus" size="24" color="#059669" />
|
|
<text class="group-add-text">
|
|
{{ t("GroupPage.addPermissionBut") }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="group-card">
|
|
<view v-if="!data.SecurityGroupPermissionViews.length" class="group-empty">
|
|
<text>
|
|
{{ t("GroupPage.permissionEmpty") }}
|
|
</text>
|
|
</view>
|
|
|
|
<view v-for="record in data.SecurityGroupPermissionViews" :key="record.PermissionName"
|
|
class="perm-row">
|
|
<view class="perm-head">
|
|
<view class="perm-text">
|
|
<text class="perm-name">
|
|
{{ record.PermissionName || t("GroupPage.unnamedPermission") }}
|
|
</text>
|
|
<text v-if="record.PermissionDescription" class="perm-sub">
|
|
{{ record.PermissionDescription }}
|
|
</text>
|
|
</view>
|
|
<u-icon v-if="!ro && perm.canAmend.value" name="trash" size="34" color="#c4c8cf"
|
|
@click="deletePermission(record.PermissionName)" />
|
|
</view>
|
|
<view class="dep-entry" @click="addPermissionDependent(record.Dependent)">
|
|
<text class="dep-entry-label">
|
|
{{ t("GroupPage.dependentLabel") }}
|
|
</text>
|
|
<text class="dep-entry-value">
|
|
{{ record.Dependent || "-" }}
|
|
</text>
|
|
</view>
|
|
<view class="perm-actions">
|
|
<view v-for="key in ACTION_KEYS" :key="key" class="perm-action"
|
|
:class="{ 'perm-action-disabled': ro || !perm.canAmend.value }"
|
|
@click="toggleAction(record, key)">
|
|
<u-icon :name="record[key] ? 'checkmark-circle-fill' : 'checkmark-circle'" size="34"
|
|
:color="record[key] ? '#059669' : '#c4c8cf'" />
|
|
<text class="perm-action-label">
|
|
{{ actionLabel(key) }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 扩展权限明细 -->
|
|
<view class="group">
|
|
<view class="group-head">
|
|
<text class="group-title">
|
|
{{ t("GroupPage.dependentViewTitle") }}
|
|
</text>
|
|
</view>
|
|
|
|
<view class="group-card">
|
|
<view v-if="!data.SecurityGroupPermissionDependentViews.length" class="group-empty">
|
|
<text>
|
|
{{ t("GroupPage.dependentViewEmpty") }}
|
|
</text>
|
|
</view>
|
|
|
|
<view v-for="record in data.SecurityGroupPermissionDependentViews" :key="record.DependentDetailName"
|
|
class="perm-row">
|
|
<view class="perm-head">
|
|
<view class="perm-text">
|
|
<text class="perm-name">
|
|
{{ record.DependentDetailName || t("GroupPage.unnamedDependent") }}
|
|
</text>
|
|
<text v-if="record.DependentDetailDescription" class="perm-sub">
|
|
{{ record.DependentDetailDescription }}
|
|
</text>
|
|
</view>
|
|
<u-icon v-if="!ro && perm.canAmend.value" name="trash" size="34" color="#c4c8cf"
|
|
@click="deletePermissionDependent(record.DependentDetailName)" />
|
|
</view>
|
|
<view class="perm-actions">
|
|
<view v-for="key in ACTION_KEYS" :key="key" class="perm-action"
|
|
:class="{ 'perm-action-disabled': ro || !perm.canAmend.value }"
|
|
@click="toggleDependentAction(record, key)">
|
|
<u-icon :name="record[key] ? 'checkmark-circle-fill' : 'checkmark-circle'" size="34"
|
|
:color="record[key] ? '#059669' : '#c4c8cf'" />
|
|
<text class="perm-action-label">
|
|
{{ actionLabel(key) }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 组别内角色群 -->
|
|
<view class="group">
|
|
<view class="group-head">
|
|
<text class="group-title">
|
|
{{ t("GroupPage.actorTitle") }}
|
|
</text>
|
|
<view v-if="!ro && perm.canAmend.value" class="group-add" @click="addActor">
|
|
<u-icon name="plus" size="24" color="#059669" />
|
|
<text class="group-add-text">
|
|
{{ t("GroupPage.addActorBut") }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="group-card">
|
|
<view v-if="!data.SecurityActorGroups.length" class="group-empty">
|
|
<text>
|
|
{{ t("GroupPage.actorEmpty") }}
|
|
</text>
|
|
</view>
|
|
|
|
<view v-for="record in data.SecurityActorGroups" :key="record.ActorName" class="actor-row">
|
|
<view class="perm-text">
|
|
<text class="perm-name">
|
|
{{ record.ActorLocalName || record.ActorName || t("GroupPage.unnamedActor") }}
|
|
</text>
|
|
<text v-if="record.ActorLocalName" class="perm-sub">
|
|
{{ record.ActorName }}
|
|
</text>
|
|
</view>
|
|
<u-icon v-if="!ro && perm.canAmend.value" name="trash" size="34" color="#c4c8cf"
|
|
@click="deleteActor(record.ActorName)" />
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
</admin-form>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.group {
|
|
margin-bottom: 28rpx;
|
|
}
|
|
|
|
.group-head {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 12rpx 12rpx;
|
|
}
|
|
|
|
.group-title {
|
|
font-size: 24rpx;
|
|
color: $u-tips-color;
|
|
}
|
|
|
|
.group-add {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
}
|
|
|
|
.group-add-text {
|
|
margin-left: 4rpx;
|
|
font-size: 24rpx;
|
|
color: $u-type-primary;
|
|
}
|
|
|
|
.group-card {
|
|
padding: 0 28rpx;
|
|
overflow: hidden;
|
|
background-color: #ffffff;
|
|
border-radius: 20rpx;
|
|
}
|
|
|
|
.group-empty {
|
|
padding: 40rpx 0;
|
|
font-size: 24rpx;
|
|
color: $u-tips-color;
|
|
text-align: center;
|
|
}
|
|
|
|
.perm-row {
|
|
padding: 20rpx 0;
|
|
border-bottom: 1rpx solid $u-border-color;
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
}
|
|
|
|
.perm-head {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.perm-text {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-width: 0;
|
|
}
|
|
|
|
.perm-name {
|
|
font-size: 28rpx;
|
|
color: $u-main-color;
|
|
}
|
|
|
|
.perm-sub {
|
|
margin-top: 4rpx;
|
|
font-size: 22rpx;
|
|
color: $u-tips-color;
|
|
}
|
|
|
|
.dep-entry {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
margin-top: 10rpx;
|
|
}
|
|
|
|
.dep-entry-label {
|
|
margin-right: 12rpx;
|
|
font-size: 22rpx;
|
|
color: $u-tips-color;
|
|
}
|
|
|
|
.dep-entry-value {
|
|
font-size: 24rpx;
|
|
color: $u-type-primary;
|
|
}
|
|
|
|
.perm-actions {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-top: 16rpx;
|
|
}
|
|
|
|
.perm-action {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
|
|
.perm-action-disabled {
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.perm-action-label {
|
|
margin-top: 4rpx;
|
|
font-size: 22rpx;
|
|
color: $u-content-color;
|
|
}
|
|
|
|
.actor-row {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
min-height: 92rpx;
|
|
padding: 16rpx 0;
|
|
border-bottom: 1rpx solid $u-border-color;
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
}
|
|
</style>
|