ow
This commit is contained in:
1 parent
a8896cbfb2
commit
282d8ce583
20 files changed
+1133
-401
No files matched your search
@@ -0,0 +1,465 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 通用"弹窗选择器"(底部抽屉):替代旧的跳页选择(useAdminPick → 全页面 enquiry → navigateBack)。
|
||||
*
|
||||
* 对齐旧 WebVue 的 <dragModal> + 内嵌 Enquiry 组件模式:
|
||||
* 在调用方页面内弹出,选完 confirm 回传行数据,不离开当前编辑上下文。
|
||||
* 用户可通过 X / 遮罩 / 取消按钮随时关闭,彻底解决"进去无法返回"的问题。
|
||||
*
|
||||
* 列表用 scroll-view + 手动分页(触底加载更多),避免 z-paging 的 fixed 定位在弹层内冲突。
|
||||
*/
|
||||
import type { PropType } from "vue";
|
||||
import type { AdminRow } from "@/admin/admin-types";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, ref, watch } from "vue";
|
||||
|
||||
/** 列定义:精简版,第一列为 title,其余为副信息 */
|
||||
export interface PickColumn {
|
||||
label: string;
|
||||
prop: string;
|
||||
}
|
||||
|
||||
/** 取数函数签名 */
|
||||
type PickLoader = (
|
||||
page: number,
|
||||
pageSize: number,
|
||||
keyword: string,
|
||||
fixedFilters: Record<string, string>,
|
||||
) => Promise<{ rows: AdminRow[]; total: number }>;
|
||||
|
||||
const props = defineProps({
|
||||
/** 弹层是否展开(v-model:visible) */
|
||||
visible: { type: Boolean, default: false },
|
||||
/** 弹窗标题 */
|
||||
title: { type: String, default: "" },
|
||||
/** 显示列:第一列为卡片主标题,其余为副字段 */
|
||||
columns: { type: Array as PropType<PickColumn[]>, required: true },
|
||||
/** 取数函数 */
|
||||
loader: { type: Function as PropType<PickLoader>, required: true },
|
||||
/** 行唯一标识字段 */
|
||||
rowKey: { type: String, required: true },
|
||||
/** 主标题字段(优先于 columns[0].prop) */
|
||||
titleKey: { type: String, default: "" },
|
||||
/** 是否多选 */
|
||||
multiple: { type: Boolean, default: false },
|
||||
/** 固定过滤条件(如 { dependent: "xxx" }),透传给 loader */
|
||||
fixedFilters: { type: Object as PropType<Record<string, string>>, default: () => ({}) },
|
||||
/** 每页条数 */
|
||||
pageSize: { type: Number, default: 20 },
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "update:visible", value: boolean): void;
|
||||
(e: "close"): void;
|
||||
(e: "confirm", rows: AdminRow[]): void;
|
||||
}>();
|
||||
|
||||
const { t } = useLocale();
|
||||
|
||||
// ===== 列表状态 =====
|
||||
const rows = ref<AdminRow[]>([]);
|
||||
const total = ref(0);
|
||||
const page = ref(1);
|
||||
const loading = ref(false);
|
||||
const keyword = ref("");
|
||||
|
||||
// ===== 选择状态 =====
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
const selectedPool = ref<Record<string, AdminRow>>({});
|
||||
|
||||
const titleProp = computed(() => props.titleKey || props.columns[0]?.prop || "");
|
||||
|
||||
const hasMore = computed(() => rows.value.length < total.value);
|
||||
|
||||
const selectedRows = computed(() =>
|
||||
selectedKeys.value.map(key => selectedPool.value[key]).filter(Boolean),
|
||||
);
|
||||
|
||||
function keyOf(row: AdminRow): string {
|
||||
return String(row[props.rowKey] ?? "");
|
||||
}
|
||||
|
||||
function isSelected(row: AdminRow): boolean {
|
||||
return selectedKeys.value.includes(keyOf(row));
|
||||
}
|
||||
|
||||
function getTitle(row: AdminRow): string {
|
||||
return String(row[titleProp.value] ?? "");
|
||||
}
|
||||
|
||||
function getSubtitles(): PickColumn[] {
|
||||
return props.columns.filter(col => col.prop !== titleProp.value);
|
||||
}
|
||||
|
||||
// ===== 数据加载 =====
|
||||
async function loadData(reset = false): Promise<void> {
|
||||
if (loading.value) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
if (reset) {
|
||||
page.value = 1;
|
||||
rows.value = [];
|
||||
total.value = 0;
|
||||
}
|
||||
try {
|
||||
const result = await props.loader(page.value, props.pageSize, keyword.value, props.fixedFilters);
|
||||
if (reset) {
|
||||
rows.value = result.rows;
|
||||
}
|
||||
else {
|
||||
rows.value = [...rows.value, ...result.rows];
|
||||
}
|
||||
total.value = result.total;
|
||||
}
|
||||
catch {
|
||||
// 错误由 http.interceptor 统一提示
|
||||
}
|
||||
finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function loadMore(): void {
|
||||
if (!hasMore.value || loading.value) {
|
||||
return;
|
||||
}
|
||||
page.value += 1;
|
||||
loadData();
|
||||
}
|
||||
|
||||
function onSearch(): void {
|
||||
loadData(true);
|
||||
}
|
||||
|
||||
// ===== 选择操作 =====
|
||||
function toggleSelect(row: AdminRow): void {
|
||||
const key = keyOf(row);
|
||||
if (!props.multiple) {
|
||||
// 单选:直接确认
|
||||
emit("confirm", [row]);
|
||||
closePopup();
|
||||
return;
|
||||
}
|
||||
const idx = selectedKeys.value.indexOf(key);
|
||||
const pool = { ...selectedPool.value };
|
||||
if (idx >= 0) {
|
||||
selectedKeys.value = selectedKeys.value.filter(k => k !== key);
|
||||
delete pool[key];
|
||||
}
|
||||
else {
|
||||
selectedKeys.value = [...selectedKeys.value, key];
|
||||
pool[key] = row;
|
||||
}
|
||||
selectedPool.value = pool;
|
||||
}
|
||||
|
||||
function toggleSelectAll(): void {
|
||||
if (!props.multiple) {
|
||||
return;
|
||||
}
|
||||
const visibleKeys = rows.value.map(row => keyOf(row));
|
||||
const pool = { ...selectedPool.value };
|
||||
if (selectedKeys.value.length >= rows.value.length && rows.value.length > 0) {
|
||||
// 取消全选(仅当前可见)
|
||||
selectedKeys.value = selectedKeys.value.filter(k => !visibleKeys.includes(k));
|
||||
for (const key of visibleKeys) {
|
||||
delete pool[key];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const row of rows.value) {
|
||||
pool[keyOf(row)] = row;
|
||||
}
|
||||
selectedKeys.value = [...new Set([...selectedKeys.value, ...visibleKeys])];
|
||||
}
|
||||
selectedPool.value = pool;
|
||||
}
|
||||
|
||||
function onConfirm(): void {
|
||||
if (!selectedRows.value.length) {
|
||||
uni.showToast({ title: t("AdminPage.pickNoneTip"), icon: "none" });
|
||||
return;
|
||||
}
|
||||
emit("confirm", selectedRows.value);
|
||||
closePopup();
|
||||
}
|
||||
|
||||
// ===== 生命周期 =====
|
||||
function resetState(): void {
|
||||
rows.value = [];
|
||||
total.value = 0;
|
||||
page.value = 1;
|
||||
keyword.value = "";
|
||||
selectedKeys.value = [];
|
||||
selectedPool.value = {};
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
function closePopup(): void {
|
||||
emit("update:visible", false);
|
||||
emit("close");
|
||||
}
|
||||
|
||||
// 每次展开重置并加载
|
||||
watch(
|
||||
() => props.visible,
|
||||
(on) => {
|
||||
if (on) {
|
||||
resetState();
|
||||
loadData(true);
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<u-popup :model-value="props.visible" mode="bottom" round="24" :safe-area-inset-bottom="true"
|
||||
:close-on-click-overlay="true" @close="closePopup">
|
||||
<view class="pick-popup">
|
||||
<!-- 头部:标题 + 关闭 -->
|
||||
<view class="pick-popup__header">
|
||||
<text class="pick-popup__title">
|
||||
{{ props.title }}
|
||||
</text>
|
||||
<view class="pick-popup__close" @tap="closePopup">
|
||||
<u-icon name="close-circle-fill" size="40" color="#9aa0a6" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<view class="pick-popup__search">
|
||||
<u-search v-model="keyword" :placeholder="t('AdminPage.searchPlaceholder')" :show-action="false"
|
||||
:animation="false" @search="onSearch" @clear="onSearch" />
|
||||
</view>
|
||||
|
||||
<!-- 列表 -->
|
||||
<scroll-view scroll-y class="pick-popup__list" @scrolltolower="loadMore">
|
||||
<!-- 加载中占位(首次) -->
|
||||
<view v-if="loading && !rows.length" class="pick-popup__loading">
|
||||
<u-loading mode="circle" />
|
||||
</view>
|
||||
|
||||
<!-- 空态 -->
|
||||
<view v-else-if="!rows.length" class="pick-popup__empty">
|
||||
<text>{{ t("AdminPage.emptyText") }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 数据行 -->
|
||||
<template v-else>
|
||||
<view v-for="row in rows" :key="keyOf(row)" class="pick-popup__row"
|
||||
:class="{ 'is-selected': isSelected(row) }" @tap="toggleSelect(row)">
|
||||
<!-- 多选勾选框 -->
|
||||
<view v-if="props.multiple" class="pick-popup__check" :class="{ 'is-on': isSelected(row) }">
|
||||
<u-icon v-if="isSelected(row)" name="checkbox-mark" size="24" color="#ffffff" />
|
||||
</view>
|
||||
<view class="pick-popup__content">
|
||||
<text class="pick-popup__main">
|
||||
{{ getTitle(row) }}
|
||||
</text>
|
||||
<view v-for="col in getSubtitles()" :key="col.prop" class="pick-popup__sub">
|
||||
<text class="pick-popup__sub-label">{{ col.label }}</text>
|
||||
<text class="pick-popup__sub-value">{{ row[col.prop] ?? "" }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部加载状态 -->
|
||||
<view class="pick-popup__footer-hint">
|
||||
<text v-if="loading">{{ t("AdminPage.pickLoadingMore") }}</text>
|
||||
<text v-else-if="!hasMore">{{ t("AdminPage.pickNoMore") }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部操作栏(多选时) -->
|
||||
<view v-if="props.multiple" class="pick-popup__actions">
|
||||
<view class="pick-popup__action-left" @tap="toggleSelectAll">
|
||||
<text class="pick-popup__toggle">
|
||||
{{ selectedKeys.length >= rows.length && rows.length > 0 ? t("AdminPage.unselectAllBut") :
|
||||
t("AdminPage.selectAllBut") }}
|
||||
</text>
|
||||
<text class="pick-popup__count">
|
||||
{{ t("AdminPage.selectedText", { count: selectedKeys.length }) }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="pick-popup__action-btns">
|
||||
<view class="pick-popup__btn pick-popup__btn--cancel" @tap="closePopup">
|
||||
<text>{{ t("AdminPage.cancelBut") }}</text>
|
||||
</view>
|
||||
<view class="pick-popup__btn pick-popup__btn--confirm"
|
||||
:class="{ 'is-disabled': !selectedKeys.length }" @tap="onConfirm">
|
||||
<text>{{ t("AdminPage.confirmBut") }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$brand-green: #2f7d5c;
|
||||
$brand-green-light: #47a678;
|
||||
|
||||
.pick-popup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 80vh;
|
||||
background-color: #f7f8fa;
|
||||
}
|
||||
|
||||
.pick-popup__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 28rpx 32rpx 16rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
}
|
||||
|
||||
.pick-popup__title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1f2329;
|
||||
}
|
||||
|
||||
.pick-popup__close {
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.pick-popup__search {
|
||||
padding: 12rpx 24rpx;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.pick-popup__list {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 16rpx 24rpx;
|
||||
}
|
||||
|
||||
.pick-popup__loading,
|
||||
.pick-popup__empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80rpx 0;
|
||||
font-size: 26rpx;
|
||||
color: #9aa0a6;
|
||||
}
|
||||
|
||||
.pick-popup__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 12rpx;
|
||||
padding: 24rpx 28rpx;
|
||||
border-radius: 16rpx;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.pick-popup__row.is-selected {
|
||||
background-color: rgba(47, 125, 92, 0.06);
|
||||
border: 2rpx solid $brand-green;
|
||||
}
|
||||
|
||||
.pick-popup__check {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border: 2rpx solid #c4c8cf;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.pick-popup__check.is-on {
|
||||
background-color: $brand-green;
|
||||
border-color: $brand-green;
|
||||
}
|
||||
|
||||
.pick-popup__content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.pick-popup__main {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #1f2329;
|
||||
}
|
||||
|
||||
.pick-popup__sub {
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
margin-top: 6rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.pick-popup__sub-label {
|
||||
color: #9aa0a6;
|
||||
}
|
||||
|
||||
.pick-popup__sub-value {
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.pick-popup__footer-hint {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 24rpx 0;
|
||||
font-size: 24rpx;
|
||||
color: #9aa0a6;
|
||||
}
|
||||
|
||||
.pick-popup__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 32rpx;
|
||||
border-top: 2rpx solid #eee;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.pick-popup__action-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4rpx;
|
||||
}
|
||||
|
||||
.pick-popup__toggle {
|
||||
font-size: 26rpx;
|
||||
color: $brand-green;
|
||||
}
|
||||
|
||||
.pick-popup__count {
|
||||
font-size: 22rpx;
|
||||
color: #9aa0a6;
|
||||
}
|
||||
|
||||
.pick-popup__action-btns {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.pick-popup__btn {
|
||||
padding: 16rpx 40rpx;
|
||||
border-radius: 40rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.pick-popup__btn--cancel {
|
||||
color: #606266;
|
||||
background-color: #f2f3f5;
|
||||
}
|
||||
|
||||
.pick-popup__btn--confirm {
|
||||
color: #ffffff;
|
||||
background: linear-gradient(135deg, $brand-green-light, $brand-green);
|
||||
}
|
||||
|
||||
.pick-popup__btn--confirm.is-disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,380 @@
|
||||
/**
|
||||
* 安全域"弹窗选择器"配置工厂:为 admin-pick-popup 提供 columns / loader / rowKey / titleKey。
|
||||
*
|
||||
* 每个工厂对应一种被选实体,内部封装 service 调用与 criteria 构建。
|
||||
* fixedFilters 是一个响应式 ref,调用方在打开弹窗前动态设值(如 { dependent: "xxx" }),
|
||||
* loader 会将其作为等值条件注入 criteria.Filter。
|
||||
*
|
||||
* keyword 搜索统一映射到各实体的"主名称字段"(CONTAIN 模糊)。
|
||||
*/
|
||||
import type { PickColumn } from "@/components/admin-pick-popup/admin-pick-popup.vue";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, ref } from "vue";
|
||||
import { SearchFilter } from "@/data/auto/searchFilter";
|
||||
import { SecurityActorEntity } from "@/data/auto/securityActorEntity";
|
||||
import { SecurityActorSearchCriteria } from "@/data/auto/securityActorSearchCriteria";
|
||||
import { SecurityFunctionDisplayData } from "@/data/auto/securityFunctionDisplayData";
|
||||
import { SecurityFunctionSearchCriteria } from "@/data/auto/securityFunctionSearchCriteria";
|
||||
import { SecurityGroupEntity } from "@/data/auto/securityGroupEntity";
|
||||
import { SecurityGroupSearchCriteria } from "@/data/auto/securityGroupSearchCriteria";
|
||||
import { SecurityHelpFeatureEntity } from "@/data/auto/securityHelpFeatureEntity";
|
||||
import { SecurityHelpFunctionSearchCriteria } from "@/data/auto/securityHelpFunctionSearchCriteria";
|
||||
import { SecurityPermissionDependentEntity } from "@/data/auto/securityPermissionDependentEntity";
|
||||
import { SecurityPermissionEntity } from "@/data/auto/securityPermissionEntity";
|
||||
import { SecurityPermissionSearchCriteria } from "@/data/auto/securityPermissionSearchCriteria";
|
||||
import { SecurityUserEntity } from "@/data/auto/securityUserEntity";
|
||||
import { SecurityUserSearchCriteria } from "@/data/auto/securityUserSearchCriteria";
|
||||
import { SearchFilterOperatorPolicy } from "@/data/custom/searchFilterOperatorPolicy";
|
||||
import { SearchFilterTypePolicy } from "@/data/custom/searchFilterTypePolicy";
|
||||
import securityActorService from "@/service/security/securityActorService";
|
||||
import securityFunctionService from "@/service/security/securityFunctionService";
|
||||
import securityGroupService from "@/service/security/securityGroupService";
|
||||
import securityHelpFunctionService from "@/service/security/securityHelpFunctionService";
|
||||
import securityPermissionService from "@/service/security/securityPermissionService";
|
||||
import securityUserService from "@/service/security/securityUserService";
|
||||
|
||||
/** 构建一条文本 CONTAIN 过滤 */
|
||||
function containFilter(column: string, value: string): SearchFilter {
|
||||
const f = new SearchFilter();
|
||||
f.Column = column;
|
||||
f.Type = SearchFilterTypePolicy.TEXT;
|
||||
f.Operator = SearchFilterOperatorPolicy.CONTAIN;
|
||||
f.Value = value;
|
||||
return f;
|
||||
}
|
||||
|
||||
/** 构建一条等值过滤 */
|
||||
function equalFilter(column: string, value: string): SearchFilter {
|
||||
const f = new SearchFilter();
|
||||
f.Column = column;
|
||||
f.Type = SearchFilterTypePolicy.TEXT;
|
||||
f.Operator = SearchFilterOperatorPolicy.EQUAL_TO;
|
||||
f.Value = value;
|
||||
return f;
|
||||
}
|
||||
|
||||
/** 通用返回值 */
|
||||
export interface PickerConfig {
|
||||
columns: PickColumn[];
|
||||
loader: (
|
||||
page: number,
|
||||
pageSize: number,
|
||||
keyword: string,
|
||||
fixedFilters: Record<string, string>,
|
||||
) => Promise<{ rows: any[]; total: number }>;
|
||||
rowKey: string;
|
||||
titleKey: string;
|
||||
fixedFilters: Record<string, string>;
|
||||
}
|
||||
|
||||
// ===== Permission =====
|
||||
export function usePermissionPickConfig() {
|
||||
const { t } = useLocale();
|
||||
const fixedFilters = ref<Record<string, string>>({});
|
||||
|
||||
const columns = computed<PickColumn[]>(() => [
|
||||
{
|
||||
label: t("PermissionPage.permissionName"),
|
||||
prop: SecurityPermissionEntity.PERMISSION_NAME,
|
||||
},
|
||||
{
|
||||
label: t("PermissionPage.description"),
|
||||
prop: SecurityPermissionEntity.DESCRIPTION,
|
||||
},
|
||||
{
|
||||
label: t("PermissionPage.dependent"),
|
||||
prop: SecurityPermissionEntity.DEPENDENT,
|
||||
},
|
||||
]);
|
||||
|
||||
async function loader(page: number, pageSize: number, keyword: string) {
|
||||
const criteria = new SecurityPermissionSearchCriteria();
|
||||
criteria.GridColumns = [];
|
||||
criteria.page = page;
|
||||
criteria.limit = pageSize;
|
||||
criteria.orderby = SecurityPermissionEntity.DB_PERMISSION_NAME;
|
||||
const filters: SearchFilter[] = [];
|
||||
if (keyword) {
|
||||
filters.push(
|
||||
containFilter(SecurityPermissionEntity.DB_PERMISSION_NAME, keyword),
|
||||
);
|
||||
}
|
||||
criteria.Filter = filters;
|
||||
const data = await securityPermissionService.FindBySearchCriteria(criteria);
|
||||
return { rows: data?.Permissions ?? [], total: data?.Total ?? 0 };
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
loader,
|
||||
rowKey: SecurityPermissionEntity.PERMISSION_NAME,
|
||||
titleKey: SecurityPermissionEntity.PERMISSION_NAME,
|
||||
fixedFilters,
|
||||
};
|
||||
}
|
||||
|
||||
// ===== Permission Dependent =====
|
||||
export function useDependentPickConfig() {
|
||||
const { t } = useLocale();
|
||||
const fixedFilters = ref<Record<string, string>>({});
|
||||
|
||||
const columns = computed<PickColumn[]>(() => [
|
||||
{
|
||||
label: t("PermissionPage.dependentDetailName"),
|
||||
prop: SecurityPermissionDependentEntity.DEPENDENT_DETAIL_NAME,
|
||||
},
|
||||
{
|
||||
label: t("PermissionPage.dependentDetailDescription"),
|
||||
prop: SecurityPermissionDependentEntity.DEPENDENT_DETAIL_DESCRIPTION,
|
||||
},
|
||||
]);
|
||||
|
||||
async function loader(
|
||||
page: number,
|
||||
pageSize: number,
|
||||
keyword: string,
|
||||
filters: Record<string, string>,
|
||||
) {
|
||||
const criteria = new SecurityPermissionSearchCriteria();
|
||||
criteria.GridColumns = [];
|
||||
criteria.page = page;
|
||||
criteria.limit = pageSize;
|
||||
criteria.orderby =
|
||||
SecurityPermissionDependentEntity.DB_DEPENDENT_DETAIL_NAME;
|
||||
const filterList: SearchFilter[] = [];
|
||||
// 固定过滤:按 dependent 等值(由调用方设定)
|
||||
if (filters.dependent) {
|
||||
filterList.push(
|
||||
equalFilter(
|
||||
SecurityPermissionDependentEntity.DB_DEPENDENT,
|
||||
filters.dependent,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (keyword) {
|
||||
filterList.push(
|
||||
containFilter(
|
||||
SecurityPermissionDependentEntity.DB_DEPENDENT_DETAIL_NAME,
|
||||
keyword,
|
||||
),
|
||||
);
|
||||
}
|
||||
criteria.Filter = filterList;
|
||||
const data =
|
||||
await securityPermissionService.FindDependentsBySearchCriteria(criteria);
|
||||
return { rows: data?.Dependents ?? [], total: data?.Total ?? 0 };
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
loader,
|
||||
rowKey: SecurityPermissionDependentEntity.DEPENDENT_DETAIL_NAME,
|
||||
titleKey: SecurityPermissionDependentEntity.DEPENDENT_DETAIL_NAME,
|
||||
fixedFilters,
|
||||
};
|
||||
}
|
||||
|
||||
// ===== Actor =====
|
||||
export function useActorPickConfig() {
|
||||
const { t } = useLocale();
|
||||
const fixedFilters = ref<Record<string, string>>({});
|
||||
|
||||
const columns = computed<PickColumn[]>(() => [
|
||||
{
|
||||
label: t("UserPage.actorLocalName"),
|
||||
prop: SecurityActorEntity.ACTOR_LOCAL_NAME,
|
||||
},
|
||||
{ label: t("UserPage.actorName"), prop: SecurityActorEntity.ACTOR_NAME },
|
||||
]);
|
||||
|
||||
async function loader(page: number, pageSize: number, keyword: string) {
|
||||
const criteria = new SecurityActorSearchCriteria();
|
||||
criteria.GridColumns = [];
|
||||
criteria.page = page;
|
||||
criteria.limit = pageSize;
|
||||
const filters: SearchFilter[] = [];
|
||||
if (keyword) {
|
||||
filters.push(
|
||||
containFilter(SecurityActorEntity.DB_ACTOR_LOCAL_NAME, keyword),
|
||||
);
|
||||
}
|
||||
criteria.Filter = filters;
|
||||
const data = await securityActorService.FindBySearchCriteria(criteria);
|
||||
return { rows: data?.Datas ?? [], total: data?.Total ?? 0 };
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
loader,
|
||||
rowKey: SecurityActorEntity.ACTOR_NAME,
|
||||
titleKey: SecurityActorEntity.ACTOR_LOCAL_NAME,
|
||||
fixedFilters,
|
||||
};
|
||||
}
|
||||
|
||||
// ===== Group =====
|
||||
export function useGroupPickConfig() {
|
||||
const { t } = useLocale();
|
||||
const fixedFilters = ref<Record<string, string>>({});
|
||||
|
||||
const columns = computed<PickColumn[]>(() => [
|
||||
{ label: t("GroupPage.groupName"), prop: SecurityGroupEntity.GROUP_NAME },
|
||||
{
|
||||
label: t("GroupPage.groupLocalName"),
|
||||
prop: SecurityGroupEntity.GROUP_LOCAL_NAME,
|
||||
},
|
||||
]);
|
||||
|
||||
async function loader(page: number, pageSize: number, keyword: string) {
|
||||
const criteria = new SecurityGroupSearchCriteria();
|
||||
criteria.GridColumns = [];
|
||||
criteria.page = page;
|
||||
criteria.limit = pageSize;
|
||||
criteria.orderby = SecurityGroupEntity.DB_GROUP_NAME;
|
||||
const filters: SearchFilter[] = [];
|
||||
if (keyword) {
|
||||
filters.push(containFilter(SecurityGroupEntity.DB_GROUP_NAME, keyword));
|
||||
}
|
||||
criteria.Filter = filters;
|
||||
const data = await securityGroupService.FindBySearchCriteria(criteria);
|
||||
return { rows: data?.Datas ?? [], total: data?.Total ?? 0 };
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
loader,
|
||||
rowKey: SecurityGroupEntity.GROUP_NAME,
|
||||
titleKey: SecurityGroupEntity.GROUP_NAME,
|
||||
fixedFilters,
|
||||
};
|
||||
}
|
||||
|
||||
// ===== User =====
|
||||
export function useUserPickConfig() {
|
||||
const { t } = useLocale();
|
||||
const fixedFilters = ref<Record<string, string>>({});
|
||||
|
||||
const columns = computed<PickColumn[]>(() => [
|
||||
{
|
||||
label: t("UserPage.userLocalName"),
|
||||
prop: SecurityUserEntity.USER_LOCAL_NAME,
|
||||
},
|
||||
{ label: t("UserPage.userName"), prop: SecurityUserEntity.USER_NAME },
|
||||
]);
|
||||
|
||||
async function loader(page: number, pageSize: number, keyword: string) {
|
||||
const criteria = new SecurityUserSearchCriteria();
|
||||
criteria.GridColumns = [];
|
||||
criteria.page = page;
|
||||
criteria.limit = pageSize;
|
||||
const filters: SearchFilter[] = [];
|
||||
if (keyword) {
|
||||
filters.push(containFilter(SecurityUserEntity.DB_USER_NAME, keyword));
|
||||
}
|
||||
criteria.Filter = filters;
|
||||
const data = await securityUserService.FindBySearchCriteria(criteria);
|
||||
return { rows: data?.Datas ?? [], total: data?.Total ?? 0 };
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
loader,
|
||||
rowKey: SecurityUserEntity.USER_NAME,
|
||||
titleKey: SecurityUserEntity.USER_LOCAL_NAME,
|
||||
fixedFilters,
|
||||
};
|
||||
}
|
||||
|
||||
// ===== Function =====
|
||||
export function useFunctionPickConfig() {
|
||||
const { t } = useLocale();
|
||||
const fixedFilters = ref<Record<string, string>>({});
|
||||
|
||||
const columns = computed<PickColumn[]>(() => [
|
||||
{
|
||||
label: t("FunctionPage.functionName"),
|
||||
prop: SecurityFunctionDisplayData.FUNCTION_NAME,
|
||||
},
|
||||
{
|
||||
label: t("FunctionPage.functionLocalName"),
|
||||
prop: SecurityFunctionDisplayData.FUNCTION_LOCAL_NAME,
|
||||
},
|
||||
]);
|
||||
|
||||
async function loader(
|
||||
page: number,
|
||||
pageSize: number,
|
||||
keyword: string,
|
||||
filters: Record<string, string>,
|
||||
) {
|
||||
const criteria = new SecurityFunctionSearchCriteria();
|
||||
criteria.GridColumns = [];
|
||||
criteria.page = page;
|
||||
criteria.limit = pageSize;
|
||||
criteria.orderby = SecurityFunctionDisplayData.DB_FUNCTION_NAME;
|
||||
const filterList: SearchFilter[] = [];
|
||||
if (keyword) {
|
||||
filterList.push(
|
||||
containFilter(SecurityFunctionDisplayData.DB_FUNCTION_NAME, keyword),
|
||||
);
|
||||
}
|
||||
// 固定过滤:按 FunctionIndc 等值(如 indc=N 只要目录、indc=Y 只要功能)
|
||||
if (filters.indc) {
|
||||
filterList.push(
|
||||
equalFilter(SecurityFunctionDisplayData.DB_FUNCTION_INDC, filters.indc),
|
||||
);
|
||||
}
|
||||
criteria.Filter = filterList;
|
||||
const data = await securityFunctionService.FindBySearchCriteria(criteria);
|
||||
return {
|
||||
rows: data?.SecurityFunctionDisplayDatas ?? [],
|
||||
total: data?.Total ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
loader,
|
||||
rowKey: SecurityFunctionDisplayData.FUNCTION_NAME,
|
||||
titleKey: SecurityFunctionDisplayData.FUNCTION_NAME,
|
||||
fixedFilters,
|
||||
};
|
||||
}
|
||||
|
||||
// ===== Help Feature =====
|
||||
export function useHelpFeaturePickConfig() {
|
||||
const { t } = useLocale();
|
||||
const fixedFilters = ref<Record<string, string>>({});
|
||||
|
||||
const columns = computed<PickColumn[]>(() => [
|
||||
{ label: t("HelpFeaturePage.name"), prop: SecurityHelpFeatureEntity.NAME },
|
||||
{
|
||||
label: t("HelpFeaturePage.description"),
|
||||
prop: SecurityHelpFeatureEntity.DESCRIPTION,
|
||||
},
|
||||
]);
|
||||
|
||||
async function loader(page: number, pageSize: number, keyword: string) {
|
||||
const criteria = new SecurityHelpFunctionSearchCriteria();
|
||||
criteria.GridColumns = [];
|
||||
criteria.page = page;
|
||||
criteria.limit = pageSize;
|
||||
criteria.orderby = SecurityHelpFeatureEntity.DB_NAME;
|
||||
const filters: SearchFilter[] = [];
|
||||
if (keyword) {
|
||||
filters.push(containFilter(SecurityHelpFeatureEntity.DB_NAME, keyword));
|
||||
}
|
||||
criteria.Filter = filters;
|
||||
const data =
|
||||
await securityHelpFunctionService.FindFeatureBySearchCriteria(criteria);
|
||||
return { rows: data?.Features ?? [], total: data?.Total ?? 0 };
|
||||
}
|
||||
|
||||
return {
|
||||
columns,
|
||||
loader,
|
||||
rowKey: SecurityHelpFeatureEntity.NAME,
|
||||
titleKey: SecurityHelpFeatureEntity.NAME,
|
||||
fixedFilters,
|
||||
};
|
||||
}
|
||||
@@ -545,6 +545,7 @@
|
||||
},
|
||||
"SharePage": {
|
||||
"title": "Family Groups",
|
||||
"searchPlaceholder": "Search group name",
|
||||
"createTitle": "Create Group",
|
||||
"noAccess": "This group is private and you are not a member",
|
||||
"unnamed": "Unnamed",
|
||||
@@ -599,6 +600,7 @@
|
||||
},
|
||||
"MediaPage": {
|
||||
"title": "Media",
|
||||
"searchPlaceholder": "Search album name",
|
||||
"createTitle": "New Album",
|
||||
"segPhoto": "Photos",
|
||||
"segAudio": "Music",
|
||||
@@ -690,7 +692,9 @@
|
||||
"menuSection": "Features",
|
||||
"menuEmptyText": "The server returned no authorized function",
|
||||
"loadingTip": "Loading…",
|
||||
"savingTip": "Saving…"
|
||||
"savingTip": "Saving…",
|
||||
"pickLoadingMore": "Pull up for more…",
|
||||
"pickNoMore": "— No more —"
|
||||
},
|
||||
"UserPage": {
|
||||
"title": "User Query",
|
||||
|
||||
@@ -545,6 +545,7 @@
|
||||
},
|
||||
"SharePage": {
|
||||
"title": "家庭群组",
|
||||
"searchPlaceholder": "搜索群组名称",
|
||||
"createTitle": "创建群组",
|
||||
"noAccess": "该群组为私密,您还不是成员",
|
||||
"unnamed": "未命名",
|
||||
@@ -599,6 +600,7 @@
|
||||
},
|
||||
"MediaPage": {
|
||||
"title": "多媒体",
|
||||
"searchPlaceholder": "搜索专辑名称",
|
||||
"createTitle": "新建专辑",
|
||||
"segPhoto": "相册",
|
||||
"segAudio": "音乐",
|
||||
@@ -690,7 +692,9 @@
|
||||
"menuSection": "功能菜单",
|
||||
"menuEmptyText": "后端没有返回你有权限的功能",
|
||||
"loadingTip": "加载中…",
|
||||
"savingTip": "保存中…"
|
||||
"savingTip": "保存中…",
|
||||
"pickLoadingMore": "上拉加载更多…",
|
||||
"pickNoMore": "— 没有更多了 —"
|
||||
},
|
||||
"UserPage": {
|
||||
"title": "用户查询",
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
|
||||
- **保留** `bindOption` 数据模型(后端列名 / `Type` / `Operator` / `SelectOptions`)与查询语义(分页、条件、`orderby`),因为后端 `FindBySearchCriteria` 吃的就是这个形状;
|
||||
- **砍掉** 多列网格、列配置、打印、排序表头——手机没有对应交互;
|
||||
- **重写** 列表为卡片流、详情为单列卡、选择为"真跳一页再回传"。
|
||||
- **重写** 列表为卡片流、详情为单列卡、选择为"页内底部弹窗"(`admin-pick-popup`,见 §3.3)。
|
||||
|
||||
复用分三层,边界清楚,换域移植(Group/Function/Dms/Publish…)时机械部分零改动:
|
||||
|
||||
| 层 | 文件 | 认识什么 | 不认识什么 |
|
||||
| --- | --- | --- | --- |
|
||||
| 纯函数 | `admin/admin-filter.ts`、`admin-date.ts`、`admin-column.ts`、`admin-types.ts` | SearchFilter / BindOption / 日期口径 | vue、uni、service |
|
||||
| composable | `useAdminList`、`useAdminPick`、`useAdminMenu` | 上面那层 + vue 响应式 | z-paging、service、权限名 |
|
||||
| 组件 | `admin-list`、`admin-form`、`admin-filter-drawer` | z-paging + uview-pro | service、**权限** |
|
||||
| composable | `useAdminList`、`useAdminPick`、`usePickerConfigs`、`useAdminMenu` | 上面那层 + vue 响应式 | z-paging、service、权限名 |
|
||||
| 组件 | `admin-list`、`admin-form`、`admin-filter-drawer`、`admin-pick-popup` | z-paging / u-popup + uview-pro | service、**权限** |
|
||||
|
||||
**组件不判权限**:按钮由页面按 `canCreate/canAmend/canDelete` 组好数组(详情)或用 `v-if`(列表工具条)传进来。这样公共组件不需要知道任何一个域的权限名。
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
| --- | --- | --- | --- |
|
||||
| `userEnquiry` | `pages/security/user-enquiry` | `UserEnquiry.vue` | 用户列表:三条筛选(用户名/中文名/创建日期区间)、每页 20、按创建日期倒序、行点进详情、多选批量作废 |
|
||||
| `userOperation` | `pages/security/user-operation` | `UserOperation.vue` | 用户详情:查看/新增/修改,`?id=&mode=`;角色明细在 `#extra` 插槽自绘 |
|
||||
| `actorEnquiry` | `pages/security/actor-enquiry` | `ActorEnquiry.vue` | 角色查询,**双用途**(见 §3.3) |
|
||||
| `actorEnquiry` | `pages/security/actor-enquiry` | `ActorEnquiry.vue` | 角色查询,纯列表页(选角色改由调用方弹窗,见 §3.3) |
|
||||
| `dashboard` | `pages/dashboard/dashboard` | `layout` 菜单 | 入口九宫格,`FindMenuByUsername` 驱动(见 §5) |
|
||||
|
||||
列表页写法(`user-enquiry.vue` 为准):`reactive(bindOptions)` + 一个只碰具体 service 的 `fetchData(query)` 交给 `useAdminList`;解构出的 `filterCount/filterVisible/draft/searchToken` 是 ref/computed,模板自动 unwrap,方法仍走 `state.xxx`。取数排在 `useAuthGuard` 之后(`admin-list` 的 `auto` 默认 false,首屏手动 `listRef.refresh()` 一次)。
|
||||
@@ -53,16 +53,20 @@
|
||||
### 3.2 user-operation 的取数与保存
|
||||
`onLoad` 读 `id`(=`UserName`)与 `mode`;`FindByKey` 取 `SecurityUserData`,取不到提示"记录不存在"。保存按有无 `CreatedBy` 决定 `Update`/`Insert`,成功后用后端回写的档案刷新、回填主键、退回只读。密码编辑的是 `SecurityUserDisplayData.OrgUserPassword`(留空即不改),只在编辑态出现。
|
||||
|
||||
### 3.3 选择页回传协议(useAdminPick / useAdminPickTarget)
|
||||
旧系统的"选角色"是 Operation 用 `<dragModal>` 内嵌 Enquiry + `provide/inject`。移动端改成**真跳一页再回传**:
|
||||
### 3.3 选择器:`admin-pick-popup` 页内底部弹窗
|
||||
旧系统的"选角色/选权限/选功能"是 Operation 用 `<dragModal>` 内嵌 Enquiry + `provide/inject`。移动端第一版改成"真跳一页再回传",但多数选择页 `show-nav-back=false` 且没有取消按钮,用户进去就退不出来。现已改为**页内弹窗**:
|
||||
|
||||
- 调用方 `useAdminPick<T>(channel).open({ page, params }, onPicked)`——先退订再订阅,`onUnmounted` 兜底清理;
|
||||
- 目标页(`actor-enquiry`)`useAdminPickTarget()` 在 `onLoad` 里 `readOptions(options)` 存下 `channel`/`multiple`,确定时 `deliver(rows)`(`uni.$emit('admin:pick:'+channel, rows)` + `navigateBack`);
|
||||
- `actor-enquiry` 因此**同一个列表两种用法**:菜单进入(无 `channel`)是普通查询;被带 `?channel=&multiple=1` 打开就是选择器,单选点行即回传、多选底部"完成"批量回传。
|
||||
- 组件 `components/admin-pick-popup/admin-pick-popup.vue`:`u-popup mode="bottom"`(80vh)+ `scroll-view` 手动分页(**不能用 z-paging**:它默认 `position:fixed` 撑满视口,在弹层内会打架);
|
||||
- Props `visible / title / columns / loader / rowKey / titleKey / multiple / fixedFilters / pageSize`,Emits `update:visible / close / confirm(rows)`;
|
||||
- 数据源在 `composables/usePickerConfigs.ts`:每个被选数据一个工厂函数(`usePermissionPickConfig`、`useDependentPickConfig`、`useActorPickConfig`、`useGroupPickConfig`、`useUserPickConfig`、`useFunctionPickConfig`、`useHelpFeaturePickConfig`),返回 `{ columns, loader, rowKey, titleKey, fixedFilters }`;
|
||||
- 调用方只需 `const xxxConfig = useXxxPickConfig(); const showXxxPicker = ref(false);`,点入口置 `true`,`@confirm` 里做原来的去重合并逻辑。需要动态过滤(如扩展权限按 `dependent`)时写 `xxxConfig.fixedFilters.value = { dependent }`;
|
||||
- 单选点行即 `confirm` + 关闭;多选勾选后点确定;X / 遮罩 / 取消三条退路都通。
|
||||
|
||||
**协议参数一律不做 `encodeURIComponent`**:H5 侧 uni/vue-router 拼 URL 用 `encodeURI`,预编码会把 `%` 再编成 `%25`。`channel` 与 `params` 的值只允许字母/数字/下划线/连字符/点/斜杠。
|
||||
**security 域的 enquiry 页因此回归纯列表页**:`isPick` / `pickTarget` / `onLoad(channel)` / `deliver()` 全部删除,`:show-nav-back="true"` 恒定,`+` 新增入口不再被选择器态隐藏。
|
||||
|
||||
> 命名订正:这个页面就叫 `actor-enquiry`(旧系统本是 `ActorEnquiry.vue`),早期草稿里的 `actor-pick` 是造错的概念——"选择"只是它带 `channel` 打开时的一种模式,不是另一张页面。
|
||||
`useAdminPick` / `useAdminPickTarget` 机制本身**保留**:`family-person-enquiry` 仍作为「族员选择器」被首页 dashboard 以 `?channel=person` 调起。那条老路子的协议参数一律不做 `encodeURIComponent`(H5 侧 uni/vue-router 拼 URL 用 `encodeURI`,预编码会把 `%` 再编成 `%25`)。
|
||||
|
||||
> 命名订正:`actor-enquiry` 就是旧 `ActorEnquiry.vue` 的移植,早期草稿里的 `actor-pick` 是造错的概念——它现在只是普通查询页,选择能力在调用方的弹窗里。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -2,20 +2,16 @@
|
||||
/**
|
||||
* 角色查询(ActorEnquiry):旧 WebVue views/security/ActorEnquiry.vue 的移动端等价物
|
||||
*
|
||||
* 它有两种打开方式,同一套列表:
|
||||
* 1. 从菜单进(无 channel):普通查询页(点行看详情,右上可新增、长按多选可批量删除);
|
||||
* 2. 被 user-operation 当"角色选择器"跳进来(带 ?channel=&multiple=1):
|
||||
* 选完 uni.$emit 回传再 navigateBack(见 useAdminPick / useAdminPickTarget 协议)。
|
||||
*
|
||||
* 旧系统里这个选择能力是 Operation 用 <dragModal> 内嵌 Enquiry + provide/inject 实现的;
|
||||
* 移动端页面之间没有 provide/inject,只能真跳一页再回传,所以这个页面本身就是 Enquiry。
|
||||
* 普通查询页:点行看详情,右上可新增、多选可批量删除。
|
||||
* 旧系统里 user-operation 用 <dragModal> 内嵌本页做"角色选择器";
|
||||
* 移动端的弹窗选择已由 user-operation 内嵌 <admin-pick-popup> 实现,
|
||||
* 本页不再承担选择器职责(useAdminPick / useAdminPickTarget 协议已移除)。
|
||||
*/
|
||||
import type { AdminColumn, AdminQuery, AdminRow } from "@/admin/admin-types";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, nextTick, reactive, ref } from "vue";
|
||||
import { fillCriteria, makeFilterOption } from "@/admin/admin-filter";
|
||||
import { useAdminList, useAdminPickTarget, useAuthGuard } from "@/composables";
|
||||
import { useAdminList, useAuthGuard } from "@/composables";
|
||||
import { SecurityActorEntity } from "@/data/auto/securityActorEntity";
|
||||
import { SecurityActorSearchCriteria } from "@/data/auto/securityActorSearchCriteria";
|
||||
import { SearchFilterOperatorPolicy } from "@/data/custom/searchFilterOperatorPolicy";
|
||||
@@ -25,12 +21,8 @@ import { pageUrl } from "@/router/routes";
|
||||
import securityActorService from "@/service/security/securityActorService";
|
||||
|
||||
const { t } = useLocale();
|
||||
const pickTarget = useAdminPickTarget();
|
||||
const perm = usePagePermission("actorEnquiry");
|
||||
|
||||
/** 选择器模式:带 channel 进来时点行回传;否则作为普通菜单页点行进详情 */
|
||||
const isPick = ref(false);
|
||||
|
||||
const bindOptions = reactive([
|
||||
makeFilterOption(
|
||||
SecurityActorEntity.DB_ACTOR_LOCAL_NAME,
|
||||
@@ -73,28 +65,12 @@ function onSearch(): void {
|
||||
}
|
||||
|
||||
function onRowTap(row: AdminRow): void {
|
||||
// 选择器模式:多选态交给勾选,单选态点一行即选中回传
|
||||
if (isPick.value) {
|
||||
if (pickTarget.multiple.value) {
|
||||
return;
|
||||
}
|
||||
pickTarget.deliverOne(row);
|
||||
return;
|
||||
}
|
||||
// 菜单模式:进角色详情(旧页面双击行开编辑弹窗,移动端改点行进详情页)
|
||||
// 进角色详情(旧页面双击行开编辑弹窗,移动端改点行进详情页)
|
||||
uni.navigateTo({
|
||||
url: `${pageUrl("actorOperation")}?id=${row.ActorName}&mode=view`,
|
||||
});
|
||||
}
|
||||
|
||||
function onConfirm(rows: AdminRow[]): void {
|
||||
if (!rows.length) {
|
||||
uni.showToast({ title: t("AdminPage.pickNoneTip"), icon: "none" });
|
||||
return;
|
||||
}
|
||||
pickTarget.deliver(rows);
|
||||
}
|
||||
|
||||
function onCreate(): void {
|
||||
uni.navigateTo({ url: `${pageUrl("actorOperation")}?mode=create` });
|
||||
}
|
||||
@@ -121,11 +97,6 @@ async function onDelete(rows: AdminRow[]): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
pickTarget.readOptions(options);
|
||||
isPick.value = String(options?.channel ?? "").trim().length > 0;
|
||||
});
|
||||
|
||||
useAuthGuard(() => {
|
||||
// 每次显示都重拉第一页:增/改/删后从详情返回能看到最新数据(navigateBack 不重新挂页面)
|
||||
nextTick(() => listRef.value?.refresh());
|
||||
@@ -135,23 +106,20 @@ useAuthGuard(() => {
|
||||
<template>
|
||||
<view class="page">
|
||||
<admin-list ref="listRef" v-model:keyword="keyword" :nav-title="t('UserPage.actorEnquiryTitle')"
|
||||
:show-nav-back="!isPick" :columns="columns" :loader="state.query" :row-key="SecurityActorEntity.ACTOR_NAME"
|
||||
title-key="ActorLocalName" :selectable="isPick ? pickTarget.multiple.value : Boolean(perm.canDelete.value)"
|
||||
:filter-count="filterCount" :filter-visible="filterVisible" :filter-items="draft" :search-token="searchToken"
|
||||
:keep-selection-on-reload="isPick" @search="onSearch" @open-filter="state.openFilter()"
|
||||
@close-filter="state.closeFilter()" @apply-filter="state.applyFilter($event)"
|
||||
:show-nav-back="true" :columns="columns" :loader="state.query" :row-key="SecurityActorEntity.ACTOR_NAME"
|
||||
title-key="ActorLocalName" :selectable="Boolean(perm.canDelete.value)" :filter-count="filterCount"
|
||||
:filter-visible="filterVisible" :filter-items="draft" :search-token="searchToken" @search="onSearch"
|
||||
@open-filter="state.openFilter()" @close-filter="state.closeFilter()" @apply-filter="state.applyFilter($event)"
|
||||
@clear-filter="state.clearFiltersAndSearch()" @row-tap="onRowTap">
|
||||
<template v-if="!isPick && perm.canCreate.value" #toolbar-actions>
|
||||
<template v-if="perm.canCreate.value" #toolbar-actions>
|
||||
<view class="create-entry" @click="onCreate">
|
||||
<u-icon name="plus" size="40" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template #actions="{ rows }">
|
||||
<u-button v-if="isPick" type="primary" shape="circle" size="mini" :text="t('AdminPage.pickDoneBut')"
|
||||
@click="onConfirm(rows)" />
|
||||
<u-button v-else-if="perm.canDelete.value" type="error" shape="circle" size="mini"
|
||||
:text="t('AdminPage.deleteBut')" @click="onDelete(rows)" />
|
||||
<u-button v-if="perm.canDelete.value" type="error" shape="circle" size="mini" :text="t('AdminPage.deleteBut')"
|
||||
@click="onDelete(rows)" />
|
||||
</template>
|
||||
</admin-list>
|
||||
</view>
|
||||
|
||||
@@ -6,17 +6,19 @@
|
||||
* - onLoad 读 id(=ActorName)与 mode(view|create|amend);
|
||||
* - FindByKey 取 SecurityActorData,取不到提示"记录不存在";
|
||||
* - 保存按 CreatedBy 有无决定 Update / Insert,成功后回填主键、退回只读;
|
||||
* - 两个子表:组别明细(SecurityActorGroups,选 group-enquiry 多选)、
|
||||
* 用户明细(SecurityUserActors,选 user-enquiry 多选),均按主键去重合并、删除即剔除。
|
||||
* - 两个子表:组别明细(SecurityActorGroups,弹窗多选组别)、
|
||||
* 用户明细(SecurityUserActors,弹窗多选用户),均按主键去重合并、删除即剔除。
|
||||
*
|
||||
* 旧页面用 dragModal 内嵌 Enquiry + provide/inject 做选择;移动端改成真跳选择页再回传
|
||||
* (见 composables/useAdminPick),所以两个子表放在 admin-form 的 #extra 插槽里自绘。
|
||||
* 旧页面用 dragModal 内嵌 Enquiry + provide/inject 做选择;移动端用
|
||||
* <admin-pick-popup> 底部弹窗(数据源见 composables/usePickerConfigs),
|
||||
* 两个子表放在 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 { useAuthGuard } from "@/composables";
|
||||
import { useGroupPickConfig, useUserPickConfig } from "@/composables/usePickerConfigs";
|
||||
import { SecurityActorData } from "@/data/auto/securityActorData";
|
||||
import { SecurityActorGroupViewEntity } from "@/data/auto/securityActorGroupViewEntity";
|
||||
import { SecurityUserActorViewEntity } from "@/data/auto/securityUserActorViewEntity";
|
||||
@@ -167,11 +169,15 @@ function onBack(): void {
|
||||
smartBack("mineTab");
|
||||
}
|
||||
|
||||
// 组别选择:跳 group-enquiry(多选),回传后按 GroupName 去重合并
|
||||
const groupPicker = useAdminPick<SecurityActorGroupViewEntity>("group");
|
||||
// 组别选择弹窗
|
||||
const groupConfig = useGroupPickConfig();
|
||||
const showGroupPicker = ref(false);
|
||||
|
||||
function onAddGroup(): void {
|
||||
groupPicker.open({ page: "groupEnquiry", params: { multiple: "1" } }, (rows) => {
|
||||
showGroupPicker.value = true;
|
||||
}
|
||||
|
||||
function onGroupPicked(rows: any[]): void {
|
||||
const exists = new Set(data.value.SecurityActorGroups.map(item => item.GroupName));
|
||||
for (const row of rows) {
|
||||
const groupName = String(row.GroupName ?? "");
|
||||
@@ -185,7 +191,7 @@ function onAddGroup(): void {
|
||||
data.value.SecurityActorGroups.push(entity);
|
||||
exists.add(groupName);
|
||||
}
|
||||
});
|
||||
showGroupPicker.value = false;
|
||||
}
|
||||
|
||||
function onDeleteGroup(groupName: string): void {
|
||||
@@ -194,11 +200,15 @@ function onDeleteGroup(groupName: string): void {
|
||||
);
|
||||
}
|
||||
|
||||
// 用户选择:跳 user-enquiry(多选),回传后按 UserName 去重合并
|
||||
const userPicker = useAdminPick<SecurityUserActorViewEntity>("user");
|
||||
// 用户选择弹窗
|
||||
const userConfig = useUserPickConfig();
|
||||
const showUserPicker = ref(false);
|
||||
|
||||
function onAddUser(): void {
|
||||
userPicker.open({ page: "userEnquiry", params: { multiple: "1" } }, (rows) => {
|
||||
showUserPicker.value = true;
|
||||
}
|
||||
|
||||
function onUserPicked(rows: any[]): void {
|
||||
const exists = new Set(data.value.SecurityUserActors.map(item => item.UserName));
|
||||
for (const row of rows) {
|
||||
const userName = String(row.UserName ?? "");
|
||||
@@ -212,7 +222,7 @@ function onAddUser(): void {
|
||||
data.value.SecurityUserActors.push(entity);
|
||||
exists.add(userName);
|
||||
}
|
||||
});
|
||||
showUserPicker.value = false;
|
||||
}
|
||||
|
||||
function onDeleteUser(userName: string): void {
|
||||
@@ -317,6 +327,16 @@ useAuthGuard(() => {
|
||||
</view>
|
||||
</template>
|
||||
</admin-form>
|
||||
|
||||
<!-- 弹窗选择器 -->
|
||||
<admin-pick-popup v-model:visible="showGroupPicker" :title="t('GroupPage.title')"
|
||||
:columns="groupConfig.columns.value" :loader="groupConfig.loader" :row-key="groupConfig.rowKey"
|
||||
:title-key="groupConfig.titleKey" :multiple="true" :fixed-filters="groupConfig.fixedFilters.value"
|
||||
@confirm="onGroupPicked" @close="showGroupPicker = false" />
|
||||
|
||||
<admin-pick-popup v-model:visible="showUserPicker" :title="t('UserPage.title')" :columns="userConfig.columns.value"
|
||||
:loader="userConfig.loader" :row-key="userConfig.rowKey" :title-key="userConfig.titleKey" :multiple="true"
|
||||
:fixed-filters="userConfig.fixedFilters.value" @confirm="onUserPicked" @close="showUserPicker = false" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -7,16 +7,15 @@
|
||||
* data.SecurityFunctionDisplayDatas(不是普通的 data.Datas!),这里逐字照抄;
|
||||
* - 中文名称筛选用 CN_CONTAIN(旧页面即如此),功能名称用 CONTAIN;
|
||||
* - 菜单? 是 SINGLE_SELECTION + yesNo 候选,列显示 Y->是 / N->否。
|
||||
* 行点击进详情(无 channel)/ 作为选择器回传(带 channel)。
|
||||
* 行点击进详情。function-operation 的"选权限/上级/下级"弹窗已由调用方内嵌
|
||||
* <admin-pick-popup> 实现,本页不再承担选择器职责(useAdminPick 协议已移除)。
|
||||
*/
|
||||
import type { AdminColumn, AdminQuery, AdminRow } from "@/admin/admin-types";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, nextTick, reactive, ref, watch } from "vue";
|
||||
import { fillCriteria, makeFilterOption } from "@/admin/admin-filter";
|
||||
import {
|
||||
useAdminList,
|
||||
useAdminPickTarget,
|
||||
useAuthGuard,
|
||||
} from "@/composables";
|
||||
import { SecurityFunctionDisplayData } from "@/data/auto/securityFunctionDisplayData";
|
||||
@@ -29,9 +28,6 @@ import securityFunctionService from "@/service/security/securityFunctionService"
|
||||
|
||||
const { t, currentLocale } = useLocale();
|
||||
const perm = usePagePermission("functionEnquiry");
|
||||
const pickTarget = useAdminPickTarget();
|
||||
|
||||
const isPick = ref(false);
|
||||
|
||||
function yesNo() {
|
||||
return [
|
||||
@@ -116,13 +112,6 @@ function onSearch(): void {
|
||||
}
|
||||
|
||||
function onRowTap(row: AdminRow): void {
|
||||
if (isPick.value) {
|
||||
if (pickTarget.multiple.value) {
|
||||
return;
|
||||
}
|
||||
pickTarget.deliverOne(row);
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `${pageUrl("functionOperation")}?id=${row.FunctionName}&mode=view`,
|
||||
});
|
||||
@@ -132,14 +121,6 @@ function onCreate(): void {
|
||||
uni.navigateTo({ url: `${pageUrl("functionOperation")}?mode=create` });
|
||||
}
|
||||
|
||||
function onConfirm(rows: AdminRow[]): void {
|
||||
if (!rows.length) {
|
||||
uni.showToast({ title: t("AdminPage.pickNoneTip"), icon: "none" });
|
||||
return;
|
||||
}
|
||||
pickTarget.deliver(rows);
|
||||
}
|
||||
|
||||
async function onDelete(rows: AdminRow[]): Promise<void> {
|
||||
if (!rows.length) {
|
||||
uni.showToast({ title: t("AdminPage.pickNoneTip"), icon: "none" });
|
||||
@@ -176,22 +157,6 @@ watch(currentLocale, () => {
|
||||
}
|
||||
});
|
||||
|
||||
onLoad((options) => {
|
||||
pickTarget.readOptions(options);
|
||||
isPick.value = String(options?.channel ?? "").trim().length > 0;
|
||||
// function-operation 选上级目录时锁 FunctionIndc=N、选下级菜单时锁 Y:
|
||||
// 用 ?indc= 传进来预置到菜单? 筛选,回传后按该类型过滤候选功能。
|
||||
const indc = String(options?.indc ?? "").trim();
|
||||
if (indc) {
|
||||
const option = bindOptions.find(
|
||||
item => item.BindValue === SecurityFunctionDisplayData.DB_FUNCTION_INDC,
|
||||
);
|
||||
if (option) {
|
||||
option.Value = indc;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useAuthGuard(() => {
|
||||
// 每次显示都重拉第一页:增/改/删后从详情返回能看到最新数据(navigateBack 不重新挂页面)
|
||||
nextTick(() => listRef.value?.refresh());
|
||||
@@ -200,24 +165,20 @@ useAuthGuard(() => {
|
||||
|
||||
<template>
|
||||
<view class="page">
|
||||
<admin-list ref="listRef" v-model:keyword="keyword" :nav-title="t('FunctionPage.title')"
|
||||
:show-nav-back="!isPick" :columns="columns" :loader="state.query"
|
||||
:row-key="SecurityFunctionDisplayData.FUNCTION_NAME" title-key="FunctionName"
|
||||
:selectable="isPick ? pickTarget.multiple.value : Boolean(perm.canDelete.value)" :filter-count="filterCount"
|
||||
:filter-visible="filterVisible" :filter-items="draft" :search-token="searchToken"
|
||||
:keep-selection-on-reload="isPick" @search="onSearch" @open-filter="state.openFilter()"
|
||||
@close-filter="state.closeFilter()" @apply-filter="state.applyFilter($event)"
|
||||
@clear-filter="state.clearFiltersAndSearch()" @row-tap="onRowTap">
|
||||
<template v-if="!isPick && perm.canCreate.value" #toolbar-actions>
|
||||
<admin-list ref="listRef" v-model:keyword="keyword" :nav-title="t('FunctionPage.title')" :show-nav-back="true"
|
||||
:columns="columns" :loader="state.query" :row-key="SecurityFunctionDisplayData.FUNCTION_NAME"
|
||||
title-key="FunctionName" :selectable="Boolean(perm.canDelete.value)" :filter-count="filterCount"
|
||||
:filter-visible="filterVisible" :filter-items="draft" :search-token="searchToken" @search="onSearch"
|
||||
@open-filter="state.openFilter()" @close-filter="state.closeFilter()"
|
||||
@apply-filter="state.applyFilter($event)" @clear-filter="state.clearFiltersAndSearch()" @row-tap="onRowTap">
|
||||
<template v-if="perm.canCreate.value" #toolbar-actions>
|
||||
<view class="create-entry" @click="onCreate">
|
||||
<u-icon name="plus" size="40" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template #actions="{ rows }">
|
||||
<u-button v-if="isPick" type="primary" shape="circle" size="mini" :text="t('AdminPage.pickDoneBut')"
|
||||
@click="onConfirm(rows)" />
|
||||
<u-button v-else-if="perm.canDelete.value" type="error" shape="circle" size="mini"
|
||||
<u-button v-if="perm.canDelete.value" type="error" shape="circle" size="mini"
|
||||
:text="t('AdminPage.deleteBut')" @click="onDelete(rows)" />
|
||||
</template>
|
||||
</admin-list>
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
*
|
||||
* 对齐旧逻辑:
|
||||
* - 主表 SecurityFunction(DisplayData 形状),字段含 菜单?(FunctionIndc Y/N)、权限名称(选择器)等;
|
||||
* - 上级目录 SecurityFunctionMapParents:选 function-enquiry(单选、锁 FunctionIndc=N,
|
||||
* - 上级目录 SecurityFunctionMapParents:弹窗选功能(单选、锁 FunctionIndc=N,
|
||||
* 即只能挂在"目录"型功能下),最多一条(旧页面父为空时才显示新增);
|
||||
* - 下级菜单 SecurityFunctionMapSons:选 function-enquiry(多选、锁 FunctionIndc=Y),
|
||||
* - 下级菜单 SecurityFunctionMapSons:弹窗选功能(多选、锁 FunctionIndc=Y),
|
||||
* 仅当本页 FunctionIndc=N(目录)才能加;可上下移动重排,重排后 Orderby=序号+1;
|
||||
* - 菜单? 从 N 改成非 N 时清空下级菜单(旧 onFunctionIndcChange);
|
||||
* - 保存按 CreatedBy 有无决定 Update / Insert(SecurityFunctionData)。
|
||||
@@ -16,7 +16,8 @@ import type { AdminFormAction, AdminFormField, AdminFormSection } from "@/admin/
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, ref } from "vue";
|
||||
import { useAdminPick, useAuthGuard } from "@/composables";
|
||||
import { useAuthGuard } from "@/composables";
|
||||
import { useFunctionPickConfig, usePermissionPickConfig } from "@/composables/usePickerConfigs";
|
||||
import { SecurityFunctionData } from "@/data/auto/securityFunctionData";
|
||||
import { SecurityFunctionMapViewEntity } from "@/data/auto/securityFunctionMapViewEntity";
|
||||
import { usePagePermission } from "@/permission/permission-context";
|
||||
@@ -180,37 +181,49 @@ function onBack(): void {
|
||||
smartBack("mineTab");
|
||||
}
|
||||
|
||||
// 权限名称:单选 permission-enquiry,回传写进 FunctionName 之外不影响
|
||||
const permissionPicker = useAdminPick("permission");
|
||||
// 权限名称:单选弹窗
|
||||
const permissionConfig = usePermissionPickConfig();
|
||||
const showPermissionPicker = ref(false);
|
||||
|
||||
function pickPermission(): void {
|
||||
if (readonly.value || !perm.canAmend.value) {
|
||||
return;
|
||||
}
|
||||
permissionPicker.open({ page: "permissionEnquiry" }, (rows) => {
|
||||
showPermissionPicker.value = true;
|
||||
}
|
||||
|
||||
function onPermissionPicked(rows: any[]): void {
|
||||
if (rows.length) {
|
||||
func.value.PermissionName = String(rows[0].PermissionName ?? "");
|
||||
}
|
||||
});
|
||||
showPermissionPicker.value = false;
|
||||
}
|
||||
|
||||
// 上级目录:单选 function-enquiry(锁 FunctionIndc=N),仅当父为空时可加
|
||||
const parentPicker = useAdminPick("parent");
|
||||
// 上级目录:单选弹窗(锁 FunctionIndc=N)
|
||||
const parentConfig = useFunctionPickConfig();
|
||||
const showParentPicker = ref(false);
|
||||
|
||||
function addParent(): void {
|
||||
if (data.value.SecurityFunctionMapParents.length > 0) {
|
||||
return;
|
||||
}
|
||||
parentPicker.open({ page: "functionEnquiry", params: { indc: "N" } }, (rows) => {
|
||||
parentConfig.fixedFilters.value = { indc: "N" };
|
||||
showParentPicker.value = true;
|
||||
}
|
||||
|
||||
function onParentPicked(rows: any[]): void {
|
||||
ensureLists();
|
||||
if (!rows.length) {
|
||||
return;
|
||||
}
|
||||
if (rows.length) {
|
||||
const parentName = String(rows[0].FunctionName ?? "");
|
||||
// 防御:不能把自己设为自己的上级目录(旧跳页版也没挡,弹窗化顺手补上)
|
||||
if (parentName && parentName !== func.value.FunctionName) {
|
||||
const parent = new SecurityFunctionMapViewEntity();
|
||||
parent.ParentFunctionName = String(rows[0].FunctionName ?? "");
|
||||
parent.ParentFunctionName = parentName;
|
||||
parent.ParentFunctionLocalName = String(rows[0].FunctionLocalName ?? "");
|
||||
data.value.SecurityFunctionMapParents.push(parent);
|
||||
});
|
||||
}
|
||||
}
|
||||
showParentPicker.value = false;
|
||||
}
|
||||
|
||||
function deleteParent(parentFunctionName: string): void {
|
||||
@@ -219,16 +232,22 @@ function deleteParent(parentFunctionName: string): void {
|
||||
);
|
||||
}
|
||||
|
||||
// 下级菜单:多选 function-enquiry(锁 FunctionIndc=Y),仅目录(FunctionIndc=N)可加
|
||||
const sonPicker = useAdminPick("son");
|
||||
// 下级菜单:多选弹窗(锁 FunctionIndc=Y),仅目录(FunctionIndc=N)可加
|
||||
const sonConfig = useFunctionPickConfig();
|
||||
const showSonPicker = ref(false);
|
||||
|
||||
function addSon(): void {
|
||||
sonPicker.open({ page: "functionEnquiry", params: { multiple: "1", indc: "Y" } }, (rows) => {
|
||||
sonConfig.fixedFilters.value = { indc: "Y" };
|
||||
showSonPicker.value = true;
|
||||
}
|
||||
|
||||
function onSonPicked(rows: any[]): void {
|
||||
ensureLists();
|
||||
const exists = new Set(data.value.SecurityFunctionMapSons.map(item => item.SonFunctionName));
|
||||
for (const row of rows) {
|
||||
const sonName = String(row.FunctionName ?? "");
|
||||
if (!sonName || exists.has(sonName)) {
|
||||
// 防御:跳过空、自身(不能把自己挂成自己的下级菜单)、以及已在列表里的
|
||||
if (!sonName || sonName === func.value.FunctionName || exists.has(sonName)) {
|
||||
continue;
|
||||
}
|
||||
const son = new SecurityFunctionMapViewEntity();
|
||||
@@ -238,7 +257,7 @@ function addSon(): void {
|
||||
data.value.SecurityFunctionMapSons.push(son);
|
||||
exists.add(sonName);
|
||||
}
|
||||
});
|
||||
showSonPicker.value = false;
|
||||
}
|
||||
|
||||
function deleteSon(sonFunctionName: string): void {
|
||||
@@ -393,6 +412,22 @@ useAuthGuard(() => {
|
||||
</view>
|
||||
</template>
|
||||
</admin-form>
|
||||
|
||||
<!-- 弹窗选择器 -->
|
||||
<admin-pick-popup v-model:visible="showPermissionPicker" :title="t('PermissionPage.title')"
|
||||
:columns="permissionConfig.columns.value" :loader="permissionConfig.loader" :row-key="permissionConfig.rowKey"
|
||||
:title-key="permissionConfig.titleKey" :multiple="false" :fixed-filters="permissionConfig.fixedFilters.value"
|
||||
@confirm="onPermissionPicked" @close="showPermissionPicker = false" />
|
||||
|
||||
<admin-pick-popup v-model:visible="showParentPicker" :title="t('FunctionPage.parentTitle')"
|
||||
:columns="parentConfig.columns.value" :loader="parentConfig.loader" :row-key="parentConfig.rowKey"
|
||||
:title-key="parentConfig.titleKey" :multiple="false" :fixed-filters="parentConfig.fixedFilters.value"
|
||||
@confirm="onParentPicked" @close="showParentPicker = false" />
|
||||
|
||||
<admin-pick-popup v-model:visible="showSonPicker" :title="t('FunctionPage.sonTitle')"
|
||||
:columns="sonConfig.columns.value" :loader="sonConfig.loader" :row-key="sonConfig.rowKey"
|
||||
:title-key="sonConfig.titleKey" :multiple="true" :fixed-filters="sonConfig.fixedFilters.value"
|
||||
@confirm="onSonPicked" @close="showSonPicker = false" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -3,22 +3,20 @@
|
||||
* 用户组查询(列表页):旧 WebVue views/security/GroupEnquiry.vue 的移动端等价物
|
||||
*
|
||||
* 保留:三条筛选(组别名称/中文名称 文本包含、创建日期 区间)、每页 20 条、
|
||||
* 行点击进详情、多选批量删除(旧页面也是循环 Delete(GroupName))、多选选择器模式。
|
||||
* 行点击进详情、多选批量删除(旧页面也是循环 Delete(GroupName))。
|
||||
* 丢掉:多列网格、列配置、打印、桌面双击弹窗。
|
||||
*
|
||||
* 两种打开方式,同一套列表:
|
||||
* 1. 从菜单/枢纽页进(无 channel):普通查询页,点行 -> group-operation 详情;
|
||||
* 2. 被 actor-operation / group-operation 当"组别选择器"跳进来(带 ?channel=&multiple=1):
|
||||
* 选完 uni.$emit 回传再 navigateBack(见 useAdminPick / useAdminPickTarget 协议)。
|
||||
* 普通查询页:点行 -> group-operation 详情。
|
||||
* 旧系统里 actor-operation / group-operation 用 <dragModal> 内嵌本页做"组别选择器";
|
||||
* 移动端的弹窗选择已由调用方内嵌 <admin-pick-popup> 实现,
|
||||
* 本页不再承担选择器职责(useAdminPick / useAdminPickTarget 协议已移除)。
|
||||
*/
|
||||
import type { AdminColumn, AdminQuery, AdminRow } from "@/admin/admin-types";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, nextTick, reactive, ref, watch } from "vue";
|
||||
import { fillCriteria, makeFilterOption } from "@/admin/admin-filter";
|
||||
import {
|
||||
useAdminList,
|
||||
useAdminPickTarget,
|
||||
useAuthGuard,
|
||||
} from "@/composables";
|
||||
import { SecurityGroupEntity } from "@/data/auto/securityGroupEntity";
|
||||
@@ -31,10 +29,6 @@ import securityGroupService from "@/service/security/securityGroupService";
|
||||
|
||||
const { t, currentLocale } = useLocale();
|
||||
const perm = usePagePermission("groupEnquiry");
|
||||
const pickTarget = useAdminPickTarget();
|
||||
|
||||
/** 选择器模式:带 channel 进来时点行回传而非跳详情 */
|
||||
const isPick = ref(false);
|
||||
|
||||
const LABEL_KEYS: Record<string, string> = {
|
||||
[SecurityGroupEntity.DB_GROUP_NAME]: "GroupPage.groupName",
|
||||
@@ -109,13 +103,6 @@ function onSearch(): void {
|
||||
}
|
||||
|
||||
function onRowTap(row: AdminRow): void {
|
||||
if (isPick.value) {
|
||||
if (pickTarget.multiple.value) {
|
||||
return;
|
||||
}
|
||||
pickTarget.deliverOne(row);
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `${pageUrl("groupOperation")}?id=${row.GroupName}&mode=view`,
|
||||
});
|
||||
@@ -125,14 +112,6 @@ function onCreate(): void {
|
||||
uni.navigateTo({ url: `${pageUrl("groupOperation")}?mode=create` });
|
||||
}
|
||||
|
||||
function onConfirm(rows: AdminRow[]): void {
|
||||
if (!rows.length) {
|
||||
uni.showToast({ title: t("AdminPage.pickNoneTip"), icon: "none" });
|
||||
return;
|
||||
}
|
||||
pickTarget.deliver(rows);
|
||||
}
|
||||
|
||||
async function onDelete(rows: AdminRow[]): Promise<void> {
|
||||
if (!rows.length) {
|
||||
uni.showToast({ title: t("AdminPage.pickNoneTip"), icon: "none" });
|
||||
@@ -163,11 +142,6 @@ watch(currentLocale, () => {
|
||||
}
|
||||
});
|
||||
|
||||
onLoad((options) => {
|
||||
pickTarget.readOptions(options);
|
||||
isPick.value = String(options?.channel ?? "").trim().length > 0;
|
||||
});
|
||||
|
||||
useAuthGuard(() => {
|
||||
// 每次显示都重拉第一页:增/改/删后从详情返回能看到最新数据(navigateBack 不重新挂页面)
|
||||
nextTick(() => listRef.value?.refresh());
|
||||
@@ -176,23 +150,20 @@ useAuthGuard(() => {
|
||||
|
||||
<template>
|
||||
<view class="page">
|
||||
<admin-list ref="listRef" v-model:keyword="keyword" :nav-title="t('GroupPage.title')" :show-nav-back="!isPick"
|
||||
<admin-list ref="listRef" v-model:keyword="keyword" :nav-title="t('GroupPage.title')" :show-nav-back="true"
|
||||
:columns="columns" :loader="state.query" :row-key="SecurityGroupEntity.GROUP_NAME" title-key="GroupName"
|
||||
:selectable="isPick ? pickTarget.multiple.value : Boolean(perm.canDelete.value)" :filter-count="filterCount"
|
||||
:filter-visible="filterVisible" :filter-items="draft" :search-token="searchToken"
|
||||
:keep-selection-on-reload="isPick" @search="onSearch" @open-filter="state.openFilter()"
|
||||
:selectable="Boolean(perm.canDelete.value)" :filter-count="filterCount" :filter-visible="filterVisible"
|
||||
:filter-items="draft" :search-token="searchToken" @search="onSearch" @open-filter="state.openFilter()"
|
||||
@close-filter="state.closeFilter()" @apply-filter="state.applyFilter($event)"
|
||||
@clear-filter="state.clearFiltersAndSearch()" @row-tap="onRowTap">
|
||||
<template v-if="!isPick && perm.canCreate.value" #toolbar-actions>
|
||||
<template v-if="perm.canCreate.value" #toolbar-actions>
|
||||
<view class="create-entry" @click="onCreate">
|
||||
<u-icon name="plus" size="40" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template #actions="{ rows }">
|
||||
<u-button v-if="isPick" type="primary" shape="circle" size="mini" :text="t('AdminPage.pickDoneBut')"
|
||||
@click="onConfirm(rows)" />
|
||||
<u-button v-else-if="perm.canDelete.value" type="error" shape="circle" size="mini"
|
||||
<u-button v-if="perm.canDelete.value" type="error" shape="circle" size="mini"
|
||||
:text="t('AdminPage.deleteBut')" @click="onDelete(rows)" />
|
||||
</template>
|
||||
</admin-list>
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
/**
|
||||
* 组别详情(查看 / 新增 / 修改):旧 WebVue views/security/GroupOperation.vue 的移动端等价物
|
||||
*
|
||||
* 三个子表(旧页用 dragModal 内嵌 Enquiry + provide/inject,移动端改成真跳选择页再回传):
|
||||
* 1. 权限明细 SecurityGroupPermissionViews —— 选 permission-enquiry(多选),
|
||||
* 三个子表(旧页用 dragModal 内嵌 Enquiry + provide/inject,移动端改用 admin-pick-popup 底部弹窗):
|
||||
* 1. 权限明细 SecurityGroupPermissionViews —— 弹窗选 permission(多选),
|
||||
* 每行带 查看/新增/修改/删除 四个勾选(对齐旧 GroupPermissionActions 的四个 checkbox);
|
||||
* 删除权限行会连同其扩展权限明细一起剔除(旧 deletePermission 同款)。
|
||||
* 2. 扩展权限明细 SecurityGroupPermissionDependentViews —— 由权限行的"扩展名称"点开
|
||||
* permission-dependent-enquiry(多选、按 dependent 过滤)补入;
|
||||
* 3. 组别内角色群 SecurityActorGroups —— 选 actor-enquiry(多选)。
|
||||
* 扩展权限弹窗(多选、按 dependent 过滤)补入;
|
||||
* 3. 组别内角色群 SecurityActorGroups —— 弹窗选角色(多选)。
|
||||
* 新增权限/扩展权限行时四个勾选全部置 false(旧页面 ent.IsAmend=...=false)。
|
||||
* 保存按 CreatedBy 有无决定 Update / Insert(SecurityGroupData)。
|
||||
*/
|
||||
@@ -16,7 +16,8 @@ import type { AdminFormAction, AdminFormField, AdminFormSection } from "@/admin/
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, ref } from "vue";
|
||||
import { useAdminPick, useAuthGuard } from "@/composables";
|
||||
import { useAuthGuard } from "@/composables";
|
||||
import { useActorPickConfig, useDependentPickConfig, usePermissionPickConfig } from "@/composables/usePickerConfigs";
|
||||
import { SecurityActorGroupViewEntity } from "@/data/auto/securityActorGroupViewEntity";
|
||||
import { SecurityGroupData } from "@/data/auto/securityGroupData";
|
||||
import { SecurityGroupPermissionDependentViewDisplayData } from "@/data/auto/securityGroupPermissionDependentViewDisplayData";
|
||||
@@ -197,11 +198,15 @@ function actionLabel(key: string): string {
|
||||
return map[key] ?? key;
|
||||
}
|
||||
|
||||
// 权限选择:跳 permission-enquiry(多选),按 PermissionName 去重
|
||||
const permissionPicker = useAdminPick("permission");
|
||||
// 权限选择弹窗
|
||||
const permissionConfig = usePermissionPickConfig();
|
||||
const showPermissionPicker = ref(false);
|
||||
|
||||
function addPermission(): void {
|
||||
permissionPicker.open({ page: "permissionEnquiry", params: { multiple: "1" } }, (rows) => {
|
||||
showPermissionPicker.value = true;
|
||||
}
|
||||
|
||||
function onPermissionPicked(rows: any[]): void {
|
||||
ensureLists();
|
||||
const exists = new Set(data.value.SecurityGroupPermissionViews.map(item => item.PermissionName));
|
||||
for (const row of rows) {
|
||||
@@ -218,7 +223,7 @@ function addPermission(): void {
|
||||
data.value.SecurityGroupPermissionViews.push(ent);
|
||||
exists.add(permissionName);
|
||||
}
|
||||
});
|
||||
showPermissionPicker.value = false;
|
||||
}
|
||||
|
||||
function deletePermission(permissionName: string): void {
|
||||
@@ -231,16 +236,19 @@ function deletePermission(permissionName: string): void {
|
||||
);
|
||||
}
|
||||
|
||||
// 扩展权限选择:跳 permission-dependent-enquiry(多选、按当前权限行的 dependent 过滤)
|
||||
const dependentPicker = useAdminPick("dependent");
|
||||
// 扩展权限选择弹窗
|
||||
const dependentConfig = useDependentPickConfig();
|
||||
const showDependentPicker = ref(false);
|
||||
|
||||
function addPermissionDependent(dependent: string): void {
|
||||
if (readonly.value || !dependent) {
|
||||
return;
|
||||
}
|
||||
dependentPicker.open(
|
||||
{ page: "permissionDependentEnquiry", params: { multiple: "1", dependent } },
|
||||
(rows) => {
|
||||
dependentConfig.fixedFilters.value = { dependent };
|
||||
showDependentPicker.value = true;
|
||||
}
|
||||
|
||||
function onDependentPicked(rows: any[]): void {
|
||||
ensureLists();
|
||||
const exists = new Set(
|
||||
data.value.SecurityGroupPermissionDependentViews.map(item => item.DependentDetailName),
|
||||
@@ -260,8 +268,7 @@ function addPermissionDependent(dependent: string): void {
|
||||
data.value.SecurityGroupPermissionDependentViews.push(ent);
|
||||
exists.add(detailName);
|
||||
}
|
||||
},
|
||||
);
|
||||
showDependentPicker.value = false;
|
||||
}
|
||||
|
||||
function deletePermissionDependent(detailName: string): void {
|
||||
@@ -270,11 +277,15 @@ function deletePermissionDependent(detailName: string): void {
|
||||
);
|
||||
}
|
||||
|
||||
// 角色选择:跳 actor-enquiry(多选),按 ActorName 去重
|
||||
const actorPicker = useAdminPick("actor");
|
||||
// 角色选择弹窗
|
||||
const actorConfig = useActorPickConfig();
|
||||
const showActorPicker = ref(false);
|
||||
|
||||
function addActor(): void {
|
||||
actorPicker.open({ page: "actorEnquiry", params: { multiple: "1" } }, (rows) => {
|
||||
showActorPicker.value = true;
|
||||
}
|
||||
|
||||
function onActorPicked(rows: any[]): void {
|
||||
ensureLists();
|
||||
const exists = new Set(data.value.SecurityActorGroups.map(item => item.ActorName));
|
||||
for (const row of rows) {
|
||||
@@ -289,7 +300,7 @@ function addActor(): void {
|
||||
data.value.SecurityActorGroups.push(ent);
|
||||
exists.add(actorName);
|
||||
}
|
||||
});
|
||||
showActorPicker.value = false;
|
||||
}
|
||||
|
||||
function deleteActor(actorName: string): void {
|
||||
@@ -460,6 +471,22 @@ useAuthGuard(() => {
|
||||
</view>
|
||||
</template>
|
||||
</admin-form>
|
||||
|
||||
<!-- 弹窗选择器 -->
|
||||
<admin-pick-popup v-model:visible="showPermissionPicker" :title="t('PermissionPage.title')"
|
||||
:columns="permissionConfig.columns.value" :loader="permissionConfig.loader" :row-key="permissionConfig.rowKey"
|
||||
:title-key="permissionConfig.titleKey" :multiple="true" :fixed-filters="permissionConfig.fixedFilters.value"
|
||||
@confirm="onPermissionPicked" @close="showPermissionPicker = false" />
|
||||
|
||||
<admin-pick-popup v-model:visible="showDependentPicker" :title="t('PermissionPage.dependentTitle')"
|
||||
:columns="dependentConfig.columns.value" :loader="dependentConfig.loader" :row-key="dependentConfig.rowKey"
|
||||
:title-key="dependentConfig.titleKey" :multiple="true" :fixed-filters="dependentConfig.fixedFilters.value"
|
||||
@confirm="onDependentPicked" @close="showDependentPicker = false" />
|
||||
|
||||
<admin-pick-popup v-model:visible="showActorPicker" :title="t('UserPage.actorTitle')"
|
||||
:columns="actorConfig.columns.value" :loader="actorConfig.loader" :row-key="actorConfig.rowKey"
|
||||
:title-key="actorConfig.titleKey" :multiple="true" :fixed-filters="actorConfig.fixedFilters.value"
|
||||
@confirm="onActorPicked" @close="showActorPicker = false" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -7,17 +7,16 @@
|
||||
* - "所属组"列存的是组主键(HelpFeatureGroupId),靠 FindAll 取回的组表翻译成组编码显示;
|
||||
* - 点一条功能点进它【所属组】的维护页(旧 gotoGroup 语义:id=组编码);
|
||||
* - 三条筛选:功能点名称 / 作用说明 / 图标 文本包含。
|
||||
* help-function-operation 会把它当"功能点组选择器"跳进来(单选),回传后按组主键翻译成组编码。
|
||||
* help-function-operation 的"选功能点组"弹窗已由调用方内嵌 <admin-pick-popup> 实现,
|
||||
* 本页不再承担选择器职责(useAdminPick / useAdminPickTarget 协议已移除)。
|
||||
*/
|
||||
import type { AdminColumn, AdminQuery, AdminRow } from "@/admin/admin-types";
|
||||
import type { SecurityHelpFeatureGroupEntity } from "@/data/auto/securityHelpFeatureGroupEntity";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, nextTick, reactive, ref, watch } from "vue";
|
||||
import { fillCriteria, makeFilterOption } from "@/admin/admin-filter";
|
||||
import {
|
||||
useAdminList,
|
||||
useAdminPickTarget,
|
||||
useAuthGuard,
|
||||
} from "@/composables";
|
||||
import { SecurityHelpFeatureEntity } from "@/data/auto/securityHelpFeatureEntity";
|
||||
@@ -30,9 +29,6 @@ import securityHelpFunctionService from "@/service/security/securityHelpFunction
|
||||
|
||||
const { t, currentLocale } = useLocale();
|
||||
const perm = usePagePermission("helpFeatureEnquiry");
|
||||
const pickTarget = useAdminPickTarget();
|
||||
|
||||
const isPick = ref(false);
|
||||
|
||||
const LABEL_KEYS: Record<string, string> = {
|
||||
[SecurityHelpFeatureEntity.DB_NAME]: "HelpFeaturePage.name",
|
||||
@@ -132,13 +128,6 @@ function onSearch(): void {
|
||||
}
|
||||
|
||||
function onRowTap(row: AdminRow): void {
|
||||
if (isPick.value) {
|
||||
if (pickTarget.multiple.value) {
|
||||
return;
|
||||
}
|
||||
pickTarget.deliverOne(row);
|
||||
return;
|
||||
}
|
||||
const groupCode = groupCodeOf(row.HelpFeatureGroupId);
|
||||
if (!groupCode) {
|
||||
uni.showToast({ title: t("HelpFeaturePage.noGroupTip"), icon: "none" });
|
||||
@@ -153,14 +142,6 @@ function onCreate(): void {
|
||||
uni.navigateTo({ url: `${pageUrl("helpFeatureOperation")}?mode=create` });
|
||||
}
|
||||
|
||||
function onConfirm(rows: AdminRow[]): void {
|
||||
if (!rows.length) {
|
||||
uni.showToast({ title: t("AdminPage.pickNoneTip"), icon: "none" });
|
||||
return;
|
||||
}
|
||||
pickTarget.deliver(rows);
|
||||
}
|
||||
|
||||
watch(currentLocale, () => {
|
||||
for (const option of bindOptions) {
|
||||
const key = LABEL_KEYS[option.BindValue];
|
||||
@@ -170,11 +151,6 @@ watch(currentLocale, () => {
|
||||
}
|
||||
});
|
||||
|
||||
onLoad((options) => {
|
||||
pickTarget.readOptions(options);
|
||||
isPick.value = String(options?.channel ?? "").trim().length > 0;
|
||||
});
|
||||
|
||||
useAuthGuard(() => {
|
||||
// 每次显示都重拉组表与第一页:增/改/删后从详情返回能看到最新数据(navigateBack 不重新挂页面)
|
||||
loadGroups();
|
||||
@@ -185,23 +161,16 @@ useAuthGuard(() => {
|
||||
<template>
|
||||
<view class="page">
|
||||
<admin-list ref="listRef" v-model:keyword="keyword" :nav-title="t('HelpFeaturePage.title')"
|
||||
:show-nav-back="!isPick" :columns="columns" :loader="state.query"
|
||||
:row-key="SecurityHelpFeatureEntity.HELP_FEATURE_ID" title-key="Name"
|
||||
:selectable="isPick && pickTarget.multiple.value" :filter-count="filterCount"
|
||||
:filter-visible="filterVisible" :filter-items="draft" :search-token="searchToken"
|
||||
:keep-selection-on-reload="isPick" @search="onSearch" @open-filter="state.openFilter()"
|
||||
@close-filter="state.closeFilter()" @apply-filter="state.applyFilter($event)"
|
||||
@clear-filter="state.clearFiltersAndSearch()" @row-tap="onRowTap">
|
||||
<template v-if="!isPick && perm.canCreate.value" #toolbar-actions>
|
||||
:show-nav-back="true" :columns="columns" :loader="state.query"
|
||||
:row-key="SecurityHelpFeatureEntity.HELP_FEATURE_ID" title-key="Name" :filter-count="filterCount"
|
||||
:filter-visible="filterVisible" :filter-items="draft" :search-token="searchToken" @search="onSearch"
|
||||
@open-filter="state.openFilter()" @close-filter="state.closeFilter()"
|
||||
@apply-filter="state.applyFilter($event)" @clear-filter="state.clearFiltersAndSearch()" @row-tap="onRowTap">
|
||||
<template v-if="perm.canCreate.value" #toolbar-actions>
|
||||
<view class="create-entry" @click="onCreate">
|
||||
<u-icon name="plus" size="40" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template #actions="{ rows }">
|
||||
<u-button v-if="isPick" type="primary" shape="circle" size="mini" :text="t('AdminPage.pickDoneBut')"
|
||||
@click="onConfirm(rows)" />
|
||||
</template>
|
||||
</admin-list>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
* - FindBySearchCriteria 的返回落在 data.Datas(SecurityHelpFunctionEntity[]);
|
||||
* - 两条筛选:功能名称 / 功能说明 文本包含;
|
||||
* - 作废走 DeleteByFunctionName(不是 Delete!旧页面即如此),行点击进详情。
|
||||
* 本页从不作为选择器被跳入,选择器的弹窗能力已由调用方内嵌 <admin-pick-popup> 实现,
|
||||
* 遗留的 useAdminPickTarget 模式代码已移除。
|
||||
*/
|
||||
import type { AdminColumn, AdminQuery, AdminRow } from "@/admin/admin-types";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, nextTick, reactive, ref, watch } from "vue";
|
||||
import { fillCriteria, makeFilterOption } from "@/admin/admin-filter";
|
||||
import {
|
||||
useAdminList,
|
||||
useAdminPickTarget,
|
||||
useAuthGuard,
|
||||
} from "@/composables";
|
||||
import { SecurityHelpFunctionEntity } from "@/data/auto/securityHelpFunctionEntity";
|
||||
@@ -27,9 +27,6 @@ import securityHelpFunctionService from "@/service/security/securityHelpFunction
|
||||
|
||||
const { t, currentLocale } = useLocale();
|
||||
const perm = usePagePermission("helpFunctionEnquiry");
|
||||
const pickTarget = useAdminPickTarget();
|
||||
|
||||
const isPick = ref(false);
|
||||
|
||||
const LABEL_KEYS: Record<string, string> = {
|
||||
[SecurityHelpFunctionEntity.DB_FUNCTION_NAME]: "HelpFunctionPage.functionName",
|
||||
@@ -97,13 +94,6 @@ function onSearch(): void {
|
||||
}
|
||||
|
||||
function onRowTap(row: AdminRow): void {
|
||||
if (isPick.value) {
|
||||
if (pickTarget.multiple.value) {
|
||||
return;
|
||||
}
|
||||
pickTarget.deliverOne(row);
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `${pageUrl("helpFunctionOperation")}?id=${row.FunctionName}&mode=view`,
|
||||
});
|
||||
@@ -113,14 +103,6 @@ function onCreate(): void {
|
||||
uni.navigateTo({ url: `${pageUrl("helpFunctionOperation")}?mode=create` });
|
||||
}
|
||||
|
||||
function onConfirm(rows: AdminRow[]): void {
|
||||
if (!rows.length) {
|
||||
uni.showToast({ title: t("AdminPage.pickNoneTip"), icon: "none" });
|
||||
return;
|
||||
}
|
||||
pickTarget.deliver(rows);
|
||||
}
|
||||
|
||||
async function onDelete(rows: AdminRow[]): Promise<void> {
|
||||
if (!rows.length) {
|
||||
uni.showToast({ title: t("AdminPage.pickNoneTip"), icon: "none" });
|
||||
@@ -151,11 +133,6 @@ watch(currentLocale, () => {
|
||||
}
|
||||
});
|
||||
|
||||
onLoad((options) => {
|
||||
pickTarget.readOptions(options);
|
||||
isPick.value = String(options?.channel ?? "").trim().length > 0;
|
||||
});
|
||||
|
||||
useAuthGuard(() => {
|
||||
// 每次显示都重拉第一页:增/改/删后从详情返回能看到最新数据(navigateBack 不重新挂页面)
|
||||
nextTick(() => listRef.value?.refresh());
|
||||
@@ -165,23 +142,20 @@ useAuthGuard(() => {
|
||||
<template>
|
||||
<view class="page">
|
||||
<admin-list ref="listRef" v-model:keyword="keyword" :nav-title="t('HelpFunctionPage.title')"
|
||||
:show-nav-back="!isPick" :columns="columns" :loader="state.query"
|
||||
:show-nav-back="true" :columns="columns" :loader="state.query"
|
||||
:row-key="SecurityHelpFunctionEntity.FUNCTION_NAME" title-key="FunctionName"
|
||||
:selectable="isPick ? pickTarget.multiple.value : Boolean(perm.canDelete.value)" :filter-count="filterCount"
|
||||
:filter-visible="filterVisible" :filter-items="draft" :search-token="searchToken"
|
||||
:keep-selection-on-reload="isPick" @search="onSearch" @open-filter="state.openFilter()"
|
||||
:selectable="Boolean(perm.canDelete.value)" :filter-count="filterCount" :filter-visible="filterVisible"
|
||||
:filter-items="draft" :search-token="searchToken" @search="onSearch" @open-filter="state.openFilter()"
|
||||
@close-filter="state.closeFilter()" @apply-filter="state.applyFilter($event)"
|
||||
@clear-filter="state.clearFiltersAndSearch()" @row-tap="onRowTap">
|
||||
<template v-if="!isPick && perm.canCreate.value" #toolbar-actions>
|
||||
<template v-if="perm.canCreate.value" #toolbar-actions>
|
||||
<view class="create-entry" @click="onCreate">
|
||||
<u-icon name="plus" size="40" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template #actions="{ rows }">
|
||||
<u-button v-if="isPick" type="primary" shape="circle" size="mini" :text="t('AdminPage.pickDoneBut')"
|
||||
@click="onConfirm(rows)" />
|
||||
<u-button v-else-if="perm.canDelete.value" type="error" shape="circle" size="mini"
|
||||
<u-button v-if="perm.canDelete.value" type="error" shape="circle" size="mini"
|
||||
:text="t('AdminPage.deleteBut')" @click="onDelete(rows)" />
|
||||
</template>
|
||||
</admin-list>
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
*
|
||||
* 对齐旧逻辑:
|
||||
* - onLoad 读 id(=FunctionName)与 mode;FindByFunctionName 取 SecurityHelpFunctionData;
|
||||
* - 主字段:功能名称(给哪个功能写帮助,新建时靠 function-enquiry 单选回填、之后锁死)、
|
||||
* - 主字段:功能名称(给哪个功能写帮助,新建时靠功能选择弹窗单选回填、之后锁死)、
|
||||
* 启用?(EnabledIndc Y/N switch)、功能说明(Intro textarea);
|
||||
* - 子表1 操作步骤 SecurityHelpSteps:内联可编辑 Title/Description/Tips/Icon,可上下移动;
|
||||
* - 子表2 引用的功能点组 SecurityHelpFunctionFeatures:GroupCode 靠 help-feature-enquiry 单选,
|
||||
* - 子表2 引用的功能点组 SecurityHelpFunctionFeatures:GroupCode 靠功能点选择弹窗单选,
|
||||
* 回传的功能点按 HelpFeatureGroupId 翻译成组编码写入;可上下移动;
|
||||
* - 保存前按界面顺序重排 StepNo=i+1 / SortNo=i+1,调 SaveFunction(不是 Insert/Update!);
|
||||
* 删除走 DeleteByFunctionName;新建默认 EnabledIndc='Y'。
|
||||
@@ -19,7 +19,8 @@ import type { SecurityHelpFeatureGroupEntity } from "@/data/auto/securityHelpFea
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, ref } from "vue";
|
||||
import { useAdminPick, useAuthGuard } from "@/composables";
|
||||
import { useAuthGuard } from "@/composables";
|
||||
import { useFunctionPickConfig, useHelpFeaturePickConfig } from "@/composables/usePickerConfigs";
|
||||
import { SecurityHelpFunctionData } from "@/data/auto/securityHelpFunctionData";
|
||||
import { SecurityHelpFunctionFeatureEntity } from "@/data/auto/securityHelpFunctionFeatureEntity";
|
||||
import { SecurityHelpStepEntity } from "@/data/auto/securityHelpStepEntity";
|
||||
@@ -213,25 +214,33 @@ function onBack(): void {
|
||||
smartBack("mineTab");
|
||||
}
|
||||
|
||||
// 功能名称:新建时单选 function-enquiry 回填(旧页用 FindFunctionNameOptions 下拉,移动端退化成选择器)
|
||||
const functionNamePicker = useAdminPick("functionName");
|
||||
// 功能名称:新建时单选弹窗回填
|
||||
const functionConfig = useFunctionPickConfig();
|
||||
const showFunctionPicker = ref(false);
|
||||
|
||||
function pickFunctionName(): void {
|
||||
if (readonly.value || !isNew.value || !perm.canAmend.value) {
|
||||
return;
|
||||
}
|
||||
functionNamePicker.open({ page: "functionEnquiry" }, (rows) => {
|
||||
showFunctionPicker.value = true;
|
||||
}
|
||||
|
||||
function onFunctionPicked(rows: any[]): void {
|
||||
if (rows.length) {
|
||||
helpFunction.value.FunctionName = String(rows[0].FunctionName ?? "");
|
||||
}
|
||||
});
|
||||
showFunctionPicker.value = false;
|
||||
}
|
||||
|
||||
// 引用的功能点组:单选 help-feature-enquiry,回传功能点后翻译成组编码
|
||||
const groupPicker = useAdminPick("group");
|
||||
// 引用的功能点组:单选弹窗,回传功能点后翻译成组编码
|
||||
const featureConfig = useHelpFeaturePickConfig();
|
||||
const showFeaturePicker = ref(false);
|
||||
|
||||
function addFeature(): void {
|
||||
groupPicker.open({ page: "helpFeatureEnquiry" }, (rows) => {
|
||||
showFeaturePicker.value = true;
|
||||
}
|
||||
|
||||
function onFeaturePicked(rows: any[]): void {
|
||||
ensureLists();
|
||||
if (!rows.length) {
|
||||
return;
|
||||
@@ -250,7 +259,7 @@ function addFeature(): void {
|
||||
row.GroupCode = groupCode;
|
||||
row.SortNo = data.value.SecurityHelpFunctionFeatures.length + 1;
|
||||
data.value.SecurityHelpFunctionFeatures.push(row);
|
||||
});
|
||||
showFeaturePicker.value = false;
|
||||
}
|
||||
|
||||
function deleteFeature(index: number): void {
|
||||
@@ -486,6 +495,17 @@ useAuthGuard(() => {
|
||||
|
||||
<help-doc-preview :visible="previewVisible" :title="heroTitle" :intro="helpFunction.Intro" :steps="previewSteps"
|
||||
:features="previewFeatures" @close="previewVisible = false" />
|
||||
|
||||
<!-- 弹窗选择器 -->
|
||||
<admin-pick-popup v-model:visible="showFunctionPicker" :title="t('FunctionPage.title')"
|
||||
:columns="functionConfig.columns.value" :loader="functionConfig.loader" :row-key="functionConfig.rowKey"
|
||||
:title-key="functionConfig.titleKey" :multiple="false" :fixed-filters="functionConfig.fixedFilters.value"
|
||||
@confirm="onFunctionPicked" @close="showFunctionPicker = false" />
|
||||
|
||||
<admin-pick-popup v-model:visible="showFeaturePicker" :title="t('HelpFeaturePage.title')"
|
||||
:columns="featureConfig.columns.value" :loader="featureConfig.loader" :row-key="featureConfig.rowKey"
|
||||
:title-key="featureConfig.titleKey" :multiple="false" :fixed-filters="featureConfig.fixedFilters.value"
|
||||
@confirm="onFeaturePicked" @close="showFeaturePicker = false" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 扩展权限选择页:旧 WebVue views/security/PermissionDependentModal.vue 的移动端等价物
|
||||
* 扩展权限查询页:旧 WebVue views/security/PermissionDependentModal.vue 的移动端等价物
|
||||
*
|
||||
* 旧页面就是一个纯"选择弹窗"(dragModal 内嵌),供 group-operation 挑扩展权限明细,
|
||||
* 移动端改成独立选择页:用 FindDependentsBySearchCriteria,返回落在 data.Dependents;
|
||||
* 唯一筛选是按"扩展名称"等值(EQUAL_TO)——由调用方通过 ?dependent= 传进来锁死,
|
||||
* 旧页面就是一个纯"选择弹窗"(dragModal 内嵌),移动端弹窗选择已由 group-operation 内嵌
|
||||
* <admin-pick-popup> 实现,本页保留为独立查询列表页:
|
||||
* 用 FindDependentsBySearchCriteria,返回落在 data.Dependents;
|
||||
* 唯一筛选是按"扩展名称"等值(EQUAL_TO)——可由 ?dependent= 传入锁定范围,
|
||||
* 因为扩展权限明细必须归属某个权限的扩展名称之下。
|
||||
*/
|
||||
import type { AdminColumn, AdminQuery, AdminRow } from "@/admin/admin-types";
|
||||
import type { AdminColumn, AdminQuery } from "@/admin/admin-types";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, nextTick, reactive, ref, watch } from "vue";
|
||||
import { fillCriteria, makeFilterOption } from "@/admin/admin-filter";
|
||||
import {
|
||||
useAdminList,
|
||||
useAdminPickTarget,
|
||||
useAuthGuard,
|
||||
} from "@/composables";
|
||||
import { SecurityPermissionDependentEntity } from "@/data/auto/securityPermissionDependentEntity";
|
||||
@@ -24,7 +24,6 @@ import { SearchFilterTypePolicy } from "@/data/custom/searchFilterTypePolicy";
|
||||
import securityPermissionService from "@/service/security/securityPermissionService";
|
||||
|
||||
const { t, currentLocale } = useLocale();
|
||||
const pickTarget = useAdminPickTarget();
|
||||
|
||||
const LABEL_KEYS: Record<string, string> = {
|
||||
[SecurityPermissionDependentEntity.DB_DEPENDENT]: "PermissionPage.dependent",
|
||||
@@ -79,22 +78,6 @@ function onSearch(): void {
|
||||
state.reload();
|
||||
}
|
||||
|
||||
function onRowTap(row: AdminRow): void {
|
||||
// 单选语义:直接回传;多选交给底部"确定"按钮统一交付
|
||||
if (pickTarget.multiple.value) {
|
||||
return;
|
||||
}
|
||||
pickTarget.deliverOne(row);
|
||||
}
|
||||
|
||||
function onConfirm(rows: AdminRow[]): void {
|
||||
if (!rows.length) {
|
||||
uni.showToast({ title: t("AdminPage.pickNoneTip"), icon: "none" });
|
||||
return;
|
||||
}
|
||||
pickTarget.deliver(rows);
|
||||
}
|
||||
|
||||
watch(currentLocale, () => {
|
||||
for (const option of bindOptions) {
|
||||
const key = LABEL_KEYS[option.BindValue];
|
||||
@@ -105,8 +88,7 @@ watch(currentLocale, () => {
|
||||
});
|
||||
|
||||
onLoad((options) => {
|
||||
pickTarget.readOptions(options);
|
||||
// 调用方锁死的扩展名称:写进等值筛选,只列出该扩展名称下的子项目
|
||||
// 可选的扩展名称过滤:?dependent= 传入时只列出该扩展名称下的子项目
|
||||
const dependent = String(options?.dependent ?? "").trim();
|
||||
if (dependent) {
|
||||
const option = bindOptions.find(
|
||||
@@ -127,16 +109,12 @@ useAuthGuard(() => {
|
||||
<template>
|
||||
<view class="page">
|
||||
<admin-list ref="listRef" v-model:keyword="keyword" :nav-title="t('PermissionPage.dependentTitle')"
|
||||
:show-nav-back="false" :columns="columns" :loader="state.query"
|
||||
:show-nav-back="true" :columns="columns" :loader="state.query"
|
||||
:row-key="SecurityPermissionDependentEntity.DEPENDENT_DETAIL_NAME" title-key="DependentDetailName"
|
||||
:selectable="pickTarget.multiple.value" :filter-count="filterCount" :filter-visible="filterVisible"
|
||||
:filter-items="draft" :search-token="searchToken" @search="onSearch" @open-filter="state.openFilter()"
|
||||
:filter-count="filterCount" :filter-visible="filterVisible" :filter-items="draft"
|
||||
:search-token="searchToken" @search="onSearch" @open-filter="state.openFilter()"
|
||||
@close-filter="state.closeFilter()" @apply-filter="state.applyFilter($event)"
|
||||
@clear-filter="state.clearFiltersAndSearch()" @row-tap="onRowTap">
|
||||
<template #actions="{ rows }">
|
||||
<u-button type="primary" shape="circle" size="mini" :text="t('AdminPage.pickDoneBut')"
|
||||
@click="onConfirm(rows)" />
|
||||
</template>
|
||||
@clear-filter="state.clearFiltersAndSearch()">
|
||||
</admin-list>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -5,17 +5,14 @@
|
||||
* 与旧页面对齐的关键点:
|
||||
* - findBySearchCriteria 的返回落在 data.Permissions(不是 Datas!),逐字照抄;
|
||||
* - 四条筛选:权限名称/中文名称/扩展名称 文本包含、创建日期 区间;
|
||||
* - 行点击进详情(无 channel)/ 作为选择器回传(带 channel)。
|
||||
* group-operation、function-operation 会把它当"权限选择器"跳进来(多选 / 单选)。
|
||||
* - 行点击进详情。
|
||||
*/
|
||||
import type { AdminColumn, AdminQuery, AdminRow } from "@/admin/admin-types";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, nextTick, reactive, ref, watch } from "vue";
|
||||
import { fillCriteria, makeFilterOption } from "@/admin/admin-filter";
|
||||
import {
|
||||
useAdminList,
|
||||
useAdminPickTarget,
|
||||
useAuthGuard,
|
||||
} from "@/composables";
|
||||
import { SecurityPermissionEntity } from "@/data/auto/securityPermissionEntity";
|
||||
@@ -28,9 +25,6 @@ import securityPermissionService from "@/service/security/securityPermissionServ
|
||||
|
||||
const { t, currentLocale } = useLocale();
|
||||
const perm = usePagePermission("permissionEnquiry");
|
||||
const pickTarget = useAdminPickTarget();
|
||||
|
||||
const isPick = ref(false);
|
||||
|
||||
const LABEL_KEYS: Record<string, string> = {
|
||||
[SecurityPermissionEntity.DB_PERMISSION_NAME]: "PermissionPage.permissionName",
|
||||
@@ -116,13 +110,6 @@ function onSearch(): void {
|
||||
}
|
||||
|
||||
function onRowTap(row: AdminRow): void {
|
||||
if (isPick.value) {
|
||||
if (pickTarget.multiple.value) {
|
||||
return;
|
||||
}
|
||||
pickTarget.deliverOne(row);
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `${pageUrl("permissionOperation")}?id=${row.PermissionName}&mode=view`,
|
||||
});
|
||||
@@ -132,14 +119,6 @@ function onCreate(): void {
|
||||
uni.navigateTo({ url: `${pageUrl("permissionOperation")}?mode=create` });
|
||||
}
|
||||
|
||||
function onConfirm(rows: AdminRow[]): void {
|
||||
if (!rows.length) {
|
||||
uni.showToast({ title: t("AdminPage.pickNoneTip"), icon: "none" });
|
||||
return;
|
||||
}
|
||||
pickTarget.deliver(rows);
|
||||
}
|
||||
|
||||
async function onDelete(rows: AdminRow[]): Promise<void> {
|
||||
if (!rows.length) {
|
||||
uni.showToast({ title: t("AdminPage.pickNoneTip"), icon: "none" });
|
||||
@@ -170,11 +149,6 @@ watch(currentLocale, () => {
|
||||
}
|
||||
});
|
||||
|
||||
onLoad((options) => {
|
||||
pickTarget.readOptions(options);
|
||||
isPick.value = String(options?.channel ?? "").trim().length > 0;
|
||||
});
|
||||
|
||||
useAuthGuard(() => {
|
||||
// 每次显示都重拉第一页:增/改/删后从详情返回能看到最新数据(navigateBack 不重新挂页面)
|
||||
nextTick(() => listRef.value?.refresh());
|
||||
@@ -183,24 +157,20 @@ useAuthGuard(() => {
|
||||
|
||||
<template>
|
||||
<view class="page">
|
||||
<admin-list ref="listRef" v-model:keyword="keyword" :nav-title="t('PermissionPage.title')"
|
||||
:show-nav-back="!isPick" :columns="columns" :loader="state.query"
|
||||
:row-key="SecurityPermissionEntity.PERMISSION_NAME" title-key="PermissionName"
|
||||
:selectable="isPick ? pickTarget.multiple.value : Boolean(perm.canDelete.value)" :filter-count="filterCount"
|
||||
:filter-visible="filterVisible" :filter-items="draft" :search-token="searchToken"
|
||||
:keep-selection-on-reload="isPick" @search="onSearch" @open-filter="state.openFilter()"
|
||||
@close-filter="state.closeFilter()" @apply-filter="state.applyFilter($event)"
|
||||
@clear-filter="state.clearFiltersAndSearch()" @row-tap="onRowTap">
|
||||
<template v-if="!isPick && perm.canCreate.value" #toolbar-actions>
|
||||
<admin-list ref="listRef" v-model:keyword="keyword" :nav-title="t('PermissionPage.title')" :show-nav-back="true"
|
||||
:columns="columns" :loader="state.query" :row-key="SecurityPermissionEntity.PERMISSION_NAME"
|
||||
title-key="PermissionName" :selectable="Boolean(perm.canDelete.value)" :filter-count="filterCount"
|
||||
:filter-visible="filterVisible" :filter-items="draft" :search-token="searchToken" @search="onSearch"
|
||||
@open-filter="state.openFilter()" @close-filter="state.closeFilter()"
|
||||
@apply-filter="state.applyFilter($event)" @clear-filter="state.clearFiltersAndSearch()" @row-tap="onRowTap">
|
||||
<template v-if="perm.canCreate.value" #toolbar-actions>
|
||||
<view class="create-entry" @click="onCreate">
|
||||
<u-icon name="plus" size="40" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template #actions="{ rows }">
|
||||
<u-button v-if="isPick" type="primary" shape="circle" size="mini" :text="t('AdminPage.pickDoneBut')"
|
||||
@click="onConfirm(rows)" />
|
||||
<u-button v-else-if="perm.canDelete.value" type="error" shape="circle" size="mini"
|
||||
<u-button v-if="perm.canDelete.value" type="error" shape="circle" size="mini"
|
||||
:text="t('AdminPage.deleteBut')" @click="onDelete(rows)" />
|
||||
</template>
|
||||
</admin-list>
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
* 丢掉的东西:多列网格、列配置、打印——手机上没有对应交互。
|
||||
*
|
||||
* 页面只组数据与路由,列表/筛选/分页的机制全在 admin-list + useAdminList 里。
|
||||
* actor-operation 的"选用户"弹窗已由调用方内嵌 <admin-pick-popup> 实现,
|
||||
* 本页不再承担选择器职责(useAdminPick / useAdminPickTarget 协议已移除)。
|
||||
*/
|
||||
import type { AdminColumn, AdminQuery, AdminRow } from "@/admin/admin-types";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, nextTick, reactive, ref, watch } from "vue";
|
||||
import { fillCriteria, makeFilterOption } from "@/admin/admin-filter";
|
||||
import { useAdminList, useAdminPickTarget, useAuthGuard } from "@/composables";
|
||||
import { useAdminList, useAuthGuard } from "@/composables";
|
||||
import { SecurityUserEntity } from "@/data/auto/securityUserEntity";
|
||||
import { SecurityUserSearchCriteria } from "@/data/auto/securityUserSearchCriteria";
|
||||
import { SearchFilterOperatorPolicy } from "@/data/custom/searchFilterOperatorPolicy";
|
||||
@@ -24,10 +25,6 @@ import securityUserService from "@/service/security/securityUserService";
|
||||
|
||||
const { t, currentLocale } = useLocale();
|
||||
const perm = usePagePermission("userEnquiry");
|
||||
const pickTarget = useAdminPickTarget();
|
||||
|
||||
/** 选择器模式:带 channel 进来(如 actor-operation 选用户)时点行回传而非跳详情 */
|
||||
const isPick = ref(false);
|
||||
|
||||
/** 列名 -> i18n key:抽屉标签、卡片行标签都靠它,切换语言时就地刷新而不重建条件 */
|
||||
const LABEL_KEYS: Record<string, string> = {
|
||||
@@ -107,28 +104,13 @@ function onSearch(): void {
|
||||
}
|
||||
|
||||
function onRowTap(row: AdminRow): void {
|
||||
if (isPick.value) {
|
||||
if (pickTarget.multiple.value) {
|
||||
return;
|
||||
}
|
||||
pickTarget.deliverOne(row);
|
||||
return;
|
||||
}
|
||||
// 与旧页面一致:id 用 UserName,mode 决定详情页是只读还是编辑
|
||||
// 不做 encodeURIComponent:见 composables/useAdminPick 的说明(H5 二次编码坑)
|
||||
// 不做 encodeURIComponent:URL 参数按原样透传,避免 H5 二次编码坑
|
||||
uni.navigateTo({
|
||||
url: `${pageUrl("userOperation")}?id=${row.UserName}&mode=view`,
|
||||
});
|
||||
}
|
||||
|
||||
function onConfirm(rows: AdminRow[]): void {
|
||||
if (!rows.length) {
|
||||
uni.showToast({ title: t("AdminPage.pickNoneTip"), icon: "none" });
|
||||
return;
|
||||
}
|
||||
pickTarget.deliver(rows);
|
||||
}
|
||||
|
||||
function onCreate(): void {
|
||||
uni.navigateTo({ url: `${pageUrl("userOperation")}?mode=create` });
|
||||
}
|
||||
@@ -165,11 +147,6 @@ watch(currentLocale, () => {
|
||||
}
|
||||
});
|
||||
|
||||
onLoad((options) => {
|
||||
pickTarget.readOptions(options);
|
||||
isPick.value = String(options?.channel ?? "").trim().length > 0;
|
||||
});
|
||||
|
||||
// 取数排在登录守卫之后:admin-list 的 auto 默认 false,首屏由这里手动触发一次
|
||||
useAuthGuard(() => {
|
||||
// 每次显示都重拉第一页:增/改/删后从详情返回能看到最新数据(navigateBack 不重新挂页面)
|
||||
@@ -179,23 +156,20 @@ useAuthGuard(() => {
|
||||
|
||||
<template>
|
||||
<view class="page">
|
||||
<admin-list ref="listRef" v-model:keyword="keyword" :nav-title="t('UserPage.title')" :show-nav-back="!isPick"
|
||||
<admin-list ref="listRef" v-model:keyword="keyword" :nav-title="t('UserPage.title')" :show-nav-back="true"
|
||||
:columns="columns" :loader="state.query" :row-key="SecurityUserEntity.USER_NAME" title-key="UserLocalName"
|
||||
:selectable="isPick ? pickTarget.multiple.value : Boolean(perm.canDelete.value)" :filter-count="filterCount"
|
||||
:filter-visible="filterVisible" :filter-items="draft" :search-token="searchToken"
|
||||
:keep-selection-on-reload="isPick" @search="onSearch" @open-filter="state.openFilter()"
|
||||
:selectable="Boolean(perm.canDelete.value)" :filter-count="filterCount" :filter-visible="filterVisible"
|
||||
:filter-items="draft" :search-token="searchToken" @search="onSearch" @open-filter="state.openFilter()"
|
||||
@close-filter="state.closeFilter()" @apply-filter="state.applyFilter($event)"
|
||||
@clear-filter="state.clearFiltersAndSearch()" @row-tap="onRowTap">
|
||||
<template v-if="!isPick && perm.canCreate.value" #toolbar-actions>
|
||||
<template v-if="perm.canCreate.value" #toolbar-actions>
|
||||
<view class="create-entry" @click="onCreate">
|
||||
<u-icon name="plus" size="40" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template #actions="{ rows }">
|
||||
<u-button v-if="isPick" type="primary" shape="circle" size="mini" :text="t('AdminPage.pickDoneBut')"
|
||||
@click="onConfirm(rows)" />
|
||||
<u-button v-else-if="perm.canDelete.value" type="error" shape="circle" size="mini"
|
||||
<u-button v-if="perm.canDelete.value" type="error" shape="circle" size="mini"
|
||||
:text="t('AdminPage.deleteBut')" @click="onDelete(rows)" />
|
||||
</template>
|
||||
</admin-list>
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
* - 密码编辑的是 SecurityUserDisplayData.OrgUserPassword(留空即不改);
|
||||
* - 角色明细按 ActorName 去重合并,删除即从数组里剔除。
|
||||
*
|
||||
* 旧页面的"选角色"是弹窗内嵌 Enquiry + provide/inject;这里改成真跳 actor-enquiry 页
|
||||
* 再回传(见 composables/useAdminPick),所以角色区放在 admin-form 的 #extra 插槽里自绘。
|
||||
* 旧页面的"选角色"是弹窗内嵌 Enquiry + provide/inject;这里用 <admin-pick-popup>
|
||||
* 底部弹窗(数据源见 composables/usePickerConfigs),角色区放在 admin-form 的 #extra 插槽里自绘。
|
||||
*/
|
||||
import type {
|
||||
AdminFormAction,
|
||||
@@ -20,7 +20,8 @@ import type {
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, ref } from "vue";
|
||||
import { useAdminPick, useAuthGuard } from "@/composables";
|
||||
import { useAuthGuard } from "@/composables";
|
||||
import { useActorPickConfig } from "@/composables/usePickerConfigs";
|
||||
import { SecurityUserActorViewEntity } from "@/data/auto/securityUserActorViewEntity";
|
||||
import { SecurityUserData } from "@/data/auto/securityUserData";
|
||||
import { usePagePermission } from "@/permission/permission-context";
|
||||
@@ -174,13 +175,15 @@ function onBack(): void {
|
||||
smartBack("mineTab");
|
||||
}
|
||||
|
||||
// 角色选择:跳 actor-enquiry(可多选),回传后按 ActorName 去重合并
|
||||
const actorPicker = useAdminPick<SecurityUserActorViewEntity>("actor");
|
||||
// 角色选择弹窗
|
||||
const actorConfig = useActorPickConfig();
|
||||
const showActorPicker = ref(false);
|
||||
|
||||
function onAddActor(): void {
|
||||
actorPicker.open(
|
||||
{ page: "actorEnquiry", params: { multiple: "1" } },
|
||||
(rows) => {
|
||||
showActorPicker.value = true;
|
||||
}
|
||||
|
||||
function onActorPicked(rows: any[]): void {
|
||||
const exists = new Set(
|
||||
data.value.SecurityUserActors.map(item => item.ActorName),
|
||||
);
|
||||
@@ -196,8 +199,7 @@ function onAddActor(): void {
|
||||
data.value.SecurityUserActors.push(entity);
|
||||
exists.add(actorName);
|
||||
}
|
||||
},
|
||||
);
|
||||
showActorPicker.value = false;
|
||||
}
|
||||
|
||||
function onDeleteActor(actorName: string): void {
|
||||
@@ -266,6 +268,12 @@ useAuthGuard(() => {
|
||||
</view>
|
||||
</template>
|
||||
</admin-form>
|
||||
|
||||
<!-- 弹窗选择器 -->
|
||||
<admin-pick-popup v-model:visible="showActorPicker" :title="t('UserPage.actorTitle')"
|
||||
:columns="actorConfig.columns.value" :loader="actorConfig.loader" :row-key="actorConfig.rowKey"
|
||||
:title-key="actorConfig.titleKey" :multiple="true" :fixed-filters="actorConfig.fixedFilters.value"
|
||||
@confirm="onActorPicked" @close="showActorPicker = false" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -336,7 +336,7 @@ function onRemoveGroup(familyName: string): void {
|
||||
<text class="seg-bar__text">{{ t(seg.labelKey) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<search-input v-if="showSearch" v-model="keyword" :placeholder="t('FilePage.searchPlaceholder')"
|
||||
<search-input v-if="showSearch" v-model="keyword" :placeholder="t('MediaPage.searchPlaceholder')"
|
||||
@search="onSearch" />
|
||||
</view>
|
||||
<view class="media-page__scroll-wrap">
|
||||
|
||||
@@ -320,7 +320,7 @@ function groupDesc(group: FamilyGroupEntity): string {
|
||||
<page-navbar :title="t('SharePage.title')" :show-back="false" :search-on="showSearch"
|
||||
:show-transfer="false" :show-quick-toggle="false" @back="onBack"
|
||||
@toggle-search="onToggleSearch" />
|
||||
<search-input v-if="showSearch" v-model="keyword" :placeholder="t('FilePage.searchPlaceholder')"
|
||||
<search-input v-if="showSearch" v-model="keyword" :placeholder="t('SharePage.searchPlaceholder')"
|
||||
@search="onSearch" />
|
||||
</view>
|
||||
<view class="share-page__scroll-wrap">
|
||||
|
||||
Reference in new issue
Block a user