ow
This commit is contained in:
1 parent
2b963e4bfe
commit
501c92d87c
13 files changed
+2374
-322
No files matched your search
@@ -21,7 +21,7 @@
|
||||
@click="onDownload"></a-button>
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
<a-col :span="2" v-if="!isDirectorySelected && isSingleItem">
|
||||
<a-col :span="2" v-if="!isDirectorySelected && (isSingleItem || isSameMediaTypeSelected)">
|
||||
<a-tooltip title="共享">
|
||||
<a-button class="nav-btn" :icon="h(ShareAltOutlined)" @click="onShare"></a-button>
|
||||
</a-tooltip>
|
||||
@@ -46,7 +46,7 @@
|
||||
</a-popconfirm>
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
<a-col :span="2" v-if="!isDirectorySelected && isSingleItem">
|
||||
<a-col :span="2" v-if="!isDirectorySelected && (isSingleItem || isSameMediaTypeSelected)">
|
||||
<a-tooltip :title="publishTooltip">
|
||||
<a-button class="nav-btn" :icon="h(NotificationOutlined)" @click="onPublish"></a-button>
|
||||
</a-tooltip>
|
||||
@@ -109,6 +109,7 @@ import {
|
||||
} from "@ant-design/icons-vue";
|
||||
import { DmsFileUploadViewDisplayData } from "@/data/auto/dmsFileUploadViewDisplayData";
|
||||
import FileUpload from "@/components/basic/FileUpload.vue";
|
||||
import fileTypePolicy from "@/policy/custom/fileTypePolicy";
|
||||
|
||||
const props = defineProps<{
|
||||
selectedItems: DmsFileUploadViewDisplayData[];
|
||||
@@ -180,6 +181,29 @@ const isSingleAudio = computed(() => {
|
||||
return isSingleItem.value && isAudio(selectedOrderKey.value);
|
||||
});
|
||||
|
||||
/**
|
||||
* 识别媒体类型,兼顾本地扩展名判断与全局 fileTypePolicy,
|
||||
* 保证与发布时的类型判断一致。
|
||||
*/
|
||||
const mediaKind = (key: string): "image" | "video" | "audio" | "" => {
|
||||
if (isImage(key) || fileTypePolicy.isImage(key)) return "image";
|
||||
if (isVideo(key) || fileTypePolicy.isVideo(key)) return "video";
|
||||
if (isAudio(key) || fileTypePolicy.isAudio(key)) return "audio";
|
||||
return "";
|
||||
};
|
||||
|
||||
/**
|
||||
* 多选时选中项是否全部属于同一媒体类型(图片/视频/音频)。
|
||||
* 满足时允许批量共享与发布。
|
||||
*/
|
||||
const isSameMediaTypeSelected = computed(() => {
|
||||
const items = props.selectedItems;
|
||||
if (items.length <= 1) return false;
|
||||
const kinds = items.map((x) => mediaKind(x.OrderKey || x.OrgFileName || ""));
|
||||
const kind = kinds[0];
|
||||
return kind !== "" && kinds.every((k) => k === kind);
|
||||
});
|
||||
|
||||
const publishTooltip = computed(() => {
|
||||
if (isImage(selectedOrderKey.value)) return "发布相册";
|
||||
if (isVideo(selectedOrderKey.value)) return "发布影视专辑";
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -470,14 +470,4 @@ const { run, current, pageSize } = usePagination(queryData, {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
</style>
|
||||
@@ -402,6 +402,11 @@ onMounted(() => {
|
||||
resizeObserver = new ResizeObserver(() => {
|
||||
void relayout();
|
||||
});
|
||||
// 弹窗形态未显式传 is-modal 时自动识别:位于 .ant-modal-body 内即按弹窗布局
|
||||
// 否则分页栏会按「页面模式」position:fixed 钉在视口底部,压住弹窗/页面 footer
|
||||
if (!isModal.value && searchRef.value?.closest(".ant-modal-body")) {
|
||||
isModal.value = true;
|
||||
}
|
||||
rebindObserver();
|
||||
void setupModalLayout();
|
||||
});
|
||||
@@ -647,17 +652,6 @@ const { run } = usePagination(queryData, {
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
/* ==================== 结果区:卡片列表 ==================== */
|
||||
.sc-list {
|
||||
overflow-x: hidden;
|
||||
|
||||
@@ -509,11 +509,10 @@ watch(current, async (t) => {
|
||||
}
|
||||
});
|
||||
|
||||
// 曲目从无到有时默认展开播放列表(对齐原 APlayer showList 的行为)
|
||||
// 播放列表默认收起,由用户点击列表按钮展开
|
||||
watch(
|
||||
() => tracks.value.length,
|
||||
(n, o) => {
|
||||
if (n > 0 && !o) showList.value = true;
|
||||
(n) => {
|
||||
if (index.value >= n) index.value = 0;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
<button class="ftc-zoom-btn" title="放大" @click="zoomBy(ZOOM_STEP)">+</button>
|
||||
<button class="ftc-zoom-btn" title="缩小" @click="zoomBy(-ZOOM_STEP)">-</button>
|
||||
<button class="ftc-zoom-btn" title="适应画布" @click="fitGraph">⤢</button>
|
||||
<button class="ftc-zoom-btn" :title="dirTitle" @click="toggleDirection">{{ layoutDir === "h" ? "⇄" : "⇅" }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<a-empty v-else description="暂无当前族员数据" />
|
||||
@@ -162,8 +161,8 @@ const describePerson = (personId: string): any => {
|
||||
return null;
|
||||
};
|
||||
|
||||
/** 构建横向布局图数据:深度向右排列,本人默认展开孩子,点击孩子可递归加载子树 */
|
||||
const buildHorizontalData = () => {
|
||||
/** 构建图数据:树形竖向排列,本人默认展开孩子,点击孩子可递归加载子树 */
|
||||
const buildGraphData = () => {
|
||||
const nodes: any[] = [];
|
||||
const edges: any[] = [];
|
||||
const root = rootPersonId.value;
|
||||
@@ -264,166 +263,6 @@ const buildHorizontalData = () => {
|
||||
return { nodes, edges };
|
||||
};
|
||||
|
||||
/**
|
||||
* 构建纵向布局图数据:每层一行,孩子横向铺开并居中于父节点,配偶与本人并排。
|
||||
* 仅改变排列方式,卡片内容与样式不变。
|
||||
*/
|
||||
const buildVerticalData = () => {
|
||||
const nodes: any[] = [];
|
||||
const edges: any[] = [];
|
||||
const root = rootPersonId.value;
|
||||
if (!root) return { nodes, edges };
|
||||
|
||||
const CARD_WIDTH = 360;
|
||||
const CARD_HEIGHT = 220;
|
||||
const SPOUSE_GAP = 30; // 配偶紧贴本人
|
||||
const H_GAP = 60; // 兄弟子树水平间隔
|
||||
const Y_STEP = CARD_HEIGHT + 40; // 上下层间隔
|
||||
|
||||
/** 布局单元:本人 + 同行配偶,孩子排在下一行 */
|
||||
interface VUnit {
|
||||
personId: string;
|
||||
desc: any;
|
||||
/** 需要展开的孩子(未展开则为空) */
|
||||
children: any[];
|
||||
spouses: any[];
|
||||
}
|
||||
|
||||
const unitOf = (personId: string): VUnit | null => {
|
||||
const desc = describePerson(personId);
|
||||
if (!desc) return null;
|
||||
return {
|
||||
personId,
|
||||
desc,
|
||||
children: expandedIds.value.has(personId) ? desc.children : [],
|
||||
spouses: desc.spouses ?? [],
|
||||
};
|
||||
};
|
||||
|
||||
/** 本人 + 配偶并排一行的总宽 */
|
||||
const unitWidth = (u: VUnit) => CARD_WIDTH + u.spouses.length * (CARD_WIDTH + SPOUSE_GAP);
|
||||
|
||||
const measuring = new Set<string>();
|
||||
/** 子树宽度 = max(本行宽度, 孩子一行总宽) */
|
||||
const measure = (u: VUnit): number => {
|
||||
if (measuring.has(u.personId)) return CARD_WIDTH;
|
||||
measuring.add(u.personId);
|
||||
let childrenWidth = 0;
|
||||
u.children.forEach((c: any, i: number) => {
|
||||
const child = unitOf(c.ChildrenId);
|
||||
childrenWidth += (child ? measure(child) : CARD_WIDTH) + (i > 0 ? H_GAP : 0);
|
||||
});
|
||||
measuring.delete(u.personId);
|
||||
return Math.max(unitWidth(u), childrenWidth);
|
||||
};
|
||||
|
||||
const place = (u: VUnit, depth: number, xLeft: number) => {
|
||||
if (depth > 40) return;
|
||||
const width = measure(u);
|
||||
const p = u.desc.person;
|
||||
const isRoot = u.personId === root;
|
||||
const y = depth * Y_STEP;
|
||||
// 本人 + 配偶并排在本行,整体居中于本子树
|
||||
const unitLeft = xLeft + (width - unitWidth(u)) / 2;
|
||||
const selfX = unitLeft + CARD_WIDTH / 2;
|
||||
// 父子连线的横向干线高度:本行底部与下一行顶部之间的中线
|
||||
const railY = (depth + 0.5) * Y_STEP;
|
||||
|
||||
// 孩子排在下一行,整体居中于本子树
|
||||
const children = u.children.map((c: any) => ({ c, unit: unitOf(c.ChildrenId) }));
|
||||
const childWidths = children.map((x) => (x.unit ? measure(x.unit) : CARD_WIDTH));
|
||||
const childrenWidth =
|
||||
childWidths.reduce((a, b) => a + b, 0) + H_GAP * Math.max(0, childWidths.length - 1);
|
||||
let cx = xLeft + (width - childrenWidth) / 2;
|
||||
children.forEach(({ c, unit }, i) => {
|
||||
const childX = cx + CARD_WIDTH / 2;
|
||||
if (unit) {
|
||||
place(unit, depth + 1, cx);
|
||||
} else {
|
||||
nodes.push({
|
||||
id: "n-" + c.ChildrenId,
|
||||
style: { x: childX, y: (depth + 1) * Y_STEP },
|
||||
data: {
|
||||
kind: "child",
|
||||
personId: c.ChildrenId,
|
||||
tag: c.RelationType ? relationLabel(c.RelationType) : "子女",
|
||||
name: (c.ChildrenSurname ?? "") + (c.ChildrenGenealogyName ?? ""),
|
||||
commonName: c.ChildrenCommonName,
|
||||
gender: c.Gender,
|
||||
avatarUrl: avatarMap.value[c.ChildrenId] ?? "",
|
||||
hasChildren: false,
|
||||
expanded: false,
|
||||
loaded: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
edges.push({
|
||||
source: "n-" + u.personId,
|
||||
target: "n-" + c.ChildrenId,
|
||||
// 折点:父卡底部中心下方 → 横向干线 → 子卡顶部中心上方
|
||||
data: { kind: "parent", points: [[selfX, railY], [childX, railY]] },
|
||||
});
|
||||
cx += childWidths[i] + H_GAP;
|
||||
});
|
||||
|
||||
nodes.push({
|
||||
id: "n-" + u.personId,
|
||||
style: { x: selfX, y },
|
||||
data: {
|
||||
kind: isRoot ? "self" : "member",
|
||||
personId: u.personId,
|
||||
tag: isRoot ? "本人" : "族员",
|
||||
name: (p?.Surname ?? "") + (p?.GenealogyName ?? ""),
|
||||
commonName: p?.CommonName,
|
||||
gender: p?.Gender,
|
||||
avatarUrl: p?.AvatarUrl ?? avatarMap.value[u.personId] ?? "",
|
||||
hasChildren: u.desc.children.length > 0,
|
||||
expanded: expandedIds.value.has(u.personId),
|
||||
loaded: u.desc.loaded,
|
||||
},
|
||||
});
|
||||
|
||||
u.spouses.forEach((s: any, i: number) => {
|
||||
const sid = "s-" + u.personId + "-" + i;
|
||||
nodes.push({
|
||||
id: sid,
|
||||
style: { x: selfX + (i + 1) * (CARD_WIDTH + SPOUSE_GAP), y },
|
||||
data: {
|
||||
kind: "spouse",
|
||||
personId: s.SpouseId,
|
||||
tag: "配偶",
|
||||
name: (s.SpouseSurname ?? "") + (s.SpouseGenealogyName ?? ""),
|
||||
gender: s.Gender,
|
||||
avatarUrl: avatarMap.value[s.SpouseId] ?? "",
|
||||
loaded: true,
|
||||
hasChildren: false,
|
||||
expanded: false,
|
||||
},
|
||||
});
|
||||
edges.push({ source: "n-" + u.personId, target: sid, data: { kind: "spouse" } });
|
||||
});
|
||||
};
|
||||
|
||||
const rootUnit = unitOf(root);
|
||||
if (rootUnit) place(rootUnit, 0, 0);
|
||||
return { nodes, edges };
|
||||
};
|
||||
|
||||
/** 布局方向:h=横向(深度向右),v=纵向(深度向下) */
|
||||
const layoutDir = ref<"h" | "v">("h");
|
||||
|
||||
/** 按当前方向构建图数据 */
|
||||
const buildGraphData = () => (layoutDir.value === "v" ? buildVerticalData() : buildHorizontalData());
|
||||
|
||||
/** 方向按钮提示(按钮上的箭头表示当前方向) */
|
||||
const dirTitle = computed(() => (layoutDir.value === "h" ? "当前横向排列,点击切换为纵向" : "当前纵向排列,点击切换为横向"));
|
||||
|
||||
/** 横向 / 纵向排列切换 */
|
||||
const toggleDirection = () => {
|
||||
layoutDir.value = layoutDir.value === "h" ? "v" : "h";
|
||||
initGraph();
|
||||
};
|
||||
|
||||
const containerRef = ref<HTMLDivElement>();
|
||||
let graph: any = null;
|
||||
|
||||
@@ -440,13 +279,12 @@ const zoomBy = (delta: number) => {
|
||||
graph.zoomTo(Math.min(Math.max(next, MIN_ZOOM), 10));
|
||||
};
|
||||
|
||||
/** 还原为适配整个画布(缩到最小比例以下的需重新居中,否则整图可能落在视口之外) */
|
||||
/** 还原为适配整个画布 */
|
||||
const fitGraph = async () => {
|
||||
if (!graph) return;
|
||||
await graph.fitView();
|
||||
if (graph.getZoom() < MIN_ZOOM) {
|
||||
await graph.zoomTo(MIN_ZOOM);
|
||||
if (typeof graph.fitCenter === "function") await graph.fitCenter();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -485,22 +323,13 @@ const initGraph = async () => {
|
||||
},
|
||||
edge: {
|
||||
type: (d: any) => (d?.data?.kind === "spouse" ? "line" : "polyline"),
|
||||
style: (d: any) => {
|
||||
const isSpouse = d?.data?.kind === "spouse";
|
||||
return {
|
||||
// 夫妻关系=紫色虚线,亲子关系=灰色实线
|
||||
stroke: isSpouse ? "#722ed1" : "#a0a0a0",
|
||||
lineWidth: 1.5,
|
||||
lineDash: isSpouse ? [4, 4] : undefined,
|
||||
// 纵向布局:用布局阶段算好的折点(父卡底部中心 → 横向干线 → 子卡顶部中心)
|
||||
// 横向布局:沿用正交路由
|
||||
...(isSpouse
|
||||
? {}
|
||||
: layoutDir.value === "v"
|
||||
? { controlPoints: d?.data?.points ?? [] }
|
||||
: { router: { type: "orth" } }),
|
||||
};
|
||||
},
|
||||
style: (d: any) => ({
|
||||
// 夫妻关系=紫色虚线,亲子关系=灰色实线
|
||||
stroke: d?.data?.kind === "spouse" ? "#722ed1" : "#a0a0a0",
|
||||
lineWidth: 1.5,
|
||||
lineDash: d?.data?.kind === "spouse" ? [4, 4] : undefined,
|
||||
router: d?.data?.kind === "spouse" ? undefined : { type: "orth" },
|
||||
}),
|
||||
},
|
||||
behaviors: ["drag-canvas", "zoom-canvas"],
|
||||
});
|
||||
@@ -523,10 +352,9 @@ const initGraph = async () => {
|
||||
}
|
||||
});
|
||||
await graph.render();
|
||||
// 世代过多导致 fitView 缩得太小时,放大到最小显示比例并重新居中,保证卡片可见可读
|
||||
// 世代过多导致 fitView 缩得太小时,放大到最小显示比例,保证卡片可读
|
||||
if (graph.getZoom() < MIN_ZOOM) {
|
||||
await graph.zoomTo(MIN_ZOOM);
|
||||
if (typeof graph.fitCenter === "function") await graph.fitCenter();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
/* 底部固定操作栏 */
|
||||
/* 底部固定操作栏:全站统一不透明底色,避免下方内容透出 */
|
||||
.page-footer {
|
||||
z-index: 999;
|
||||
padding-left: 8px;
|
||||
@@ -49,6 +49,7 @@
|
||||
position: fixed;
|
||||
bottom: 8px;
|
||||
border-top: 1px solid silver;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
/* 表格行高亮:成员/子级选中等场景,与 FamilyTreeOperation 保持一致 */
|
||||
|
||||
@@ -96,7 +96,7 @@ interface BreadcrumbItem {
|
||||
const { width } = useWindowSize();
|
||||
|
||||
const showModalShare = ref<boolean>(false);
|
||||
const sharedFileRecord = ref<DmsFileUploadViewDisplayData | null>(null);
|
||||
const sharedFileRecords = ref<DmsFileUploadViewDisplayData[]>([]);
|
||||
const showModalPublishPhoto = ref<boolean>(false);
|
||||
const showModalPublishAudio = ref<boolean>(false);
|
||||
const showModalPublishVideo = ref<boolean>(false);
|
||||
@@ -487,8 +487,8 @@ const onPublishVideo = async () => {
|
||||
showModalPublishVideo.value = true;
|
||||
};
|
||||
|
||||
const onShareFamily = async (record: Record<string, any>) => {
|
||||
sharedFileRecord.value = toUploadView(record);
|
||||
const onShareFamily = async (records: DmsFileUploadViewDisplayData[]) => {
|
||||
sharedFileRecords.value = records.map((r) => toUploadView(r));
|
||||
showModalShare.value = true;
|
||||
};
|
||||
|
||||
@@ -801,18 +801,20 @@ const onReload = async () => {
|
||||
|
||||
const handleSelectShare = async () => {
|
||||
if (shareEnquiry.value) {
|
||||
if (shareEnquiry.value.keys.length > 0) {
|
||||
if (shareEnquiry.value.keys.length > 0 && sharedFileRecords.value.length > 0) {
|
||||
let fgps: FamilyGroupFolderEntity[] = [];
|
||||
let fgp: FamilyGroupFolderEntity;
|
||||
shareEnquiry.value.keys.forEach((element) => {
|
||||
fgp = new FamilyGroupFolderEntity();
|
||||
fgp.FamilyName = element.FamilyName;
|
||||
fgp.TopFolderName = element.TopFolderName;
|
||||
fgp.FolderName = sharedFileRecord.value?.FileName ?? "";
|
||||
fgp.FolderLocalName = sharedFileRecord.value?.OrgFileName ?? "";
|
||||
fgp.FilePath = element.FolderName;
|
||||
// 每个选中的目标共享目录 × 每个待共享的文件,各生成一条群组文件夹记录
|
||||
shareEnquiry.value.keys.forEach((target) => {
|
||||
sharedFileRecords.value.forEach((record) => {
|
||||
let fgp: FamilyGroupFolderEntity = new FamilyGroupFolderEntity();
|
||||
fgp.FamilyName = target.FamilyName;
|
||||
fgp.TopFolderName = target.TopFolderName;
|
||||
fgp.FolderName = record.FileName ?? "";
|
||||
fgp.FolderLocalName = record.OrgFileName ?? "";
|
||||
fgp.FilePath = target.FolderName;
|
||||
|
||||
fgps.push(fgp);
|
||||
fgps.push(fgp);
|
||||
});
|
||||
});
|
||||
|
||||
await dmsFileService.CreateFamilyGroupFolder(fgps);
|
||||
@@ -997,28 +999,26 @@ const onDownloadSelected = (items: DmsFileUploadViewDisplayData[]) => {
|
||||
};
|
||||
|
||||
const onShareSelected = (items: DmsFileUploadViewDisplayData[]) => {
|
||||
if (items.length > 0) {
|
||||
onShareFamily(items[0]);
|
||||
const files = items.filter((item) => item.IsDirectoryIndc != "Y");
|
||||
if (files.length > 0) {
|
||||
onShareFamily(files);
|
||||
}
|
||||
};
|
||||
|
||||
const onPublishSelected = (items: DmsFileUploadViewDisplayData[]) => {
|
||||
if (items.length === 0) return;
|
||||
const first = items[0];
|
||||
if (
|
||||
fileTypePolicy.isImage(first.OrderKey) ||
|
||||
fileTypePolicy.isImage(first.OrgFileName)
|
||||
) {
|
||||
const files = items.filter((item) => item.IsDirectoryIndc != "Y");
|
||||
if (files.length === 0) return;
|
||||
const first = files[0];
|
||||
const mediaKey = (item: DmsFileUploadViewDisplayData) =>
|
||||
item.OrderKey || item.OrgFileName || "";
|
||||
if (fileTypePolicy.isImage(mediaKey(first))) {
|
||||
keys.value = files.filter((item) => fileTypePolicy.isImage(mediaKey(item)));
|
||||
onPublishPhoto();
|
||||
} else if (
|
||||
fileTypePolicy.isVideo(first.OrderKey) ||
|
||||
fileTypePolicy.isVideo(first.OrgFileName)
|
||||
) {
|
||||
} else if (fileTypePolicy.isVideo(mediaKey(first))) {
|
||||
keys.value = files.filter((item) => fileTypePolicy.isVideo(mediaKey(item)));
|
||||
onPublishVideo();
|
||||
} else if (
|
||||
fileTypePolicy.isAudio(first.OrderKey) ||
|
||||
fileTypePolicy.isAudio(first.OrgFileName)
|
||||
) {
|
||||
} else if (fileTypePolicy.isAudio(mediaKey(first))) {
|
||||
keys.value = files.filter((item) => fileTypePolicy.isAudio(mediaKey(item)));
|
||||
onPublishAudio();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
@on-select-change="onSelectChange">
|
||||
<template #cardTitle="{ record }">
|
||||
{{ record.FamilyLocalName }}
|
||||
<span style="margin-left: 6px"><FolderOutlined style="margin-right: 2px" />{{ getSharePath(record) }}</span>
|
||||
<span style="margin-left: 6px"><FolderOutlined style="margin-right: 2px; color: #faad14" />{{ getSharePath(record) }}</span>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.dataIndex === FamilyFolderFavoriteViewEntity.TOP_FOLDER_LOCAL_NAME">
|
||||
<FolderOutlined style="margin-right: 2px" /><span>{{ getSharePath(record) }}</span>
|
||||
<FolderOutlined style="margin-right: 2px; color: #faad14" /><span>{{ getSharePath(record) }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</searchCard>
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
@on-select-change="onSelectChange"
|
||||
>
|
||||
<template #control> </template>
|
||||
<template #cardTitle="{ record }">
|
||||
{{ record.FamilyLocalName }}
|
||||
<span style="margin-left: 6px"><FolderOutlined style="margin-right: 2px; color: #faad14" />{{ record.TopFolderLocalName + (record.TopFolderLocalName != record.FolderLocalName ? " > " + record.FolderLocalName : "") }}</span>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template
|
||||
v-if="
|
||||
@@ -22,7 +26,7 @@
|
||||
FamilyFolderFavoriteViewEntity.TOP_FOLDER_LOCAL_NAME
|
||||
"
|
||||
>
|
||||
<FolderOutlined /> {{ record.TopFolderLocalName +(record.TopFolderLocalName!=record.FolderLocalName?(' > '+record.FolderLocalName):'') }}
|
||||
<FolderOutlined style="margin-right: 2px; color: #faad14" /> {{ record.TopFolderLocalName +(record.TopFolderLocalName!=record.FolderLocalName?(' > '+record.FolderLocalName):'') }}
|
||||
</template>
|
||||
</template>
|
||||
<template #footer>
|
||||
|
||||
@@ -1355,17 +1355,6 @@ const onPersonQuickCreated = (person: FamilyPersonDisplayData) => {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.son-row-selected td {
|
||||
border-top: 2px solid #1677ff !important;
|
||||
border-bottom: 2px solid #1677ff !important;
|
||||
@@ -2038,13 +2027,11 @@ const onPersonQuickCreated = (person: FamilyPersonDisplayData) => {
|
||||
color: var(--fto-blue);
|
||||
}
|
||||
|
||||
/* ---------- 底部操作条:白色底 + 模糊,避免下方内容透出来 ---------- */
|
||||
/* ---------- 底部操作条:不透明白底,避免下方内容透出来 ---------- */
|
||||
.family-tree-op .page-footer {
|
||||
padding: 10px 16px;
|
||||
border-top: 1px solid var(--fto-line);
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
background: #ffffff;
|
||||
box-shadow: 0 -8px 24px -18px rgba(15, 23, 42, 0.5);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,15 @@
|
||||
<template>
|
||||
<div class="main-list" :style="{ height: (height - 150) + 'px' }">
|
||||
<a-row style="margin-left:-8px;margin-top:-8px;">
|
||||
<a-col :sm="24" :lg="24">
|
||||
<section class="vertical" :style='imgStyle'>
|
||||
<photo-provider>
|
||||
<photo-consumer v-for="(src, index) in images" :key="src.DetailId" :src="src.FileName">
|
||||
<img :src="src.PublishName" class="view-box">
|
||||
</photo-consumer>
|
||||
</photo-provider>
|
||||
|
||||
</section>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
<div class="main-page-footer">
|
||||
<a-pagination v-model:current="result.Page" v-model:page-size="result.Limit" :total="result.Total"
|
||||
@change="onPagination" :pageSizeOptions="pageSizeOptions"></a-pagination>
|
||||
<div class="pv-page">
|
||||
<PhotoSlidePlayer :items="slides" @close="onClose" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { watch, computed, inject, reactive, ref, VNode, h, unref, onMounted, onBeforeMount } from "vue";
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import PhotoSlidePlayer from "@/components/basic/PhotoSlidePlayer.vue";
|
||||
import type { PhotoSlideItem } from "@/components/basic/PhotoSlidePlayer.vue";
|
||||
import { PublishPhotoDetailEntity } from "@/data/auto/publishPhotoDetailEntity";
|
||||
import publishPhotoService from "@/service/master/publishPhotoService";
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import { bsInfo } from "@/utils/browser"
|
||||
import { DmsFileSearchCriteria } from "@/data/auto/dmsFileSearchCriteria";
|
||||
import { SearchFilter } from "@/data/auto/searchFilter";
|
||||
@@ -34,8 +20,6 @@ import dmsFileService from "@/service/master/dmsFileService";
|
||||
import { LeftCircleOutlined, RightCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { SearchCriteriaResultData } from "@/data/auto/searchCriteriaResultData";
|
||||
|
||||
const { width, height } = useWindowSize()
|
||||
|
||||
const images = ref<PublishPhotoDetailEntity[]>([]);
|
||||
|
||||
|
||||
@@ -53,14 +37,24 @@ const beforeChange = (from: number, to: number) => {
|
||||
}
|
||||
}
|
||||
|
||||
const imgStyle = computed(() => {
|
||||
if (!bsInfo.size.sm) {
|
||||
return ' object-fit: cover;width:100%; height:' + (height.value - 260) + 'px;'
|
||||
// 交给 PhotoSlidePlayer 播放:FileName 为缩略图,PublishName 为原图
|
||||
const slides = computed<PhotoSlideItem[]>(() =>
|
||||
images.value.map((item) => ({
|
||||
url: item.PublishName,
|
||||
thumb: item.FileName,
|
||||
name: (item.FileName || "").replace(/\.[^./\\]+$/, "") || undefined,
|
||||
}))
|
||||
);
|
||||
|
||||
// 收起播放器:返回来源页;没有历史时回首页
|
||||
const onClose = (() => {
|
||||
if (window.history.length > 1) {
|
||||
router.back();
|
||||
}
|
||||
else {
|
||||
return ' object-fit: cover;width:' + width.value + 'px; height:' + height.value + 'px;'
|
||||
router.push('/');
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const pagingParams = ref<PagingParams>({ pageSize: bsInfo.size.sm ? 30 : 100, page: 1 });
|
||||
const result = ref<SearchCriteriaResultData>(new SearchCriteriaResultData());
|
||||
@@ -95,7 +89,7 @@ const findData = async (params: number) => {
|
||||
try {
|
||||
let c: DmsFileSearchCriteria = new DmsFileSearchCriteria();
|
||||
c.page = params;
|
||||
c.limit = pagingParams.value.pageSize;
|
||||
c.limit =999999;// pagingParams.value.pageSize;
|
||||
let filter: SearchFilter;
|
||||
filter = new SearchFilter();
|
||||
filter.Column = DmsFileUploadViewDisplayData.DB_FILE_PATH;
|
||||
@@ -145,46 +139,18 @@ const showSizeChange = (async (current: number, size: number) => {
|
||||
|
||||
|
||||
<style lang="less" scoped>
|
||||
.vertical {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
/* 外层 .layout-content-view 自带 padding-top / padding-left(各 8px),
|
||||
这里用等量负外边距抵消、同时把宽高补回满格,让播放器铺满整个内容区,
|
||||
最外层不留任何间距 */
|
||||
.pv-page {
|
||||
width: calc(100% + 8px);
|
||||
height: calc(100% + 8px);
|
||||
margin: -8px 0 0 -8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.main-list {
|
||||
/* 设置高度 */
|
||||
overflow-y: auto;
|
||||
/* 显示纵向滚动条 */
|
||||
|
||||
background-color: bisque;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
padding-top: 8px;
|
||||
margin-right: 8px;
|
||||
background-image: url('/images/main/p3.png');
|
||||
background-size: cover;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.view-box {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin-right: 20px;
|
||||
margin-bottom: 20px;
|
||||
cursor: pointer;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.main-page-footer {
|
||||
background-color: aliceblue;
|
||||
z-index: 999;
|
||||
padding-left: 8px;
|
||||
padding-right: 18px;
|
||||
padding-top: 8px;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
border-top: 1px solid silver;
|
||||
/* 铺满后不再需要卡片圆角,否则四个角会露出留白 */
|
||||
.pv-page :deep(.psp) {
|
||||
border-radius: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -110,5 +110,9 @@ const onClose = (() => {
|
||||
<style scoped>
|
||||
.pa-page {
|
||||
width: 100%;
|
||||
/* 外层 .layout-content-view 只有 padding-left: 8px,这里补上右侧
|
||||
同样的间距,播放器才会左右居中而不是偏右贴边 */
|
||||
padding-right: 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user