398 lines
13 KiB
Vue
398 lines
13 KiB
Vue
<script setup lang="ts">
|
||
/**
|
||
* 角色详情(查看 / 新增 / 修改):旧 WebVue views/security/ActorOperation.vue 的移动端等价物
|
||
*
|
||
* 逐条对齐旧逻辑:
|
||
* - onLoad 读 id(=ActorName)与 mode(view|create|amend);
|
||
* - FindByKey 取 SecurityActorData,取不到提示"记录不存在";
|
||
* - 保存按 CreatedBy 有无决定 Update / Insert,成功后回填主键、退回只读;
|
||
* - 两个子表:组别明细(SecurityActorGroups,选 group-enquiry 多选)、
|
||
* 用户明细(SecurityUserActors,选 user-enquiry 多选),均按主键去重合并、删除即剔除。
|
||
*
|
||
* 旧页面用 dragModal 内嵌 Enquiry + provide/inject 做选择;移动端改成真跳选择页再回传
|
||
* (见 composables/useAdminPick),所以两个子表放在 admin-form 的 #extra 插槽里自绘。
|
||
*/
|
||
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 { SecurityActorData } from "@/data/auto/securityActorData";
|
||
import { SecurityActorGroupViewEntity } from "@/data/auto/securityActorGroupViewEntity";
|
||
import { SecurityUserActorViewEntity } from "@/data/auto/securityUserActorViewEntity";
|
||
import { usePagePermission } from "@/permission/permission-context";
|
||
import { smartBack } from "@/router/routes";
|
||
import securityActorService from "@/service/security/securityActorService";
|
||
|
||
const { t } = useLocale();
|
||
const perm = usePagePermission("actorOperation");
|
||
|
||
const data = ref<SecurityActorData>(new SecurityActorData());
|
||
const actor = computed(() => data.value.SecurityActor);
|
||
const readonly = ref(true);
|
||
const routeId = ref("");
|
||
|
||
/** 新增态判定:没有 CreatedBy 就说明是还没落库的新记录 */
|
||
const isNew = computed(() => !String(actor.value.CreatedBy ?? "").trim());
|
||
|
||
const heroTitle = computed(
|
||
() => String(actor.value.ActorLocalName || actor.value.ActorName || ""),
|
||
);
|
||
const heroChips = computed(() => {
|
||
const chips: string[] = [];
|
||
if (actor.value.ActorName) {
|
||
chips.push(`${t("ActorPage.actorName")}: ${actor.value.ActorName}`);
|
||
}
|
||
return chips;
|
||
});
|
||
|
||
const sections = computed<AdminFormSection[]>(() => {
|
||
const fields: AdminFormField[] = [
|
||
{
|
||
label: t("ActorPage.actorName"),
|
||
prop: "ActorName",
|
||
required: true,
|
||
// 已有记录时主键锁死:与旧页面"编辑态 ActorName 只读"一致
|
||
lock: routeId.value !== "",
|
||
},
|
||
{ label: t("ActorPage.actorLocalName"), prop: "ActorLocalName", required: true },
|
||
{ label: t("ActorPage.actorEngName"), prop: "ActorEngName" },
|
||
];
|
||
return [{ title: t("ActorPage.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;
|
||
});
|
||
|
||
async function load(): Promise<void> {
|
||
const result = await securityActorService.FindByKey(routeId.value);
|
||
if (!result?.SecurityActor?.ActorName) {
|
||
uni.showToast({ title: t("AdminPage.notExistTip"), icon: "none" });
|
||
return;
|
||
}
|
||
data.value = result;
|
||
if (!data.value.SecurityActorGroups) {
|
||
data.value.SecurityActorGroups = [];
|
||
}
|
||
if (!data.value.SecurityUserActors) {
|
||
data.value.SecurityUserActors = [];
|
||
}
|
||
}
|
||
|
||
function onFieldChange(prop: string, value: unknown): void {
|
||
(actor.value as Record<string, any>)[prop] = value;
|
||
}
|
||
|
||
async function onSave(): Promise<void> {
|
||
if (!String(actor.value.ActorName ?? "").trim()) {
|
||
uni.showToast({ title: t("ActorPage.actorNameRequired"), icon: "none" });
|
||
return;
|
||
}
|
||
if (!String(actor.value.ActorLocalName ?? "").trim()) {
|
||
uni.showToast({ title: t("ActorPage.localNameRequired"), icon: "none" });
|
||
return;
|
||
}
|
||
if (!data.value.SecurityUserActors) {
|
||
data.value.SecurityUserActors = [];
|
||
}
|
||
if (!data.value.SecurityActorGroups) {
|
||
data.value.SecurityActorGroups = [];
|
||
}
|
||
const isUpdate = !isNew.value;
|
||
const result = await (isUpdate
|
||
? securityActorService.Update(data.value)
|
||
: securityActorService.Insert(data.value));
|
||
if (result?.SecurityActor) {
|
||
data.value = result;
|
||
}
|
||
// 服务端保存返回可能不含子表字段:统一兑底成 [],否则模板读 .length 会报 undefined
|
||
if (!data.value.SecurityActorGroups) {
|
||
data.value.SecurityActorGroups = [];
|
||
}
|
||
if (!data.value.SecurityUserActors) {
|
||
data.value.SecurityUserActors = [];
|
||
}
|
||
routeId.value = String(actor.value.ActorName ?? "");
|
||
readonly.value = true;
|
||
uni.showToast({ title: t("AdminPage.savedTip"), icon: "none" });
|
||
}
|
||
|
||
async function onDelete(): Promise<void> {
|
||
await securityActorService.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");
|
||
}
|
||
|
||
// 组别选择:跳 group-enquiry(多选),回传后按 GroupName 去重合并
|
||
const groupPicker = useAdminPick<SecurityActorGroupViewEntity>("group");
|
||
|
||
function onAddGroup(): void {
|
||
groupPicker.open({ page: "groupEnquiry", params: { multiple: "1" } }, (rows) => {
|
||
const exists = new Set(data.value.SecurityActorGroups.map(item => item.GroupName));
|
||
for (const row of rows) {
|
||
const groupName = String(row.GroupName ?? "");
|
||
if (!groupName || exists.has(groupName)) {
|
||
continue;
|
||
}
|
||
const entity = new SecurityActorGroupViewEntity();
|
||
entity.ActorName = String(actor.value.ActorName ?? "");
|
||
entity.GroupName = groupName;
|
||
entity.GroupLocalName = String(row.GroupLocalName ?? "");
|
||
data.value.SecurityActorGroups.push(entity);
|
||
exists.add(groupName);
|
||
}
|
||
});
|
||
}
|
||
|
||
function onDeleteGroup(groupName: string): void {
|
||
data.value.SecurityActorGroups = data.value.SecurityActorGroups.filter(
|
||
item => item.GroupName !== groupName,
|
||
);
|
||
}
|
||
|
||
// 用户选择:跳 user-enquiry(多选),回传后按 UserName 去重合并
|
||
const userPicker = useAdminPick<SecurityUserActorViewEntity>("user");
|
||
|
||
function onAddUser(): void {
|
||
userPicker.open({ page: "userEnquiry", params: { multiple: "1" } }, (rows) => {
|
||
const exists = new Set(data.value.SecurityUserActors.map(item => item.UserName));
|
||
for (const row of rows) {
|
||
const userName = String(row.UserName ?? "");
|
||
if (!userName || exists.has(userName)) {
|
||
continue;
|
||
}
|
||
const entity = new SecurityUserActorViewEntity();
|
||
entity.UserName = userName;
|
||
entity.UserLocalName = String(row.UserLocalName ?? "");
|
||
entity.ActorName = String(actor.value.ActorName ?? "");
|
||
data.value.SecurityUserActors.push(entity);
|
||
exists.add(userName);
|
||
}
|
||
});
|
||
}
|
||
|
||
function onDeleteUser(userName: string): void {
|
||
data.value.SecurityUserActors = data.value.SecurityUserActors.filter(
|
||
item => item.UserName !== userName,
|
||
);
|
||
}
|
||
|
||
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('ActorPage.detailTitle')" :show-nav-back="true" :hero-title="heroTitle"
|
||
:hero-chips="heroChips" :sections="sections" :model="actor" :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("ActorPage.groupTitle") }}
|
||
</text>
|
||
<view v-if="!ro && perm.canAmend.value" class="group-add" @click="onAddGroup">
|
||
<u-icon name="plus" size="24" color="#059669" />
|
||
<text class="group-add-text">
|
||
{{ t("ActorPage.addGroupBut") }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="group-card">
|
||
<view v-if="!data.SecurityActorGroups.length" class="group-empty">
|
||
<text>
|
||
{{ t("ActorPage.groupEmpty") }}
|
||
</text>
|
||
</view>
|
||
|
||
<view v-for="item in data.SecurityActorGroups" :key="item.GroupName" class="actor-row">
|
||
<view class="actor-text">
|
||
<text class="actor-name">
|
||
{{ item.GroupLocalName || item.GroupName || t("ActorPage.unnamedGroup") }}
|
||
</text>
|
||
<text v-if="item.GroupLocalName" class="actor-sub">
|
||
{{ item.GroupName }}
|
||
</text>
|
||
</view>
|
||
<u-icon v-if="!ro && perm.canAmend.value" name="trash" size="34" color="#c4c8cf"
|
||
@click="onDeleteGroup(item.GroupName)" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 用户明细 -->
|
||
<view class="group">
|
||
<view class="group-head">
|
||
<text class="group-title">
|
||
{{ t("ActorPage.userTitle") }}
|
||
</text>
|
||
<view v-if="!ro && perm.canAmend.value" class="group-add" @click="onAddUser">
|
||
<u-icon name="plus" size="24" color="#059669" />
|
||
<text class="group-add-text">
|
||
{{ t("ActorPage.addUserBut") }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="group-card">
|
||
<view v-if="!data.SecurityUserActors.length" class="group-empty">
|
||
<text>
|
||
{{ t("ActorPage.userEmpty") }}
|
||
</text>
|
||
</view>
|
||
|
||
<view v-for="item in data.SecurityUserActors" :key="item.UserName" class="actor-row">
|
||
<view class="actor-text">
|
||
<text class="actor-name">
|
||
{{ item.UserLocalName || item.UserName || t("ActorPage.unnamedUser") }}
|
||
</text>
|
||
<text v-if="item.UserLocalName" class="actor-sub">
|
||
{{ item.UserName }}
|
||
</text>
|
||
</view>
|
||
<u-icon v-if="!ro && perm.canAmend.value" name="trash" size="34" color="#c4c8cf"
|
||
@click="onDeleteUser(item.UserName)" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
</admin-form>
|
||
</template>
|
||
|
||
<style lang="scss" scoped>
|
||
/* 子表明细:与 admin-form 的分组卡同一套视觉(细标题 + 白卡 + 行间细线) */
|
||
.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;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
}
|
||
|
||
.actor-text {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-width: 0;
|
||
}
|
||
|
||
.actor-name {
|
||
font-size: 28rpx;
|
||
color: $u-main-color;
|
||
}
|
||
|
||
.actor-sub {
|
||
margin-top: 4rpx;
|
||
font-size: 22rpx;
|
||
color: $u-tips-color;
|
||
}
|
||
</style>
|