This commit is contained in:
owenchen committed 2026-08-28 17:02:34 +08:00
1 parent c253805122
commit 7df55a11bf
46 files changed
+3689 -27

No files matched your search

+1 -1
View File
@@ -4,7 +4,7 @@
<br />
<a-row :wrap="false">
<a-col :lg="24">
<a-input flex="none" readonly value="视图" class="site-input-split" style="
<a-input flex="none" readonly value="当前视图" class="site-input-split" style="
width: 80px;
border-radius: 0;
border-top-left-radius: 5pt 5pt;
+7 -3
View File
@@ -60,6 +60,10 @@ const emit = defineEmits<EmitsType>();
const sortable = ref<Sortable | null>(null);
onMounted(() => {
let i: number = 0;
props.list.forEach((element: Column) => {
element.sequence = i++;
});
initDraggable();
});
@@ -82,13 +86,13 @@ const initDraggable = () => {
onUpdate(e: any) {
if (e.oldIndex !== undefined && e.newIndex !== undefined) {
// 删除拖拽的元素
const list = [...props.list];
let list = [...props.list];
const item = list.splice(e.oldIndex, 1)[0];
// 把删除的元素放到新的位置
list.splice(e.newIndex, 0, item);
let i: number = 0;
list.forEach((element: Column) => {
element.sequence = i++;
list = list.map((element: Column) => {
return { ...element, sequence: i++ };
});
emit('update:list', list);
emit('onChangeList', list);
+3 -3
View File
@@ -115,7 +115,7 @@ import {
} from "vue";
import { usePagination, type PaginationQueryResult } from "vue-request";
import type { TablePaginationConfig, TableProps } from "ant-design-vue";
import type { ATable } from "ant-design-vue";
import { Table as ATable } from 'ant-design-vue';
import searchCriteria from "@/components/basic/SearchCriteria.vue";
import { SearchFilter } from "@/data/auto/searchFilter";
import { SearchCriteriaResultData } from "@/data/auto/searchCriteriaResultData";
@@ -387,10 +387,10 @@ const onSelectChange = (changableRowKeys: any[]) => {
emit("onSelectChange", selected.value);
};
const rowSelection = computed(() => {
const rowSelection = computed<TableProps["rowSelection"]>(() => {
return {
columnWidth: "4%",
type: rowSelectionType.value,
type: rowSelectionType.value as "radio" | "checkbox",
selectedRowKeys: unref(selected),
onChange: onSelectChange,
};
@@ -0,0 +1,93 @@
import { request,download } from "@/utils/request";
import type { FamilyPersonEntity } from "@/data/auto/familyPersonEntity";
import type { FamilyPersonData } from "@/data/auto/familyPersonData";
import type { FamilyPersonSearchCriteria } from "@/data/auto/familyPersonSearchCriteria";
import type { FamilyPersonResultData } from "@/data/auto/familyPersonResultData";
class FamilyPersonService{
async FindAll() {
return request<FamilyPersonEntity[]>(
{
url: "/FamilyPerson/FindAll",
method: "post",
}
)
};
async FindByKey(data: string) {
return request<FamilyPersonData>(
{
url: "/FamilyPerson/FindByKey?key="+data,
method: "post",
data,
}
)
};
async FindBySearchCriteria(data: FamilyPersonSearchCriteria) {
return request<FamilyPersonResultData>(
{
url: "/FamilyPerson/FindBySearchCriteria",
method: "post",
data,
}
)
};
async PrintBySearchCriteria(data: FamilyPersonSearchCriteria) {
return download(
{
url: "/FamilyPerson/PrintBySearchCriteria",
method: "post",
data,
filename:'FamilyPerson'
}
)
};
async Insert(data: FamilyPersonData) {
return request<FamilyPersonData>(
{
url: "/FamilyPerson/Insert",
method: "post",
data,
}
)
};
async Update(data: FamilyPersonData) {
return request<FamilyPersonData>(
{
url: "/FamilyPerson/Update",
method: "post",
data,
}
)
};
async Delete(data: string) {
return request<any>(
{
url: "/FamilyPerson/Delete?key="+data,
method: "post",
data,
}
)
};
async Deletes(data: String[]) {
return request<any>(
{
url: "/FamilyPerson/Deletes",
method: "post",
data,
}
)
};
}
const familyPersonService= new FamilyPersonService();
export default familyPersonService;
@@ -0,0 +1,72 @@
import { request } from "@/utils/request";
import type { FamilyTreeData } from "@/data/auto/familyTreeData";
import type { FamilyTreeSearchCriteria } from "@/data/auto/familyTreeSearchCriteria";
import type { FamilyTreeResultData } from "@/data/auto/familyTreeResultData";
class FamilyTreeService{
async FindByKey(data: string) {
return request<FamilyTreeData>(
{
url: "/FamilyTree/FindByKey?key="+data,
method: "post",
data,
}
)
};
async FindBySearchCriteria(data: FamilyTreeSearchCriteria) {
return request<FamilyTreeResultData>(
{
url: "/FamilyTree/FindBySearchCriteria",
method: "post",
data,
}
)
};
async Insert(data: FamilyTreeData) {
return request<FamilyTreeData>(
{
url: "/FamilyTree/Insert",
method: "post",
data,
}
)
};
async Update(data: FamilyTreeData) {
return request<FamilyTreeData>(
{
url: "/FamilyTree/Update",
method: "post",
data,
}
)
};
async Delete(data: string) {
return request<any>(
{
url: "/FamilyTree/Delete?key="+data,
method: "post",
data,
}
)
};
async Deletes(data: String[]) {
return request<any>(
{
url: "/FamilyTree/Deletes",
method: "post",
data,
}
)
};
}
const familyTreeService= new FamilyTreeService();
export default familyTreeService;
@@ -14,15 +14,6 @@ class YesNoPolicyService{
)
};
async Copy() {
return request<any>(
{
url: "/YesNoPolicy/Copy",
method: "get",
}
)
};
}
const yesNoPolicyService= new YesNoPolicyService();
+18 -1
View File
@@ -231,6 +231,18 @@ export const download = async (d: {
service
.post(d.url, d.data)
.then((res) => {
// 关键:先解析响应头,判断是不是json报错
const contentType = res.headers["content-type"] || "";
// 如果返回的是json,代表后端业务异常,不是excel文件
if (contentType.includes("application/json")) {
// arraybuffer转字符串
const text = new TextDecoder().decode(res.data);
const errObj = JSON.parse(text);
errorNotificationWithIcon(errObj.message || errObj.msg || "下载失败");
return; // ❌直接return,不执行下载逻辑
}
// ✅正常excel文件,执行下载
const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
const link = document.createElement("a");
link.href = downloadUrl;
@@ -240,12 +252,17 @@ export const download = async (d: {
);
document.body.appendChild(link);
link.click();
// 释放内存
link.remove();
window.URL.revokeObjectURL(downloadUrl);
})
.catch((error) => {
errorNotificationWithIcon(error.message);
// 网络异常、超时才走到这里
errorNotificationWithIcon(error.message ?? "网络异常,下载失败");
});
};
const errorNotificationWithIcon = (message: string) => {
notification.error({
message: t("request.notification.errorMessage"),
@@ -0,0 +1,339 @@
<template>
<search @on-submit="onSubmit" @on-print="onPrint" @on-reset="onReset" @use-pagination="onPagination"
:is-modal="isModal" :loading="loading" :pagination="result" :rowSelectionType="selectionType"
@on-row-dblclick="onRowDblclick" @on-select-change="onSelectChange">
<template #control>
<a-button type="primary" v-if="canCreate && !isModal" :icon="h(FileAddOutlined)"
@click.prevent="onCreate">新增</a-button>
</template>
<template #bodyCell="{ column, record, index }">
</template>
<template #footer>
<div v-if="!isModal" class="page-footer">
<a-row :gutter="24">
<a-col :lg="24" :sm="24">
<a-row :gutter="[16, 8]">
<a-space>
<a-button type="primary" v-if="canView" :icon="h(FileTextOutlined)"
:disabled="!(keys && keys.length ==1)" @click.prevent="onView">查看</a-button>
<a-button type="primary" v-if="canAmend" :icon="h(EditOutlined)"
:disabled="!(keys && keys.length ==1)" @click.prevent="onAmend">修改</a-button>
<a-popconfirm title="确定是否删除这条记录?" ok-text="是" cancel-text="否" @confirm="onDelete">
<a-button type="primary" danger :icon="h(DeleteOutlined)" v-if="canDelete"
:disabled="!(keys && keys.length > 0)">删除</a-button>
</a-popconfirm>
</a-space>
</a-row>
</a-col>
</a-row>
</div>
</template>
</search>
</template>
<script setup lang="ts">
import { reactive, inject, defineComponent, onMounted, ref, unref, provide, h } from "vue";
import { BindOption } from "@/data/custom/bindOption";
import { SelectOption } from "@/data/auto/selectOption";
import search from "@/components/basic/Search.vue";
import { DeleteOutlined, EditOutlined, FileAddOutlined, FileTextOutlined } from "@ant-design/icons-vue";
import { FamilyPersonResultData } from "@/data/auto/familyPersonResultData";
import { FamilyPersonEntity } from "@/data/auto/familyPersonEntity";
import { SearchFilter } from "@/data/auto/searchFilter"
import familyPersonService from "@/service/master/familyPersonService";
import { FamilyPersonSearchCriteria } from "@/data/auto/familyPersonSearchCriteria";
import { SearchCriteriaResultData } from "@/data/auto/searchCriteriaResultData";
import { PermissionContext, usePermissionContext } from "@/hooks/PermissionContext"
import { useRoute, useRouter } from "vue-router";
import { useBindOption } from "@/hooks/useBindOption";
import { SearchFilterOperatorPolicy } from "@/data/custom/searchFilterOperatorPolicy"
import { SearchFilterTypePolicy } from "@/data/custom/searchFilterTypePolicy"
import { notification } from "ant-design-vue";
import { GridColumnInfo } from "@/data/auto/gridColumnInfo"
import { useGridColumnInfo } from "@/hooks/gridColumnInfo"
import { title } from "process";
import { useOrderedChildren } from "@/hooks/use-ordered-children";
import { SearchCriteriaContext } from "@/injections";
import { enquiryMethodKey, searchCriteriaManagerKey, operationToEnquiryMethodKey, OperationToEnquiryModelOption, layoutProviderMethodKey } from "@/injections";
//page from-----------------------------------------------------------------------------------------------------------------------------------
//================inject================
const actorOperationMethod = inject(operationToEnquiryMethodKey);
const modelOption = actorOperationMethod?.getOperationToEnquiryModelOption() as OperationToEnquiryModelOption;
const isModal = ref(modelOption ? modelOption.isModalPermission : false);
const selectionType = ref(modelOption ? modelOption.selectionTypePermission : 'checkbox');
const initBindOptions = ref((modelOption ? modelOption.initBindOptionsPermission : []) as BindOption[]);
//报表名称(layout)
const layoutProviderMethod = inject(layoutProviderMethodKey);
//================inject================
const emit = defineEmits(['onRowDblclick']);
const loading = ref<boolean>(false);
const canView = ref<boolean>(false);
const canAmend = ref<boolean>(false);
const canDelete = ref<boolean>(false);
const canCreate = ref<boolean>(false);
const route = useRoute();
const router = useRouter();
const columns = ref([
{
title: '序号',
dataIndex: 'index',
width: '5%',
align: 'center',
fixed: 'left',
sequence: 0,
hidden: 'N',
format: ''
},
{
title: "姓氏",
dataIndex: FamilyPersonEntity.SURNAME,
sorter: true,
width: '15%',
sequence: 1,
hidden: 'N'
},
{
title: "谱名",
dataIndex: FamilyPersonEntity.GENEALOGY_NAME,
sorter: true,
width: '15%',
sequence: 2,
hidden: 'N'
},
{
title: "常用名",
dataIndex: FamilyPersonEntity.COMMON_NAME,
sorter: true,
width: '20%',
sequence: 2,
hidden: 'N'
},
{
title: "创建日期",
dataIndex: FamilyPersonEntity.CREATED_DATETIME,
sorter: true,
width: '20%',
sequence: 2,
hidden: 'N'
},
]);
const viewOption = {
Value: 'default',
SelectOptions: [{ BindValue: 'default', DisplayName: "默认" }],
Columns: [{ BindValue: 'default', BindColumn: columns }]
};
const bindOptions = ref<BindOption[]>([]);
const datas = ref<FamilyPersonEntity[]>([]);
const pagingParams = ref<PagingParams>({ pageSize: 20, page: 1 });
const result = ref<SearchCriteriaResultData>(new SearchCriteriaResultData());
const data = ref<FamilyPersonResultData>(new FamilyPersonResultData());
const isSearched = ref<boolean>(false);
const currentSearchCriteria = ref<FamilyPersonSearchCriteria>(new FamilyPersonSearchCriteria());
const keys = ref<FamilyPersonEntity[]>([]);
const {
children: items,
addChild: addItem,
removeChild: removeItem,
} = useOrderedChildren<SearchCriteriaContext>();
//================provide================
//初始化查询条件UI(search)
provide(enquiryMethodKey, {
getLayoutSetting: () => { return { controlLeft: 16, controlRight: 8, space: true, soltLeft: 24, soltRight: 24 }; },
getBindOptions: () => { return unref(bindOptions); },
getViewOption: () => { return unref(viewOption); },
getDatas: () => { return datas; },
});
provide(
searchCriteriaManagerKey,
reactive({
items,
addItem,
removeItem,
})
);
//================provide================
defineExpose({
keys,
initBindOptions,
})
onMounted(() => {
let pContext: PermissionContext = usePermissionContext();
canView.value = pContext.canView(route.meta.permission as string);
canAmend.value = pContext.canAmend(route.meta.permission as string);
canDelete.value = pContext.canDelete(route.meta.permission as string);
canCreate.value = pContext.canCreate(route.meta.permission as string);
initBindOption();
onSearch();
})
const onSelectChange = ((selected: FamilyPersonEntity[]) => {
keys.value = selected;
})
const onRowDblclick = ((e: any, record: FamilyPersonEntity) => {
if (isModal.value) {
emit('onRowDblclick', record);
}
else {
router.push({ name: "FamilyPersonOperation", params: { id: record.PersonId, mode: 'view' } });
}
})
const onReset = (() => {
keys.value = [];
result.value.Total = 0;
})
const onView = (() => {
if (keys.value.length == 0) return;
router.push({ name: "FamilyPersonOperation", params: { id: keys.value[0].PersonId, mode: 'view' } });
})
const onCreate = (() => {
router.push({ name: "FamilyPersonOperation", params: { id: '', mode: 'create' } });
})
const onAmend = (() => {
if (keys.value.length == 0) return;
router.push({ name: "FamilyPersonOperation", params: { id: keys.value[0].PersonId, mode: 'amend' } });
})
const onDelete = (async () => {
for (let i = 0; i < keys.value.length; i++) {
await familyPersonService.Delete(keys.value[i].PersonId);
}
notification.info({
message: '系统提示:',
description: '记录删除成功',
placement: "topRight",
});
onReload();
})
const setBindOptionValue = ((bindValue: string, value: string, value1: string, values: string[], disabled: boolean) => {
let bindOption: BindOption = bindOptions.value.filter(x => x.BindValue == bindValue)[0];
if (!bindOption) return;
bindOption.Value = value;
bindOption.Value1 = value1;
bindOption.Values = values;
bindOption.disabled = disabled;
})
//初始化查询条件
const initBindOption = (async () => {
let ubo = useBindOption();
let bindOption: BindOption = ubo.getBindOption(FamilyPersonEntity.DB_SURNAME, '姓氏', SearchFilterTypePolicy.TEXT, SearchFilterOperatorPolicy.CONTAIN);
bindOptions.value.push(bindOption);
bindOption = ubo.getBindOption(FamilyPersonEntity.DB_GENEALOGY_NAME, '谱名', SearchFilterTypePolicy.TEXT, SearchFilterOperatorPolicy.CONTAIN);
bindOption.lg = 10;
bindOptions.value.push(bindOption);
bindOption = ubo.getBindOption(FamilyPersonEntity.DB_CREATED_DATETIME, '创建日期', SearchFilterTypePolicy.DATE, SearchFilterOperatorPolicy.DATE_BETWEEN);
bindOptions.value.push(bindOption);
bindOption = ubo.getBindOption(FamilyPersonEntity.DB_COMMON_NAME, '常用名', SearchFilterTypePolicy.TEXT, SearchFilterOperatorPolicy.CONTAIN);
bindOption.lg = 10;
bindOptions.value.push(bindOption);
initBindOptions.value.forEach(element => {
setBindOptionValue(element.BindValue, element.Value, element.Value1, element.Values, element.disabled);
});
})
//table from-----------------------------------------------------------------------------------------------------------------------------------
result.value.Page = pagingParams.value.page ?? 1;
result.value.Limit = pagingParams.value.pageSize;
const onSubmit = (async (filters: SearchFilter[]) => {
let c: FamilyPersonSearchCriteria = new FamilyPersonSearchCriteria();
isSearched.value = true;
c.Filter = filters;
c.limit = pagingParams.value.pageSize;
c.page = pagingParams.value.page ?? 1;
c.GridColumns = [];
currentSearchCriteria.value = c;
await findBySearchCriteria(c);
})
const onSearch = (async () => {
onSubmit(items.value[0].getFilter());
})
const onPrint = (async () => {
let c: FamilyPersonSearchCriteria = new FamilyPersonSearchCriteria();
c.Filter = items.value[0].getFilter();
c.limit = pagingParams.value.pageSize;
c.page = pagingParams.value.page ?? 1;
c.GridColumns = useGridColumnInfo().getGridColumnInfo(unref(columns));
c.ReportName = layoutProviderMethod?.getLayoutProvider().layoutTitle!;
try {
let sss = await familyPersonService.PrintBySearchCriteria(c);
} catch (error) {
console.log(error);
}
})
const findBySearchCriteria = (async (c: FamilyPersonSearchCriteria) => {
try {
data.value = await familyPersonService.FindBySearchCriteria(c);
result.value.Total = data.value.Total;
result.value.Page = data.value.Page;
result.value.Limit = data.value.Limit;
datas.value = data.value.Datas;
} catch (error) {
console.log(error);
}
})
const onPagination = (async (params: PagingParams) => {
if (!isSearched.value) return;
let c: FamilyPersonSearchCriteria = new FamilyPersonSearchCriteria();
c.Filter = items.value[0].getFilter();
c.limit = params.pageSize;
c.page = params.page ?? 1;
if (params.sortField)
c.orderby = params.sortField!;
if (params.sortOrder)
c.orderbytag = params.sortOrder?.toString().substring(0, 4)!;
c.GridColumns = [];
currentSearchCriteria.value = c;
await findBySearchCriteria(c);
});
const onReload = (async () => {
await findBySearchCriteria(unref(currentSearchCriteria));
})
//table end-----------------------------------------------------------------------------------------------------------------------------------
</script>
@@ -0,0 +1,960 @@
<template>
<a-spin size="large" :spinning="loading">
<div class="page-container">
<div class="page-header">
<a-row :gutter="[16, 8]">
<a-col :lg="8" :sm="24">
<a-row :wrap="false">
<a-col flex="none">
<div style="padding: 0">
<a-input
flex="none"
readonly
value="族员编号"
class="site-input-split"
style="
width: 160px;
border-radius: 0;
border-top-left-radius: 5pt 5pt;
border-bottom-left-radius: 5pt 5pt;
pointer-events: none;
"
/>
</div>
</a-col>
<a-col flex="auto">
<a-input-search
:disabled="isEdit"
v-model:value="personId"
flex="auto"
@search="onModalSearch"
/>
</a-col>
</a-row>
</a-col>
<a-col :lg="12" :sm="24">
<a-space>
<a-button
type="primary"
:disabled="isEdit"
:icon="h(FileSearchOutlined)"
@click.prevent="onSearch"
>查询</a-button
>
<a-button
type="primary"
:disabled="isEdit"
:icon="h(ClearOutlined)"
@click.prevent="onReset"
>重置</a-button
>
<a-button
type="primary"
:disabled="isEdit"
:icon="h(FileAddOutlined)"
@click.prevent="onCreate"
>新增</a-button
>
</a-space>
</a-col>
</a-row>
</div>
<div class="page-main" :style="{ height: resultH }">
<a-row :gutter="[16, 16]">
<!-- 左侧头像/快捷栏 -->
<a-col :lg="5" :sm="24">
<a-row :wrap="false" justify="center">
<a-col
:flex="'auto'"
style="text-align: center; position: relative"
>
<a-avatar :size="120" :src="data.FamilyPerson.AvatarFilename">
<template #icon>
<UserOutlined />
</template>
</a-avatar>
<div class="avatar-change-mask" v-if="isEdit">点击更换</div>
<a-button
v-if="isEdit && data.FamilyPerson.AvatarFilename"
class="avatar-close"
type="text"
:icon="h(CloseOutlined)"
size="small"
@click.prevent="data.FamilyPerson.AvatarFilename = ''"
></a-button>
</a-col>
</a-row>
<a-row :gutter="[8, 8]" style="margin-top: 12px">
<a-col :span="24">
<a-row :wrap="false" align="middle">
<a-col :flex="'50px'" class="side-label">编号</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="50"
readonly="true"
v-model:value="data.FamilyPerson.PersonId"
/>
</a-col>
</a-row>
</a-col>
<a-col :span="24">
<a-row :wrap="false" align="middle">
<a-col :flex="'50px'" class="side-label">性别</a-col>
<a-col :flex="'auto'">
<a-select
v-model:value="data.FamilyPerson.Gender"
:disabled="!isEdit"
style="width: 100%"
>
<a-select-option
v-for="o in yesNoPolicy"
:value="o.BindValue"
>{{ o.DisplayName }}</a-select-option
>
</a-select>
</a-col>
</a-row>
</a-col>
<a-col :span="24">
<a-row :wrap="false" align="middle">
<a-col :flex="'50px'" class="side-label">婚姻</a-col>
<a-col :flex="'auto'">
<a-select
v-model:value="data.FamilyPerson.MaritalStatus"
:disabled="!isEdit"
style="width: 100%"
>
<a-select-option
v-for="o in yesNoPolicy"
:value="o.BindValue"
>{{ o.DisplayName }}</a-select-option
>
</a-select>
</a-col>
</a-row>
</a-col>
<a-col :span="24">
<a-row :wrap="false" align="middle">
<a-col :flex="'50px'" class="side-label">行第</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="32"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.BirthOrder"
/>
</a-col>
</a-row>
</a-col>
</a-row>
</a-col>
<!-- 右侧表单 -->
<a-col :lg="19" :sm="24">
<a-row :gutter="[8, 8]">
<a-col :span="6">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">姓氏</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="100"
required
:readonly="!isEdit"
v-model:value="data.FamilyPerson.Surname"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
<a-col :span="6">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">谱名</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="100"
required
:readonly="!isEdit"
v-model:value="data.FamilyPerson.GenealogyName"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
<a-col :span="6">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">常用名</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="100"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.CommonName"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
<a-col :span="6">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">别名</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="100"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.Alias"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
</a-row>
<a-row :gutter="[8, 8]" style="margin-top: 8px">
<a-col :span="6">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">字</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="100"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.Zi"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
<a-col :span="6">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">号</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="100"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.Hao"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
<a-col :span="6">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">父亲编号</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="50"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.FatherId"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
<a-col :span="6">
<!-- 关键::gutter="0" 关闭行的栅格间隙 -->
<a-row :wrap="false" align="middle" :gutter="0">
<a-col :flex="'80px'">
<div class="field-tag">母亲姓名</div>
</a-col>
<!-- 去掉这里的 margin‑left!改用 padding-left 在标签div留内边距 -->
<a-col :flex="'auto'">
<a-input
:maxlength="100"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.MotherName"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
</a-row>
<a-row :gutter="[8, 8]" style="margin-top: 8px">
<a-col :span="6">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">学校</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="256"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.School"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
<a-col :span="6">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">专业</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="256"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.Major"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
<a-col :span="6">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">学历</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="128"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.Education"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
<a-col :span="6">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">毕业</div>
</a-col>
<a-col :flex="'auto'">
<a-select
v-model:value="data.FamilyPerson.GraduationStatus"
:disabled="!isEdit"
style="width: 100%"
>
<a-select-option
v-for="o in yesNoPolicy"
:value="o.BindValue"
>{{ o.DisplayName }}</a-select-option
>
</a-select>
</a-col>
</a-row>
</a-col>
</a-row>
<a-row :gutter="[8, 8]" style="margin-top: 8px">
<a-col :span="12">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">职务</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="256"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.Duty"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
<a-col :span="12">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">头衔</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="256"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.Title"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
</a-row>
<a-row :gutter="[8, 8]" style="margin-top: 8px">
<a-col :span="12">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">生于</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="128"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.BirthText"
style="border-radius: 0"
/>
</a-col>
<a-col :flex="'none'">
<a-date-picker
:disabled="!isEdit"
v-model:value="birthDate"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
<a-col :span="12">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">离世</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="128"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.DeathText"
style="border-radius: 0"
/>
</a-col>
<a-col :flex="'none'">
<a-date-picker
:disabled="!isEdit"
v-model:value="deathDate"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
</a-row>
<a-row :gutter="[8, 8]" style="margin-top: 8px">
<a-col :span="12">
<a-row :wrap="false" align="top" :gutter="0">
<a-col :flex="'80px'">
<div class="field-tag-textarea">简介</div>
</a-col>
<a-col :flex="'auto'">
<a-textarea
:auto-size="{ minRows: 10, maxRows: 15 }"
:maxlength="3000"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.Introduction"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
<a-col :span="6">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">公开</div>
</a-col>
<a-col :flex="'auto'">
<a-select
v-model:value="data.FamilyPerson.Privacy"
:disabled="!isEdit"
style="width: 100%"
>
<a-select-option
v-for="o in yesNoPolicy"
:value="o.BindValue"
>{{ o.DisplayName }}</a-select-option
>
</a-select>
</a-col>
</a-row>
</a-col>
<a-col :span="6">
<a-row :wrap="false" align="middle">
<a-col :flex="'80px'">
<div class="field-tag">状态</div>
</a-col>
<a-col :flex="'auto'">
<a-select
v-model:value="data.FamilyPerson.LiveStatus"
:disabled="!isEdit"
style="width: 100%"
>
<a-select-option
v-for="o in yesNoPolicy"
:value="o.BindValue"
>{{ o.DisplayName }}</a-select-option
>
</a-select>
</a-col>
</a-row>
</a-col>
</a-row>
<a-row :gutter="[8, 8]" style="margin-top: 8px">
<a-col :span="6">
<a-button
block
type="primary"
ghost
:disabled="!isEdit"
@click.prevent="
data.FamilyPerson.IsBindUser =
data.FamilyPerson.IsBindUser === 'Y' ? 'N' : 'Y'
"
>
{{
data.FamilyPerson.IsBindUser === "Y" ? "已绑定" : "绑定用户"
}}
</a-button>
</a-col>
<a-col :span="6">
<a-row :wrap="false" align="middle">
<a-col :flex="'70px'">
<div class="field-tag">绑定用户ID</div>
</a-col>
<a-col :flex="'auto'">
<a-input
:maxlength="50"
:readonly="!isEdit"
v-model:value="data.FamilyPerson.BindUserId"
style="border-radius: 0 2px 2px 0"
/>
</a-col>
</a-row>
</a-col>
</a-row>
</a-col>
</a-row>
<a-divider
orientation="left"
orientation-margin="0px"
dashed
style="background-color: silver"
>
</a-divider>
</div>
<div class="page-footer">
<div style="">
<a-row :gutter="24">
<a-col :lg="24" :sm="24">
<div>
<a-row v-if="isEdit" :gutter="[16, 8]">
<a-space>
<a-popconfirm
v-if="data.FamilyPerson.CreatedBy"
title="确定是否取消修改?"
ok-text="是"
cancel-text="否"
@confirm="onCancel"
>
<a-button type="primary" ghost :icon="h(UndoOutlined)"
>取消</a-button
>
</a-popconfirm>
<a-button
v-if="!data.FamilyPerson.CreatedBy"
type="primary"
ghost
:icon="h(UndoOutlined)"
@click.prevent="onCancel"
>取消</a-button
>
<a-button
type="primary"
ghost
:icon="h(SaveOutlined)"
@click.prevent="onSave"
>保存</a-button
>
</a-space>
</a-row>
<a-row v-else :gutter="[16, 8]">
<a-space>
<a-button
type="primary"
v-if="canAmend"
:icon="h(EditOutlined)"
:disabled="!hasData"
@click.prevent="onAmend"
>修改</a-button
>
<a-popconfirm
:disabled="!hasData"
title="确定是否删除这条记录?"
ok-text="是"
cancel-text="否"
@confirm="onDelete"
>
<a-button
type="primary"
danger
:icon="h(DeleteOutlined)"
:disabled="!hasData"
v-if="canDelete"
>删除</a-button
>
</a-popconfirm>
</a-space>
</a-row>
</div>
</a-col>
</a-row>
</div>
</div>
</div>
<dragModal
v-model:open="modelOption.isModalPermission"
width="80%"
title="人员查询"
:mask-closable="false"
:fullscreen="width < 640"
@ok="handleSelect"
>
<FamilyPersonEnquiry
ref="enquiry"
@on-row-dblclick="handleSelect"
></FamilyPersonEnquiry>
</dragModal>
</a-spin>
</template>
<script lang="ts" setup>
import { ref, unref, onBeforeMount, inject, provide, h, onMounted } from "vue";
import { notification } from "ant-design-vue";
import { useRoute, useRouter } from "vue-router";
import {
DeleteOutlined,
EditOutlined,
ClearOutlined,
FileSearchOutlined,
FileAddOutlined,
UndoOutlined,
SaveOutlined,
PlusSquareTwoTone,
DownSquareTwoTone,
UpSquareTwoTone,
UserOutlined,
CloseOutlined,
} from "@ant-design/icons-vue";
import {
PermissionContext,
usePermissionContext,
} from "@/hooks/PermissionContext";
import dragModal from "@/components/basic/DragModal.vue";
import FamilyPersonEnquiry from "./FamilyPersonEnquiry.vue";
import { FamilyPersonData } from "@/data/auto/familyPersonData";
import FamilyPersonService from "@/service/master/familyPersonService";
import { useYesNoPolicy } from "@/hooks/useYesNoPolicy";
import { SelectOption } from "@/data/auto/selectOption";
import { useWindowSize } from "@vueuse/core";
import { FamilyPersonEntity } from "@/data/auto/familyPersonEntity";
import { BindOption } from "@/data/custom/bindOption";
import { nextTick } from "process";
import dayjs from "dayjs";
import type { Dayjs } from "dayjs";
import {
operationToEnquiryMethodKey,
OperationToEnquiryModelOption,
} from "@/injections";
const loading = ref<boolean>(false);
const { width, height } = useWindowSize();
const route = useRoute();
const router = useRouter();
const personId = ref<string>(route.params.id as string);
const mode = ref<string>(route.params.mode as string);
const isEdit = ref<boolean>(false);
const canAmend = ref<boolean>(false);
const canDelete = ref<boolean>(false);
const canCreate = ref<boolean>(false);
const showModalType = ref<number>(0);
const data = ref<FamilyPersonData>(new FamilyPersonData());
const hasData = ref<boolean>(false);
const yesNoPolicy = ref<SelectOption[]>([]);
const birthDate = ref<Dayjs | null>(null);
const deathDate = ref<Dayjs | null>(null);
const resultH = ref("");
const oldPersonId = ref<string>(personId.value);
interface FamilyPersonEnquiry {
keys: FamilyPersonEntity[];
setBindOptionValue(
bindValue: string,
value: string,
value1: string,
values: string[],
): void;
}
const enquiry = ref<FamilyPersonEnquiry | null>(null);
//================provide================
const modelOption = ref<OperationToEnquiryModelOption>(
new OperationToEnquiryModelOption(),
);
modelOption.value.isModalPermission = false;
modelOption.value.selectionTypePermission = "radio";
modelOption.value.initBindOptionsPermission = [];
const getOperationToEnquiryModelOption = () => {
return modelOption.value;
};
provide(operationToEnquiryMethodKey, {
getOperationToEnquiryModelOption,
});
//================provide================
onBeforeMount(async () => {
yesNoPolicy.value = await useYesNoPolicy().getSelectOption(false);
});
onMounted(() => {
let pContext: PermissionContext = usePermissionContext();
canAmend.value = pContext.canAmend(route.meta.permission as string);
canDelete.value = pContext.canDelete(route.meta.permission as string);
canCreate.value = pContext.canCreate(route.meta.permission as string);
computResultTableHeight();
if (personId.value && personId.value.trim().length > 0) {
onSearch();
}
if (mode.value && mode.value == "create") {
onCreate();
isEdit.value = true;
} else if (mode.value && mode.value == "amend") {
isEdit.value = true;
}
});
const computResultTableHeight = () => {
if (width.value > 640) {
resultH.value = "calc(100vh - 220px )";
} else {
resultH.value = "calc(100vh - 250px )";
}
};
const onModalSearch = () => {
modelOption.value.isModalPermission = true;
modelOption.value.selectionTypePermission = "radio";
showModalType.value = 0;
};
const onSearch = async () => {
if (!personId.value || personId.value.trim().length == 0) {
notification.info({
message: "系统提示:",
description: "请输入族员编号",
placement: "topRight",
});
hasData.value = false;
return;
}
data.value = await FamilyPersonService.FindByKey(personId.value.trim());
if (data.value.FamilyPerson == null) {
notification.info({
message: "系统提示:",
description: "记录不存在",
placement: "topRight",
});
data.value = new FamilyPersonData();
hasData.value = false;
return;
}
birthDate.value = data.value.FamilyPerson.BirthDate
? dayjs(data.value.FamilyPerson.BirthDate)
: null;
deathDate.value = data.value.FamilyPerson.DeathDate
? dayjs(data.value.FamilyPerson.DeathDate)
: null;
hasData.value = true;
};
const onReset = () => {
data.value = new FamilyPersonData();
personId.value = "";
birthDate.value = null;
deathDate.value = null;
hasData.value = false;
};
const onCreate = () => {
onReset();
isEdit.value = true;
};
const onAmend = () => {
isEdit.value = true;
oldPersonId.value = unref(data.value.FamilyPerson.PersonId);
};
const onSave = async () => {
try {
loading.value = true;
data.value.FamilyPerson.BirthDate = birthDate.value
? birthDate.value.toDate()
: null!;
data.value.FamilyPerson.DeathDate = deathDate.value
? deathDate.value.toDate()
: null!;
if (data.value.FamilyPerson?.CreatedBy)
data.value = await FamilyPersonService.Update(data.value);
else {
data.value = await FamilyPersonService.Insert(data.value);
}
personId.value = data.value.FamilyPerson.PersonId;
loading.value = false;
hasData.value = true;
isEdit.value = false;
} catch (error) {
loading.value = false;
}
};
const onCancel = () => {
onReset();
if (oldPersonId.value && oldPersonId.value.length > 0) {
personId.value = oldPersonId.value;
onSearch();
}
isEdit.value = false;
};
const onDelete = async () => {
try {
loading.value = true;
await FamilyPersonService.Delete(data.value.FamilyPerson.PersonId);
onReset();
loading.value = false;
} catch (error) {
loading.value = false;
console.log(error);
}
};
const handleSelect = async () => {
if (enquiry.value) {
if (enquiry.value.keys.length > 0) {
//查询弹出
if (showModalType.value == 0) {
personId.value = enquiry.value.keys[0].PersonId;
onSearch();
}
modelOption.value.isModalPermission = false;
} else {
notification.info({
message: "系统提示:",
description: "请选择一条记录",
placement: "topRight",
});
}
}
};
</script>
<style lang="less">
.page-container {
margin-right: 0px;
}
.page-header {
padding-top: 4px;
padding-bottom: 8px;
padding-right: 12px;
border-bottom: 1px solid silver;
margin-left: 6px;
}
.page-header .ant-input-search .ant-input {
border-radius: 0;
border-left: 0;
text-align: left;
}
.page-main {
overflow-y: auto;
padding-top: 24px;
padding-bottom: 4px;
padding-right: 32px;
padding-left: 12px;
}
.page-footer {
z-index: 999;
padding-left: 8px;
padding-right: 18px;
padding-top: 8px;
width: 100%;
position: fixed;
bottom: 8px;
border-top: 1px solid silver;
}
.field-tag {
background: #f5f5f5;
color: #666;
font-size: 12px;
text-align: center;
border: 1px solid #d9d9d9;
border-right: none;
border-radius: 2px 0 0 2px;
height: 32px;
line-height: 30px;
padding: 0 4px;
white-space: nowrap;
overflow: hidden;
}
.field-tag-textarea {
background: #f5f5f5;
color: #666;
font-size: 12px;
text-align: right;
border: 1px solid #d9d9d9;
border-right: none;
border-radius: 2px 0 0 2px;
box-sizing: border-box;
min-height: 32px;
max-height: 32px; /* 固定标签高度,只显示顶部一小块 */
display: flex;
align-items: flex-start;
padding: 8px 10px 0 10px;
white-space: nowrap;
overflow: hidden;
}
.side-label {
color: #666;
font-size: 12px;
text-align: center;
line-height: 32px;
padding-right: 6px;
white-space: nowrap;
}
.avatar-change-mask {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 120px;
background: rgba(0, 0, 0, 0.5);
color: #fff;
font-size: 12px;
text-align: center;
line-height: 24px;
}
.avatar-close {
position: absolute;
top: -8px;
right: calc(50% - 68px);
}
</style>
@@ -592,6 +592,7 @@ const upSon = (index: number) => {
const item = data.value.SecurityFunctionMapSons.splice(index, 1)[0];
data.value.SecurityFunctionMapSons.splice(index - 1, 0, item);
selectedSonIndex.value = index - 1;
refreshSonOrderby();
}
};
@@ -600,9 +601,17 @@ const downSon = (index: number) => {
const item = data.value.SecurityFunctionMapSons.splice(index, 1)[0];
data.value.SecurityFunctionMapSons.splice(index + 1, 0, item);
selectedSonIndex.value = index + 1;
refreshSonOrderby();
}
};
const refreshSonOrderby = () => {
data.value.SecurityFunctionMapSons.forEach((item, idx) => {
item.Orderby = idx + 1; // orderby从1开始,也可以用 idx
});
};
const deleteSon = (key: string) => {
data.value.SecurityFunctionMapSons = data.value.SecurityFunctionMapSons.filter(item => item.SonFunctionName !== key);
};