This commit is contained in:
itcsx committed 2026-09-13 19:01:48 +08:00
1 parent 7f5bf3c68d
commit 816209fc35
37 files changed
+3236 -4515

No files matched your search

@@ -4,9 +4,9 @@
<Toolbar :path-stack="pathStack" :root-path="rootPath" :loading="loading" @go-top="onGoTop" @go-back="onGoBack"
@refresh="onReSearch" @search="onSearchByName" @breadcrumb-click="onBreadcrumbClick" />
<OperatorStationDelete :selected-items="keys" :upload-url="uploadUrl" :headers="headers" :can-create="canCreate"
<OperatorStationDelete :selected-items="keys" :can-create="canCreate"
:can-amend="canAmend" :can-delete="canDelete" :can-view="canView" @create-folder="onCreate"
@before-upload="beforeUpload" @download="onDownloadSelected" @share="onShareSelected"
@download="onDownloadSelected" @share="onShareSelected"
@publish="onPublishSelected" @view-preview="onViewPreviewSelected" @play-video="onUrlVideoPlayItem"
@play-audio="onPlayAudioItem" @pause-audio="onPauseAudio" @continue-audio="onContinueAudio"
@preview="onPreviewSelected" @rename="onAmendSelected" @move="onMoveSelected" @delete="onDeleteSelected"
@@ -76,11 +76,7 @@ import { SearchFilterTypePolicy } from "@/data/custom/searchFilterTypePolicy";
import { notification } from "ant-design-vue";
import { SearchCriteriaResultData } from "@/data/auto/searchCriteriaResultData";
import dmsFolder from "./DmsFolderModal.vue";
import { useLoginProfileStore } from "@/store/modules/loginProfile";
import type { UploadProps } from "ant-design-vue";
import { WebSitePolicy } from "@/policy/custom/webSitePolicy";
import { orderBy } from "lodash-es";
import axios from "axios";
import VideoPlay from "./PlayVideo.vue";
import { Howl } from "howler";
import { PublishVideoEntity } from "@/data/auto/publishVideoEntity";
@@ -88,7 +84,6 @@ import publishVideoService from "@/service/master/publishVideoService";
import { useWindowSize } from "@vueuse/core";
import fileTypePolicy from "@/policy/custom/fileTypePolicy";
import { PublishAudioEntity } from "@/data/auto/publishAudioEntity";
import { v4 as uuidv4 } from "uuid";
import { FamilyFolderFavoriteViewEntity } from "@/data/auto/familyFolderFavoriteViewEntity";
import { DmsFileEntity } from "@/data/auto/dmsFileEntity";
import { PublishData } from "@/data/auto/publishData";
@@ -122,22 +117,8 @@ const route = useRoute();
const router = useRouter();
const previousFilePath = ref<string>("");
const fileList = ref<UploadProps["fileList"]>([]);
const uploadUrl = ref<string>(WebSitePolicy.WebApiRoot + "DmsFile/UploadFile");
if (process.env.NODE_ENV === "development") {
uploadUrl.value = WebSitePolicy.WebApiRootDev + "/DmsFile/UploadFile";
} else {
uploadUrl.value = WebSitePolicy.WebApiRoot + "/DmsFile/UploadFile";
}
const currFilePath = ref<string>("");
const headers = ref({
Authorization: useLoginProfileStore().getToken(),
"Content-Type": "multipart/form-data",
FilePath: currFilePath.value,
});
const bindOptions = ref<BindOption[]>([]);
const datas = ref<DmsFileUploadDeleteLogViewDisplayData[]>([]);
const pagingParams = ref<PagingParams>({ pageSize: 999999, page: 1 });
@@ -288,97 +269,6 @@ onMounted(async () => {
onSearchByFilePath(rootPath.value);
});
const beforeUpload: UploadProps["beforeUpload"] = async (file) => {
if (!file || typeof file.size !== "number") {
return false;
}
loading.value = true;
fileList.value = [...(fileList.value || []), file];
const chunks = splitFile(file);
let bindOption: BindOption = bindOptions.value.filter(
(x) => x.BindValue == DmsFileUploadDeleteLogViewDisplayData.DB_FILE_PATH,
)[0];
currFilePath.value = headers.value.FilePath = bindOption.Value;
let i = 1;
let fileSize = file.size;
let uid: string = uuidv4();
spinTip.value = "上传中(1/" + chunks.length + ")...";
await uploadChunk(chunks, file.name, uid, i);
spinTip.value = "加载中...";
return false;
};
const uploadChunk = async (
chunks: any,
fileName: string,
uid: string,
index: number,
) => {
const formData = new FormData();
const fileExtension = fileName.split(".").pop();
formData.append(
fileName,
chunks[index - 1],
uid +
"-" +
index.toString() +
"-" +
chunks.length.toString() +
"." +
fileExtension,
);
if (process.env.NODE_ENV === "development") {
axios.defaults.baseURL = WebSitePolicy.WebApiRootDev;
axios.defaults.withCredentials = true;
} else {
axios.defaults.baseURL = WebSitePolicy.WebApiRoot;
}
try {
let response = await axios.create().request({
url: "/DmsFile/UploadFile",
method: "post",
data: formData,
headers: headers.value,
});
if (response.status == 200 && index < chunks.length) {
spinTip.value = "上传中(" + (index + 1) + "/" + chunks.length + ")...";
await uploadChunk(chunks, fileName, uid, index + 1);
} else if (index == chunks.length) {
let sec: number = 2500;
if (index > 25) sec = index * 100;
spinTip.value = "加载中...";
setTimeout(function () {
onReSearch();
}, sec);
}
} catch (error) {
loading.value = false;
spinTip.value = "加载中...";
}
};
const CHUNK_SIZE = 1024 * 1024;
const splitFile = (file: any) => {
const chunks: Blob[] = [];
if (!file || typeof file.size !== "number" || typeof file.slice !== "function") {
return chunks;
}
let start = 0;
while (start < file.size) {
const end = Math.min(start + CHUNK_SIZE, file.size);
const chunk = file.slice(start, end);
chunks.push(chunk);
start += CHUNK_SIZE;
}
return chunks;
};
const onSelectChange = (selected: DmsFileUploadDeleteLogViewDisplayData[]) => {
keys.value = selected;
};
@@ -617,7 +507,6 @@ const onSearchByFilePath = async (fvalue: string) => {
);
previousFilePath.value = unref(currFilePath.value);
currFilePath.value = fvalue;
headers.value.FilePath = fvalue;
isSearched.value = true;
if (currentSearchCriteria.value && fvalue && fvalue.length > 0) {
currentSearchCriteria.value.Filter = [];
+33 -99
View File
@@ -5,11 +5,13 @@
@refresh="onReSearch" @search="onSearchByName" @breadcrumb-click="onBreadcrumbClick" />
<OperatorStation :selected-items="keys" :upload-url="uploadUrl" :headers="headers" :can-create="canCreate"
:can-amend="canAmend" :can-delete="canDelete" :can-view="canView" @create-folder="onCreate"
@before-upload="beforeUpload" @download="onDownloadSelected" @share="onShareSelected"
@publish="onPublishSelected" @view-preview="onViewPreviewSelected" @play-video="onUrlVideoPlayItem"
@play-audio="onPlayAudioItem" @pause-audio="onPauseAudio" @continue-audio="onContinueAudio"
@preview="onPreviewSelected" @rename="onAmendSelected" @move="onMoveSelected" @delete="onDeleteSelected" />
:can-amend="canAmend" :can-delete="canDelete" :can-view="canView"
:resolve-file-path="resolveUploadFilePath" @create-folder="onCreate" @upload-start="onUploadStart"
@upload-progress="onUploadProgress" @upload-success="onUploadSuccess" @upload-error="onUploadError"
@download="onDownloadSelected" @share="onShareSelected" @publish="onPublishSelected"
@view-preview="onViewPreviewSelected" @play-video="onUrlVideoPlayItem" @play-audio="onPlayAudioItem"
@pause-audio="onPauseAudio" @continue-audio="onContinueAudio" @preview="onPreviewSelected"
@rename="onAmendSelected" @move="onMoveSelected" @delete="onDeleteSelected" />
<MyComputer :datas="datas" v-model:selected-keys="keys" @select-change="onSelectChange"
@open-folder="onOpenFolder" @open-file="onOpenFile" @preview-image="onPreviewOrgFile" />
@@ -68,10 +70,8 @@ import { notification } from "ant-design-vue";
import { SearchCriteriaResultData } from "@/data/auto/searchCriteriaResultData";
import dmsFolder from "./DmsFolderModal.vue";
import { useLoginProfileStore } from "@/store/modules/loginProfile";
import type { UploadProps } from "ant-design-vue";
import { WebSitePolicy } from "@/policy/custom/webSitePolicy";
import { orderBy } from "lodash-es";
import axios from "axios";
import VideoPlay from "./PlayVideo.vue";
import { Howl } from "howler";
import { PublishVideoEntity } from "@/data/auto/publishVideoEntity";
@@ -79,7 +79,6 @@ import publishVideoService from "@/service/master/publishVideoService";
import { useWindowSize } from "@vueuse/core";
import fileTypePolicy from "@/policy/custom/fileTypePolicy";
import { PublishAudioEntity } from "@/data/auto/publishAudioEntity";
import { v4 as uuidv4 } from "uuid";
import { FamilyFolderFavoriteViewEntity } from "@/data/auto/familyFolderFavoriteViewEntity";
import { DmsFileEntity } from "@/data/auto/dmsFileEntity";
import { PublishData } from "@/data/auto/publishData";
@@ -113,12 +112,11 @@ const route = useRoute();
const router = useRouter();
const previousFilePath = ref<string>("");
const fileList = ref<UploadProps["fileList"]>([]);
const uploadUrl = ref<string>(WebSitePolicy.WebApiRoot + "DmsFile/UploadFile");
const uploadUrl = ref<string>(WebSitePolicy.WebApiRoot + "DmsFile/UploadDmsFile");
if (process.env.NODE_ENV === "development") {
uploadUrl.value = WebSitePolicy.WebApiRootDev + "/DmsFile/UploadFile";
uploadUrl.value = WebSitePolicy.WebApiRootDev + "/DmsFile/UploadDmsFile";
} else {
uploadUrl.value = WebSitePolicy.WebApiRoot + "/DmsFile/UploadFile";
uploadUrl.value = WebSitePolicy.WebApiRoot + "/DmsFile/UploadDmsFile";
}
const currFilePath = ref<string>("");
@@ -276,99 +274,35 @@ onMounted(async () => {
onSearchByFilePath(rootPath.value);
});
const beforeUpload: UploadProps["beforeUpload"] = async (file) => {
if (!file || typeof file.size !== "number") {
return false;
}
loading.value = true;
fileList.value = [...(fileList.value || []), file];
const chunks = splitFile(file);
let bindOption: BindOption = bindOptions.value.filter(
/** 解析上传目标文件夹(DMS 当前路径) */
const resolveUploadFilePath = () => {
const bindOption = bindOptions.value.filter(
(x) => x.BindValue == DmsFileUploadViewDisplayData.DB_FILE_PATH,
)[0];
currFilePath.value = headers.value.FilePath = bindOption.Value;
let i = 1;
let fileSize = file.size;
let uid: string = uuidv4();
spinTip.value = "上传中(1/" + chunks.length + ")...";
await uploadChunk(chunks, file.name, uid, i);
return bindOption?.Value || currFilePath.value;
};
const onUploadStart = () => {
loading.value = true;
};
const onUploadProgress = (payload: { text?: string }) => {
spinTip.value = payload?.text || "上传中...";
};
const onUploadSuccess = (payload: { total?: number }) => {
const total = payload?.total ?? 1;
let sec = 2500;
if (total > 25) sec = total * 100;
spinTip.value = "加载中...";
return false;
setTimeout(() => {
onReSearch();
}, sec);
};
const uploadChunk = async (
chunks: any,
fileName: string,
uid: string,
index: number,
) => {
const formData = new FormData();
const fileExtension = fileName.split(".").pop();
formData.append(
fileName,
chunks[index - 1],
uid +
"-" +
index.toString() +
"-" +
chunks.length.toString() +
"." +
fileExtension,
);
if (process.env.NODE_ENV === "development") {
axios.defaults.baseURL = WebSitePolicy.WebApiRootDev;
axios.defaults.withCredentials = true;
} else {
axios.defaults.baseURL = WebSitePolicy.WebApiRoot;
}
try {
let response = await axios.create().request({
url: "/DmsFile/UploadFile",
method: "post",
data: formData,
headers: headers.value,
});
if (response.status == 200 && index < chunks.length) {
spinTip.value = "上传中(" + (index + 1) + "/" + chunks.length + ")...";
await uploadChunk(chunks, fileName, uid, index + 1);
} else if (index == chunks.length) {
let sec: number = 2500;
if (index > 25) sec = index * 100;
spinTip.value = "加载中...";
setTimeout(function () {
onReSearch();
}, sec);
}
} catch (error) {
loading.value = false;
spinTip.value = "加载中...";
}
};
const CHUNK_SIZE = 1 * 1024 * 1024;
const splitFile = (file: any) => {
const chunks: Blob[] = [];
if (
!file ||
typeof file.size !== "number" ||
typeof file.slice !== "function"
) {
return chunks;
}
let start = 0;
while (start < file.size) {
const end = Math.min(start + CHUNK_SIZE, file.size);
const chunk = file.slice(start, end);
chunks.push(chunk);
start += CHUNK_SIZE;
}
return chunks;
const onUploadError = () => {
loading.value = false;
spinTip.value = "加载中...";
};
const onSelectChange = (selected: DmsFileUploadViewDisplayData[]) => {
+122 -279
View File
@@ -7,64 +7,33 @@
<a-row :wrap="false">
<a-col flex="none">
<div style="padding: 0">
<a-input
flex="none"
readonly
value="群组代码"
class="site-input-split"
style="
<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="familyName"
flex="auto"
@search="onModalSearch"
/>
<a-input-search :disabled="isEdit" v-model:value="familyName" 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"
v-if="canCreate"
>新增</a-button
>
<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"
v-if="canCreate">新增</a-button>
</a-space>
</a-col>
</a-row>
</div>
<div
class="page-main ft-page"
:class="{ 'ft-readonly': !isEdit }"
:style="{ height: resultH }"
>
<div class="page-main ft-page" :class="{ 'ft-readonly': !isEdit }" :style="{ height: resultH }">
<!-- 顶部信息卡 -->
<div class="ft-hero">
<div class="ft-hero-icon">
@@ -90,57 +59,33 @@
<div class="ft-grid">
<div class="ft-field">
<div class="ft-label">群组代码</div>
<a-input
size="large"
:maxlength="50"
:readonly="true"
placeholder="群组代码"
v-model:value="data.FamilyGroup.FamilyName"
/>
<a-input size="large" :maxlength="50" :readonly="true" placeholder="群组代码"
v-model:value="data.FamilyGroup.FamilyName" />
</div>
<div class="ft-field">
<div class="ft-label">
群组名称<span class="ft-required">*</span>
</div>
<a-input
size="large"
:maxlength="500"
required
:readonly="!isEdit"
placeholder="请输入群组名称"
v-model:value="data.FamilyGroup.FamilyLocalName"
/>
<a-input size="large" :maxlength="500" required :readonly="!isEdit" placeholder="请输入群组名称"
v-model:value="data.FamilyGroup.FamilyLocalName" />
</div>
<div class="ft-field ft-field-full">
<div class="ft-label">群组描述</div>
<a-textarea
:auto-size="{ minRows: 3, maxRows: 5 }"
:maxlength="256"
:readonly="!isEdit"
placeholder="请输入群组描述"
v-model:value="data.FamilyGroup.Notice"
/>
<a-textarea :auto-size="{ minRows: 3, maxRows: 5 }" :maxlength="256" :readonly="!isEdit"
placeholder="请输入群组描述" v-model:value="data.FamilyGroup.Notice" />
</div>
<div class="ft-field">
<div class="ft-label">公开设置</div>
<div class="ft-check">
<a-switch
v-model:checked="data.IsOpen"
:disabled="!isEdit"
checked-children="公开"
un-checked-children="隐私"
/>
<a-switch v-model:checked="data.IsOpen" :disabled="!isEdit" checked-children="公开"
un-checked-children="隐私" />
</div>
</div>
<div class="ft-field">
<div class="ft-label">加入申请</div>
<div class="ft-check">
<a-switch
v-model:checked="data.IsAdd"
:disabled="!isEdit"
checked-children="可申请"
un-checked-children="不可申请"
/>
<a-switch v-model:checked="data.IsAdd" :disabled="!isEdit" checked-children="可申请"
un-checked-children="不可申请" />
</div>
</div>
</div>
@@ -151,11 +96,7 @@
<a-collapse v-model:activeKey="activeKey" class="ft-collapse">
<a-collapse-panel key="1" header="共享目录">
<div class="ft-list">
<div
v-for="(record, index) in data.FamilyGroupFolders"
:key="record.FolderId || index"
class="ft-item"
>
<div v-for="(record, index) in data.FamilyGroupFolders" :key="record.FolderId || index" class="ft-item">
<div class="ft-item-head">
<span class="ft-item-index">{{ index + 1 }}</span>
<div class="ft-item-title">
@@ -164,66 +105,38 @@
</span>
</div>
<div class="ft-item-actions">
<UserAddOutlined
v-if="!isEdit && record.IsSharePermission"
class="ft-action"
style="color: green"
@click="shareFamilyGroupPermission(record.FolderName)"
/>
<DeleteOutlined
v-if="isEdit"
class="ft-action ft-action-delete"
@click="
deleteFamilyGroupFolder(
record.FolderId,
record.FolderName,
)
"
/>
<UserAddOutlined v-if="!isEdit && record.IsSharePermission" class="ft-action" style="color: green"
@click="shareFamilyGroupPermission(record.FolderName)" />
<DeleteOutlined v-if="isEdit" class="ft-action ft-action-delete" @click="
deleteFamilyGroupFolder(
record.FolderId,
record.FolderName,
)
" />
</div>
</div>
<div class="ft-item-body">
<div v-if="isEdit" class="ft-item-field">
<span class="ft-item-label">目录名称</span>
<a-input
size="large"
:maxlength="50"
required
placeholder="请输入目录名称"
v-model:value="record.FolderLocalName"
/>
<a-input size="large" :maxlength="50" required placeholder="请输入目录名称"
v-model:value="record.FolderLocalName" />
</div>
<div class="ft-item-field">
<span class="ft-item-label">备注</span>
<a-input
v-if="isEdit"
size="large"
:maxlength="500"
required
placeholder="请输入备注"
v-model:value="record.Notice"
/>
<a-input v-if="isEdit" size="large" :maxlength="500" required placeholder="请输入备注"
v-model:value="record.Notice" />
<span v-else class="ft-item-text">
{{ record.Notice || "-" }}
</span>
</div>
</div>
</div>
<div
v-if="
!data.FamilyGroupFolders || !data.FamilyGroupFolders.length
"
class="ft-empty"
>
<div v-if="
!data.FamilyGroupFolders || !data.FamilyGroupFolders.length
" class="ft-empty">
暂无共享目录
</div>
<a-button
v-if="isEdit"
class="ft-add-btn"
type="dashed"
block
@click="addFamilyGroupFolder"
>
<a-button v-if="isEdit" class="ft-add-btn" type="dashed" block @click="addFamilyGroupFolder">
<PlusSquareTwoTone /> 新增共享目录
</a-button>
</div>
@@ -235,11 +148,8 @@
<a-collapse v-model:activeKey="activeKey" class="ft-collapse">
<a-collapse-panel key="2" header="待审核成员名单">
<div class="ft-list">
<div
v-for="(record, index) in data.FamilyGroupRequests"
:key="record.UserName || index"
class="ft-item"
>
<div v-for="(record, index) in data.FamilyGroupRequests" :key="record.UserName || index"
class="ft-item">
<div class="ft-item-head">
<span class="ft-item-index">{{ index + 1 }}</span>
<div class="ft-item-title">
@@ -249,28 +159,19 @@
}}
</span>
</div>
<div
v-if="data.IsManager && !isEdit"
class="ft-item-actions"
>
<CheckCircleTwoTone
class="ft-action"
@click="
okFamilyGroupRequest(
record.FamilyName,
record.UserName,
)
"
/>
<CloseCircleOutlined
class="ft-action ft-action-delete"
@click="
rejectFamilyGroupRequest(
record.FamilyName,
record.UserName,
)
"
/>
<div v-if="data.IsManager && !isEdit" class="ft-item-actions">
<CheckCircleTwoTone class="ft-action" @click="
okFamilyGroupRequest(
record.FamilyName,
record.UserName,
)
" />
<CloseCircleOutlined class="ft-action ft-action-delete" @click="
rejectFamilyGroupRequest(
record.FamilyName,
record.UserName,
)
" />
</div>
</div>
<div class="ft-item-body">
@@ -280,13 +181,10 @@
</div>
</div>
</div>
<div
v-if="
!data.FamilyGroupRequests ||
!data.FamilyGroupRequests.length
"
class="ft-empty"
>
<div v-if="
!data.FamilyGroupRequests ||
!data.FamilyGroupRequests.length
" class="ft-empty">
暂无待审核成员
</div>
</div>
@@ -299,11 +197,8 @@
<a-collapse v-model:activeKey="activeKey" class="ft-collapse">
<a-collapse-panel key="3" header="正式成员名单">
<div class="ft-list">
<div
v-for="(record, index) in data.FamilyGroupManagers"
:key="record.UserName || index"
class="ft-item"
>
<div v-for="(record, index) in data.FamilyGroupManagers" :key="record.UserName || index"
class="ft-item">
<div class="ft-item-head">
<span class="ft-item-index">{{ index + 1 }}</span>
<div class="ft-item-title">
@@ -314,36 +209,27 @@
</span>
</div>
<div v-if="isEdit" class="ft-item-actions">
<DeleteOutlined
class="ft-action ft-action-delete"
@click="
deleteFamilyGroupManager(
record.FamilyName,
record.UserName,
)
"
/>
<DeleteOutlined class="ft-action ft-action-delete" @click="
deleteFamilyGroupManager(
record.FamilyName,
record.UserName,
)
" />
</div>
</div>
<div class="ft-item-body">
<div class="ft-item-field">
<span class="ft-item-label">群组管理员</span>
<a-checkbox
:disabled="!isEdit"
v-model:checked="record.IsManager"
>
<a-checkbox :disabled="!isEdit" v-model:checked="record.IsManager">
管理员
</a-checkbox>
</div>
</div>
</div>
<div
v-if="
!data.FamilyGroupManagers ||
!data.FamilyGroupManagers.length
"
class="ft-empty"
>
<div v-if="
!data.FamilyGroupManagers ||
!data.FamilyGroupManagers.length
" class="ft-empty">
暂无正式成员
</div>
</div>
@@ -355,13 +241,8 @@
<a-collapse v-model:activeKey="activeKey" class="ft-collapse">
<a-collapse-panel key="4" header="目录权限列表">
<div class="ft-list">
<div
v-for="(record, index) in data.FamilyGroupPermissions"
:key="
(record.FolderName || '') + '-' + (record.UserName || index)
"
class="ft-item"
>
<div v-for="(record, index) in data.FamilyGroupPermissions" :key="(record.FolderName || '') + '-' + (record.UserName || index)
" class="ft-item">
<div class="ft-item-head">
<span class="ft-item-index">{{ index + 1 }}</span>
<div class="ft-item-title">
@@ -374,16 +255,13 @@
</span>
</div>
<div v-if="isEdit" class="ft-item-actions">
<DeleteOutlined
class="ft-action ft-action-delete"
@click="
deleteFamilyGroupPermission(
record.FamilyName,
record.FolderName,
record.UserName,
)
"
/>
<DeleteOutlined class="ft-action ft-action-delete" @click="
deleteFamilyGroupPermission(
record.FamilyName,
record.FolderName,
record.UserName,
)
" />
</div>
</div>
<div class="ft-item-body">
@@ -401,13 +279,10 @@
</div>
</div>
</div>
<div
v-if="
!data.FamilyGroupPermissions ||
!data.FamilyGroupPermissions.length
"
class="ft-empty"
>
<div v-if="
!data.FamilyGroupPermissions ||
!data.FamilyGroupPermissions.length
" class="ft-empty">
暂无目录权限
</div>
</div>
@@ -422,59 +297,23 @@
<div>
<a-row v-if="isEdit" :gutter="[16, 8]">
<a-space>
<a-popconfirm
v-if="data.FamilyGroup.CreatedBy"
title="确定是否取消修改?"
ok-text="是"
cancel-text="否"
@confirm="onCancel"
>
<a-button type="primary" ghost :icon="h(UndoOutlined)"
>取消</a-button
>
<a-popconfirm v-if="data.FamilyGroup.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.FamilyGroup.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-button v-if="!data.FamilyGroup.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 && data.IsManager"
: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 && data.IsManager"
>解散</a-button
>
<a-button type="primary" v-if="canAmend" :icon="h(EditOutlined)"
:disabled="!hasData || !data.IsManager" @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 || !data.IsManager"
v-if="canDelete">解散</a-button>
</a-popconfirm>
</a-space>
</a-row>
@@ -484,29 +323,13 @@
</div>
</div>
</div>
<dragModal
v-model:open="showModal"
title="群组查询"
:mask-closable="false"
:fullscreen="width < 640"
@ok="handleSelect"
>
<familyGroupEnquiry
ref="enquiry"
@on-row-dblclick="handleSelect"
></familyGroupEnquiry>
<dragModal v-model:open="showModal" title="群组查询" :mask-closable="false" :fullscreen="width < 640"
@ok="handleSelect">
<familyGroupEnquiry ref="enquiry" @on-row-dblclick="handleSelect"></familyGroupEnquiry>
</dragModal>
<dragModal
v-model:open="showModalUser"
title="成员查询"
:mask-closable="false"
:fullscreen="width < 640"
@ok="handleSelectUser"
>
<familyGroupUserModal
ref="userEnquiry"
@on-row-dblclick="handleSelectUser"
></familyGroupUserModal>
<dragModal v-model:open="showModalUser" title="成员查询" :mask-closable="false" :fullscreen="width < 640"
@ok="handleSelectUser">
<familyGroupUserModal ref="userEnquiry" @on-row-dblclick="handleSelectUser"></familyGroupUserModal>
</dragModal>
</a-spin>
</template>
@@ -782,7 +605,6 @@ onMounted(() => {
canDelete.value = pContext.canDelete(route.meta.permission as string);
canCreate.value = pContext.canCreate(route.meta.permission as string);
computResultTableHeight();
if (familyName.value && familyName.value.trim().length > 0) {
onSearch();
}
@@ -961,7 +783,7 @@ const shareFamilyGroupPermission = (folderName: string) => {
};
const deleteFamilyGroupFolder = (key: number, folderName: string) => {
if (key > 0) {
if (folderName && folderName.length > 0) {
dmsFileService.FindDmsFolderMapByFolderName(folderName).then((count) => {
if (count > 0) {
notification.info({
@@ -969,12 +791,33 @@ const deleteFamilyGroupFolder = (key: number, folderName: string) => {
description: "【群组中心】请先删除共享目录下的内容再删除该目录.",
placement: "topRight",
});
return;
}
else {
data.value.FamilyGroupFolders = data.value.FamilyGroupFolders.filter(
(item) => item.FolderId !== key,
);
data.value.FamilyGroupPermissions = data.value.FamilyGroupPermissions.filter(
(item) =>
!(
item.FolderName == folderName
),
);
}
});
} else {
data.value.FamilyGroupFolders = data.value.FamilyGroupFolders.filter(
(item) => item.FolderId !== key,
);
data.value.FamilyGroupPermissions = data.value.FamilyGroupPermissions.filter(
(item) =>
!(
item.FolderName == folderName
),
);
}
};
+2 -116
View File
@@ -4,9 +4,9 @@
<Toolbar :path-stack="pathStack" :root-path="rootPath" :loading="loading" @go-top="onGoTop" @go-back="onGoBack"
@refresh="onReSearch" @search="onSearchByName" @breadcrumb-click="onBreadcrumbClick" />
<OperatorStationFamily :selected-items="keys" :path-stack="pathStack" :upload-url="uploadUrl" :headers="headers"
<OperatorStationFamily :selected-items="keys" :path-stack="pathStack"
:can-create="canCreate" :can-amend="canAmend" :can-delete="canDelete" :can-view="canView"
@create-folder="onCreate" @set-share-folder="onShareFolder" @before-upload="beforeUpload"
@create-folder="onCreate" @set-share-folder="onShareFolder"
@download="onDownloadSelected" @share="onShareSelected" @publish="onPublishSelected"
@view-preview="onViewPreviewSelected" @play-video="onUrlVideoPlayItem" @play-audio="onPlayAudioItem"
@pause-audio="onPauseAudio" @continue-audio="onContinueAudio" @preview="onPreviewSelected"
@@ -68,11 +68,7 @@ import { SearchFilterTypePolicy } from "@/data/custom/searchFilterTypePolicy";
import { notification } from "ant-design-vue";
import { SearchCriteriaResultData } from "@/data/auto/searchCriteriaResultData";
import dmsFolder from "./DmsFolderModal.vue";
import { useLoginProfileStore } from "@/store/modules/loginProfile";
import type { UploadProps } from "ant-design-vue";
import { WebSitePolicy } from "@/policy/custom/webSitePolicy";
import { orderBy } from "lodash-es";
import axios from "axios";
import VideoPlay from "./PlayVideo.vue";
import { Howl } from "howler";
import { PublishVideoEntity } from "@/data/auto/publishVideoEntity";
@@ -80,7 +76,6 @@ import publishVideoService from "@/service/master/publishVideoService";
import { useWindowSize } from "@vueuse/core";
import fileTypePolicy from "@/policy/custom/fileTypePolicy";
import { PublishAudioEntity } from "@/data/auto/publishAudioEntity";
import { v4 as uuidv4 } from "uuid";
import { FamilyFolderFavoriteViewEntity } from "@/data/auto/familyFolderFavoriteViewEntity";
import { DmsFileEntity } from "@/data/auto/dmsFileEntity";
import { PublishData } from "@/data/auto/publishData";
@@ -116,22 +111,8 @@ const route = useRoute();
const router = useRouter();
const previousFilePath = ref<string>("");
const fileList = ref<UploadProps["fileList"]>([]);
const uploadUrl = ref<string>(WebSitePolicy.WebApiRoot + "DmsFile/UploadFile");
if (process.env.NODE_ENV === "development") {
uploadUrl.value = WebSitePolicy.WebApiRootDev + "/DmsFile/UploadFile";
} else {
uploadUrl.value = WebSitePolicy.WebApiRoot + "/DmsFile/UploadFile";
}
const currFilePath = ref<string>("");
const headers = ref({
Authorization: useLoginProfileStore().getToken(),
"Content-Type": "multipart/form-data",
FilePath: currFilePath.value,
});
const bindOptions = ref<BindOption[]>([]);
const datas = ref<FamilyDmsFileViewDisplayData[]>([]);
const pagingParams = ref<PagingParams>({ pageSize: 999999, page: 1 });
@@ -288,101 +269,6 @@ onMounted(async () => {
onSearchByFilePath(rootPath.value);
});
const beforeUpload: UploadProps["beforeUpload"] = async (file) => {
if (!file || typeof file.size !== "number") {
return false;
}
loading.value = true;
fileList.value = [...(fileList.value || []), file];
const chunks = splitFile(file);
let bindOption: BindOption = bindOptions.value.filter(
(x) => x.BindValue == FamilyDmsFileViewDisplayData.DB_FILE_PATH,
)[0];
currFilePath.value = headers.value.FilePath = bindOption.Value;
let i = 1;
let fileSize = file.size;
let uid: string = uuidv4();
spinTip.value = "上传中(1/" + chunks.length + ")...";
await uploadChunk(chunks, file.name, uid, i);
spinTip.value = "加载中...";
return false;
};
const uploadChunk = async (
chunks: any,
fileName: string,
uid: string,
index: number,
) => {
const formData = new FormData();
const fileExtension = fileName.split(".").pop();
formData.append(
fileName,
chunks[index - 1],
uid +
"-" +
index.toString() +
"-" +
chunks.length.toString() +
"." +
fileExtension,
);
if (process.env.NODE_ENV === "development") {
axios.defaults.baseURL = WebSitePolicy.WebApiRootDev;
axios.defaults.withCredentials = true;
} else {
axios.defaults.baseURL = WebSitePolicy.WebApiRoot;
}
try {
let response = await axios.create().request({
url: "/DmsFile/UploadFile",
method: "post",
data: formData,
headers: headers.value,
});
if (response.status == 200 && index < chunks.length) {
spinTip.value = "上传中(" + (index + 1) + "/" + chunks.length + ")...";
await uploadChunk(chunks, fileName, uid, index + 1);
} else if (index == chunks.length) {
let sec: number = 2500;
if (index > 25) sec = index * 100;
spinTip.value = "加载中...";
setTimeout(function () {
onReSearch();
}, sec);
}
} catch (error) {
loading.value = false;
spinTip.value = "加载中...";
}
};
const CHUNK_SIZE = 1024 * 1024;
const splitFile = (file: any) => {
const chunks: Blob[] = [];
if (
!file ||
typeof file.size !== "number" ||
typeof file.slice !== "function"
) {
return chunks;
}
let start = 0;
while (start < file.size) {
const end = Math.min(start + CHUNK_SIZE, file.size);
const chunk = file.slice(start, end);
chunks.push(chunk);
start += CHUNK_SIZE;
}
return chunks;
};
const onSelectChange = (selected: FamilyDmsFileViewDisplayData[]) => {
keys.value = selected;
};
+157 -11
View File
@@ -66,15 +66,31 @@
>
<!-- 顶部:头像 / 姓名 / 编号 -->
<div class="fp-hero">
<a-avatar
class="fp-hero-avatar"
:size="72"
:src="data.FamilyPerson.AvatarFilename"
<FileUpload
class="fp-hero-upload"
accept="image/*"
file-path="FamilyPersonAvatar"
:disabled="!isEdit || avatarUploading"
@start="onAvatarUploadStart"
@progress="onAvatarUploadProgress"
@success="onAvatarUploadSuccess"
@error="onAvatarUploadError"
>
<template #icon>
<UserOutlined />
</template>
</a-avatar>
<div
class="fp-hero-avatar-wrap"
:class="{ 'is-editing': isEdit }"
>
<a-avatar class="fp-hero-avatar" :size="72" :src="avatarSrc">
<template #icon>
<UserOutlined />
</template>
</a-avatar>
<div v-if="isEdit" class="fp-hero-avatar-mask">
<span v-if="avatarUploading">{{ avatarUploadPercent }}%</span>
<CameraOutlined v-else />
</div>
</div>
</FileUpload>
<div class="fp-hero-main">
<div class="fp-hero-name">
{{
@@ -89,11 +105,14 @@
</div>
</div>
<a-button
v-if="isEdit && data.FamilyPerson.AvatarFilename"
v-if="
isEdit &&
(data.FamilyPerson.AvatarUrl || data.FamilyPerson.AvatarFilename)
"
class="fp-hero-clear"
type="text"
size="small"
@click.prevent="data.FamilyPerson.AvatarFilename = ''"
@click.prevent="onClearAvatar"
>清除头像</a-button
>
</div>
@@ -499,7 +518,17 @@
</a-spin>
</template>
<script lang="ts" setup>
import { ref, unref, onBeforeMount, inject, provide, h, onMounted } from "vue";
import {
ref,
unref,
computed,
onBeforeMount,
onUnmounted,
inject,
provide,
h,
onMounted,
} from "vue";
import { notification } from "ant-design-vue";
import { useRoute, useRouter } from "vue-router";
import {
@@ -514,7 +543,9 @@ import {
DownSquareTwoTone,
UpSquareTwoTone,
UserOutlined,
CameraOutlined,
} from "@ant-design/icons-vue";
import FileUpload from "@/components/basic/FileUpload.vue";
import {
PermissionContext,
usePermissionContext,
@@ -555,6 +586,76 @@ const showModalType = ref<number>(0);
const data = ref<FamilyPersonData>(new FamilyPersonData());
const hasData = ref<boolean>(false);
//================ 头像上传 ================
const avatarUploading = ref<boolean>(false);
const avatarUploadPercent = ref<number>(0);
/**
* 头像显示地址,统一取 AvatarUrl:
* 查询时为服务端地址;上传后为本地即时预览(blob)地址
*/
const avatarSrc = computed(() => data.value.FamilyPerson.AvatarUrl || "");
/** 释放本地上传产生的 blob 预览地址,避免内存泄漏 */
const revokeAvatarPreview = () => {
const url = data.value.FamilyPerson.AvatarUrl;
if (url && url.startsWith("blob:")) {
URL.revokeObjectURL(url);
}
};
/** 清空头像(含本地预览地址与文件名) */
const clearAvatarPreview = () => {
revokeAvatarPreview();
data.value.FamilyPerson.AvatarUrl = "";
data.value.FamilyPerson.AvatarFilename = "";
};
const onClearAvatar = () => {
clearAvatarPreview();
};
const onAvatarUploadStart = () => {
avatarUploading.value = true;
avatarUploadPercent.value = 0;
};
const onAvatarUploadProgress = (payload: { percent?: number }) => {
avatarUploadPercent.value = payload?.percent ?? 0;
};
const onAvatarUploadSuccess = (payload: {
file: File;
fileName: string;
response: any;
}) => {
avatarUploading.value = false;
avatarUploadPercent.value = 0;
revokeAvatarPreview();
// 上传后先用本地预览地址显示头像
data.value.FamilyPerson.AvatarUrl = URL.createObjectURL(payload.file);
// 绑定分割后序号最大的分片文件名,如 xxxx-3-3.jpg
if (payload.fileName) {
data.value.FamilyPerson.AvatarFilename = payload.fileName;
}
};
const onAvatarUploadError = () => {
avatarUploading.value = false;
avatarUploadPercent.value = 0;
notification.error({
message: "系统提示:",
description: "头像上传失败",
placement: "topRight",
});
};
onUnmounted(() => {
clearAvatarPreview();
});
const yesNoPolicy = ref<SelectOption[]>([]);
const { getPolicySelectOptionsComputed } = useLocalePolicy();
const genderSelectOptions = getPolicySelectOptionsComputed('GenderPolicy');
@@ -639,6 +740,7 @@ const onSearch = async () => {
return;
}
clearAvatarPreview();
data.value = await FamilyPersonService.FindByKey(personId.value.trim());
if (data.value.FamilyPerson == null) {
@@ -662,6 +764,7 @@ const onSearch = async () => {
};
const onReset = () => {
clearAvatarPreview();
data.value = new FamilyPersonData();
personId.value = "";
@@ -691,12 +794,26 @@ const onSave = async () => {
? deathDate.value.toDate()
: null!;
// 上传后的本地预览地址(blob),保存后若服务端已返回头像地址则释放
// 注意:无头像时服务端返回的 AvatarUrl 为 null,此处必须归一化为字符串,否则 startsWith 抛错导致保存中断
const localAvatarUrl = data.value.FamilyPerson.AvatarUrl ?? "";
const hasLocalAvatarPreview = localAvatarUrl.startsWith("blob:");
if (data.value.FamilyPerson?.CreatedBy)
data.value = await FamilyPersonService.Update(data.value);
else {
data.value = await FamilyPersonService.Insert(data.value);
}
// 服务端返回了头像地址就释放本地预览,否则继续用本地预览显示
if (hasLocalAvatarPreview) {
if (data.value.FamilyPerson.AvatarUrl) {
URL.revokeObjectURL(localAvatarUrl);
} else {
data.value.FamilyPerson.AvatarUrl = localAvatarUrl;
}
}
personId.value = data.value.FamilyPerson.PersonId;
loading.value = false;
@@ -704,6 +821,7 @@ const onSave = async () => {
isEdit.value = false;
} catch (error) {
loading.value = false;
console.log(error);
}
};
@@ -795,6 +913,34 @@ const handleSelect = async () => {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}
.page-main .fp-hero-upload {
flex: 0 0 auto;
cursor: pointer;
}
.page-main .fp-hero-avatar-wrap {
position: relative;
display: inline-flex;
border-radius: 50%;
}
.page-main .fp-hero-avatar-mask {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: rgba(0, 0, 0, 0.35);
color: #fff;
font-size: 18px;
transition: background 0.2s;
}
.page-main .fp-hero-avatar-wrap.is-editing:hover .fp-hero-avatar-mask {
background: rgba(0, 0, 0, 0.55);
}
.page-main .fp-hero-main {
flex: 1 1 auto;
min-width: 0;
@@ -1,912 +0,0 @@
<template>
<a-spin size="large" :spinning="loading">
<search
@on-submit="onSubmit"
@on-print="onPrint"
@on-reset="onReset"
@use-pagination="onPagination"
rowSelectionType="checkbox"
:scrollx="200"
:loading="loading"
:pagination="result"
@on-select-change="onSelectChange"
>
<template #control> </template>
<template #headerCell="{ column }">
<template
v-if="column.dataIndex === DmsFileUploadDeleteLogViewDisplayData.URL"
>
<span
style="color: darkgoldenrod; font-size: large; cursor: pointer"
@click.prevent="onChangeFolder(rootPath)"
>
<LaptopOutlined
style="color: darkgoldenrod; font-size: larger"
title="返回顶层"
/>返回顶层
</span>
</template>
</template>
<template #bodyCell="{ column, record, index }">
<template
v-if="column.dataIndex === DmsFileUploadDeleteLogViewDisplayData.URL"
>
<template v-if="!fileTypePolicy.isImage(record.OrderKey)">
<template v-if="record.IsDirectoryIndc == 'Y'">
<a-card hoverable style="width: 300px">
<a-card-meta>
<template #description>
<span
style="
color: darkgoldenrod;
margin-left: 4pxr;
cursor: pointer;
font-size: 26px;
"
@click.prevent="onChangeFolder(record.FileName)"
>
<FolderOutlined
style="color: darkgoldenrod; font-size: 48px"
title="打开文件夹"
/>{{ record.OrgFileName }}
</span>
</template>
</a-card-meta>
<template #actions> </template>
</a-card>
</template>
<template v-else-if="fileTypePolicy.isVideo(record.OrderKey)">
<a-card style="width: 300px">
<template #cover>
<img
style="height: 200px"
:src="record.FormattedPic.replace('.m3u8', '.png')"
:title="record.OrgFileName"
/>
</template>
<a-card-meta>
<template #description>
<a-flex>
<cust-icon-font
size="26"
type="icon-foldervideo"
v-if="record.TranscodeIndc == 'Y'"
@click.prevent="
onUrlVideoPlay(record.Url, record.OrgFileName)
"
/>
<cust-icon-font
v-else
size="26"
type="icon-mp3-play"
@click.prevent="onVideoPlay(record.FileId)"
/>
<span
style="
color: darkgoldenrod;
margin-left: 4pxr;
margin-top: 2px;
"
>
{{ record.OrgFileName }}
</span>
</a-flex>
</template>
</a-card-meta>
<template #actions> </template>
</a-card>
</template>
<template v-else>
<DownloadOutlined
style="color: rgb(19, 185, 190); font-size: larger"
title="下载"
@click.prevent="
onDownloadFile(record.FileId, record.OrgFileName)
"
/>
<span>{{ record.OrgFileName }}</span>
</template>
</template>
<template v-else>
<a-card hoverable style="width: 300px">
<template #cover>
<img
style="height: 200px"
:src="record.Url"
:title="record.OrgFileName"
@click.prevent="
onViewPictureFile(record.FileId, record.OrgFileName)
"
/>
</template>
<a-card-meta>
<template #description>
<span style="color: darkgoldenrod; margin-left: 4pxr">
<DownloadOutlined
style="color: rgb(19, 185, 190); font-size: larger"
title="原图下载"
@click.prevent="
onDownloadFile(record.FileId, record.OrgFileName)
"
/>
{{ record.OrgFileName }}
</span>
</template>
</a-card-meta>
<template #actions> </template>
</a-card>
</template>
</template>
</template>
<template #footer>
<div class="page-footer">
<a-row :gutter="24">
<a-col :lg="24" :sm="24">
<a-row v-if="isEdit" :gutter="[16, 8]">
<a-space>
<a-button type="primary" @click.prevent="onCancel"
>取消</a-button
>
<a-button type="primary" @click.prevent="onSave"
>确定</a-button
>
</a-space>
</a-row>
<a-row v-if="!isEdit" :gutter="[16, 8]">
<a-space>
<!-- <a-button type="primary" v-if="canAmend" :icon="h(FileImageOutlined)"
:disabled="!(keys && keys.length > 0)" @click.prevent="onViewPic">预览</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>
<a-image
:width="1024"
:style="{ display: 'none' }"
:preview="{
visible,
onVisibleChange: setPicVisible,
}"
:src="imageSrc"
/>
<dmsFolder @on-submit="onReload"></dmsFolder>
<videoPlay></videoPlay>
</a-spin>
</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,
PictureOutlined,
LaptopOutlined,
ScissorOutlined,
UploadOutlined,
FolderAddOutlined,
DownloadOutlined,
FolderOutlined,
FileImageOutlined,
} from "@ant-design/icons-vue";
import { DmsFileEntity } from "@/data/auto/dmsFileEntity";
import { DmsFileResultData } from "@/data/auto/dmsFileResultData";
import { DmsFileUploadDeleteLogViewDisplayData } from "@/data/auto/dmsFileUploadDeleteLogViewDisplayData";
import { SearchFilter } from "@/data/auto/searchFilter";
import dmsFileService from "@/service/master/dmsFileService";
import { DmsFileSearchCriteria } from "@/data/auto/dmsFileSearchCriteria";
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 dmsFolder from "./DmsFolderModal.vue";
import { useLoginProfileStore } from "@/store/modules/loginProfile";
import type { UploadProps } from "ant-design-vue";
import { WebSitePolicy } from "@/policy/custom/webSitePolicy";
import { orderBy } from "lodash-es";
import { bsInfo } from "@/utils/browser";
import axios from "axios";
import { getuid } from "process";
import VideoPlay from "./PlayVideo.vue";
import {
enquiryMethodKey,
searchCriteriaManagerKey,
masterOperationToEnquiryMethodKey,
MasterOperationToEnquiryModelOption,
layoutProviderMethodKey,
} from "@/injections";
import { useOrderedChildren } from "@/hooks/use-ordered-children";
import { SearchCriteriaContext } from "@/injections";
import fileTypePolicy from "@/policy/custom/fileTypePolicy";
//page from-----------------------------------------------------------------------------------------------------------------------------------
//================inject================
//报表名称
const layoutProviderMethod = inject(layoutProviderMethodKey);
//================inject================
const widthColumn = ref<string>("3%");
if (bsInfo.size.sm) {
widthColumn.value = "8%";
} else {
widthColumn.value = "3%";
}
const loading = ref<boolean>(false);
const isEdit = ref<boolean>(false);
const canView = ref<boolean>(false);
const canAmend = ref<boolean>(false);
const canDelete = ref<boolean>(false);
const canCreate = ref<boolean>(false);
const modalTitle = ref<string>("新建文件夹");
const rootPath = ref<string>("");
const imageSrc = ref<string>("");
const route = useRoute();
const router = useRouter();
const previousFilePath = ref<string>("");
const fileList = ref<UploadProps["fileList"]>([]);
const uploadUrl = ref<string>(WebSitePolicy.WebApiRoot + "DmsFile/UploadFile");
if (process.env.NODE_ENV === "development") {
uploadUrl.value = WebSitePolicy.WebApiRootDev + "/DmsFile/UploadFile";
} else {
uploadUrl.value = WebSitePolicy.WebApiRoot + "/DmsFile/UploadFile";
}
const currFilePath = ref<string>("");
const headers = ref({
Authorization: useLoginProfileStore().getToken(),
"Content-Type": "multipart/form-data",
FilePath: currFilePath.value,
});
const columns = ref([
{
title: "",
dataIndex: DmsFileUploadDeleteLogViewDisplayData.URL,
sorter: false,
width: "50%",
sequence: 1,
hidden: "N",
},
]);
const viewOption = {
Value: "default",
SelectOptions: [{ BindValue: "default", DisplayName: "默认" }],
Columns: [{ BindValue: "default", BindColumn: columns }],
};
const bindOptions = ref<BindOption[]>([]);
const datas = ref<DmsFileUploadDeleteLogViewDisplayData[]>([]);
const pagingParams = ref<PagingParams>({ pageSize: 20, page: 1 });
const result = ref<SearchCriteriaResultData>(new SearchCriteriaResultData());
const data = ref<DmsFileResultData>(new DmsFileResultData());
const currentFilter = ref<SearchFilter[]>([]);
const isSearched = ref<boolean>(false);
const currentSearchCriteria = ref<DmsFileSearchCriteria>(
new DmsFileSearchCriteria(),
);
const keys = ref<DmsFileUploadDeleteLogViewDisplayData[]>([]);
const dmsFolderModal = ref<boolean>(false);
const dmsFolderTitle = ref<string>("新建文件夹");
const dmsFolderIsRename = ref<boolean>(false);
const dmsFolderIsMove = ref<boolean>(false);
const entity = ref<DmsFileEntity>(new DmsFileEntity());
const videoPlayModal = ref<boolean>(false);
const videoSrc = ref<string>("");
const videoType = ref<string>("");
const {
children: items,
addChild: addItem,
removeChild: removeItem,
} = useOrderedChildren<SearchCriteriaContext>();
//================provide================
//初始化查询条件UI
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("bindOptions", bindOptions);
provide("dmsFolderModal", dmsFolderModal);
provide("dmsFolderTitle", dmsFolderTitle);
provide("dmsFolderIsRename", dmsFolderIsRename);
provide("dmsFolderIsMove", dmsFolderIsMove);
provide("DmsFileEntity", entity);
//控制按键多行显示,可以单列24或者多列
//provide("layoutSetting", { controlLeft: 20, controlRight: 4, soltLeft: 8, soltRight: 8 });
//space: true 控制按键单行显示
// provide("layoutSetting", { controlLeft: 16, controlRight: 8, space: true });
// provide("viewOption", viewOption);
provide("videoPlayModal", videoPlayModal);
provide("videoSrc", videoSrc);
provide("videoType", videoType);
//table数据源
// provide("datas", datas);
//================provide================
defineExpose({
keys,
});
onMounted(async () => {
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);
await initBindOption();
onSearchByFilePath(rootPath.value);
});
const beforeUpload: UploadProps["beforeUpload"] = (file) => {
fileList.value = [...(fileList.value || []), file];
const chunks = splitFile(file);
let bindOption: BindOption = bindOptions.value.filter(
(x) => x.BindValue == DmsFileUploadDeleteLogViewDisplayData.DB_FILE_PATH,
)[0];
currFilePath.value = headers.value.FilePath = bindOption.Value;
let i = 1;
let fileSize = file.size;
let uid: string = file.uid;
uploadChunk(chunks, file.name, uid, i);
return false;
};
const uploadChunk = async (
chunks: any,
fileName: string,
uid: string,
index: number,
) => {
const formData = new FormData();
const fileExtension = fileName.split(".").pop();
formData.append(
fileName,
chunks[index - 1],
uid +
"-" +
index.toString() +
"-" +
chunks.length.toString() +
"." +
fileExtension,
);
if (process.env.NODE_ENV === "development") {
axios.defaults.baseURL = WebSitePolicy.WebApiRootDev;
axios.defaults.withCredentials = true;
} else {
axios.defaults.baseURL = WebSitePolicy.WebApiRoot;
}
try {
let response = await axios.create().request({
url: "/DmsFile/UploadFile",
method: "post",
data: formData,
headers: headers.value,
});
if (response.status == 200 && index < chunks.length) {
uploadChunk(chunks, fileName, uid, index + 1);
} else if (index == chunks.length) {
onReSearch();
loading.value = false;
}
} catch (error) {
loading.value = false;
console.log(error);
}
};
const CHUNK_SIZE = 512 * 1024; // 设置每个分片的大小为1MB
const splitFile = (file: any) => {
const chunks = [];
let start = 0;
while (start < file.size) {
const end = Math.min(start + CHUNK_SIZE, file.size);
const chunk = file.slice(start, end);
chunks.push(chunk);
start += CHUNK_SIZE;
}
return chunks;
};
const onSelectChange = (selected: DmsFileUploadDeleteLogViewDisplayData[]) => {
keys.value = selected;
};
const onLastFolder = async () => {
if (!datas.value || datas.value.length == 0) {
onSearchByFilePath(previousFilePath.value);
} else {
onPreviousFolder();
}
};
const onPreviousFolder = async () => {
let filePath: string = "";
if (datas.value && datas.value.length > 0) filePath = datas.value[0].FilePath;
dmsFileService.FindFolderNameParentByFolderName(filePath).then((res) => {
if (res && res.length > 0) onSearchByFilePath(res);
});
};
const onChangeFolder = async (fvalue: string) => {
onSearchByFilePath(fvalue);
};
const onVideoPlay = async (fid: number) => {
videoType.value = "video/mp4";
videoPlayModal.value = true;
//从服务器取出
dmsFileService
.FindDeleteLogM3u8Path(fid)
.then((res) => {
videoSrc.value = res;
})
.catch(() => {
videoPlayModal.value = false;
});
};
const onPreviewFile = async (username: string, orderKey: string) => {
loading.value = true;
getPreviewFile(username, orderKey);
};
const getPreviewFile = (username: string, fname: string) => {
let dms: DmsFileEntity = new DmsFileEntity();
dms.CreatedBy = username;
dms.OrderKey = fname;
dmsFileService
.DownloadFilePreview(dms)
.then((res) => {
const downloadUrl = window.URL.createObjectURL(new Blob([res]));
imageSrc.value = downloadUrl;
loading.value = false;
setPicVisible(true);
})
.catch(() => {
loading.value = false;
});
};
const DownloadFileCombine = (
fs: string[],
mergedData: Uint8Array,
index: number,
ttl: number,
total: number,
preview: boolean,
fname: string,
) => {
loading.value = true;
fs[fs.length - 2] = index.toString();
dmsFileService
.DownloadFileCombine(fs.toString().replace(/,/g, "-"))
.then((res) => {
mergedData.set(new Uint8Array(res), ttl);
if (index < total) {
DownloadFileCombine(
fs,
mergedData,
index + 1,
ttl + res.byteLength,
total,
preview,
fname,
);
} else if (index == total) {
loading.value = false;
const downloadUrl = window.URL.createObjectURL(new Blob([mergedData]));
imageSrc.value = downloadUrl;
if (!preview) {
const link = document.createElement("a");
link.href = downloadUrl;
link.setAttribute("download", fname);
document.body.appendChild(link);
link.click();
} else {
setPicVisible(true);
}
}
})
.catch(() => {
loading.value = false;
});
};
const onDownloadFile = async (fid: number, fname: string) => {
GetOrgPictureFile(fid, fname, false);
};
const GetOrgPictureFile = async (
fid: number,
fname: string,
preview: boolean,
) => {
if (fileTypePolicy.isVideo(fname)) {
// onVideoPlay(keys.value[0].FileId);
return;
}
loading.value = true;
dmsFileService
.FindDmsFileDeleteLogByKey(fid)
.then((res) => {
let fs: string[] = res.OrderKey.replace("scale_", "").split("-");
let index: string = fs[fs.length - 2];
let total: string = fs[fs.length - 1].split(".")[0];
var mergedData = new Uint8Array((res.FileSize + 1) * 1024);
let ttl: number = 0;
fs[fs.length - 2] = "1";
DownloadFileCombine(
fs,
mergedData,
1,
ttl,
parseInt(total),
preview,
fname,
);
})
.catch(() => {
loading.value = false;
});
};
const onViewPictureFile = async (fid: number, fname: string) => {
GetOrgPictureFile(fid, fname, true);
};
const onSearchByFilePath = async (fvalue: string) => {
setBindOptionValue(
DmsFileUploadDeleteLogViewDisplayData.DB_FILE_PATH,
fvalue,
"",
[fvalue],
false,
);
previousFilePath.value = unref(currFilePath.value);
currFilePath.value = fvalue;
isSearched.value = true;
if (currentSearchCriteria.value && fvalue && fvalue.length > 0) {
currentSearchCriteria.value.Filter = [];
let filter: SearchFilter;
filter = new SearchFilter();
filter.Column = DmsFileUploadDeleteLogViewDisplayData.DB_FILE_PATH;
filter.Type = "SingleSelection";
filter.Operator = "single";
filter.Value = fvalue;
currentSearchCriteria.value.Filter.push(filter);
} else {
currentSearchCriteria.value.Filter = [];
}
await findBySearchCriteria(unref(currentSearchCriteria));
};
const onReset = () => {
keys.value = [];
result.value.Total = 0;
setBindOptionValue(
DmsFileUploadDeleteLogViewDisplayData.DB_FILE_PATH,
rootPath.value,
"",
[rootPath.value],
false,
);
};
const onUrlVideoPlay = (url: string, title: string) => {
videoType.value = "m3u8";
videoSrc.value = url;
videoPlayModal.value = true;
};
const onViewPic = () => {
if (keys.value.length == 0) return;
if (keys.value[0].IsDirectoryIndc == "Y") {
onChangeFolder(keys.value[0].FileName);
} else if (
keys.value[0].OrgFileName.toLowerCase().indexOf("png") > 0 ||
keys.value[0].OrgFileName.toLowerCase().indexOf("jpg") > 0 ||
keys.value[0].OrgFileName.toLowerCase().indexOf("jpeg") > 0 ||
keys.value[0].OrgFileName.toLowerCase().indexOf("gif") > 0
) {
onPreviewFile(keys.value[0].CreatedBy, keys.value[0].OrderKey);
} else if (
keys.value[0].OrgFileName.toLowerCase().indexOf("mp4") > 0 ||
keys.value[0].OrgFileName.toLowerCase().indexOf("rm") > 0 ||
keys.value[0].OrgFileName.toLowerCase().indexOf("mpeg") > 0 ||
keys.value[0].OrgFileName.toLowerCase().indexOf("mov") > 0 ||
keys.value[0].OrgFileName.toLowerCase().indexOf("wmv") > 0 ||
keys.value[0].OrgFileName.toLowerCase().indexOf("avi") > 0
) {
onVideoPlay(keys.value[0].FileId);
} else {
notification.info({
message: "系统提示:",
description: "未知格式无法预览",
placement: "topRight",
});
}
};
const visible = ref<boolean>(false);
const setPicVisible = (value: boolean): void => {
visible.value = value;
};
const onDelete = async () => {
if (keys.value.length == 0) return;
for (let element of keys.value) {
await dmsFileService.DeleteLog(element.FileId);
}
notification.info({
message: "系统提示:",
description: "记录删除成功",
placement: "topRight",
});
onReload();
};
const onSave = () => {
isEdit.value = false;
};
const onCancel = () => {
isEdit.value = false;
};
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();
bindOptions.value = [];
bindOptions.value.splice(0, bindOptions.value.length);
let bindOption: BindOption = ubo.getBindOption(
DmsFileUploadDeleteLogViewDisplayData.DB_ORG_FILE_NAME,
"名称",
SearchFilterTypePolicy.TEXT,
SearchFilterOperatorPolicy.CONTAIN,
);
bindOptions.value.push(bindOption);
let rps: SelectOption[] = await dmsFileService.FindDmsFolderDeleteLogs();
if (rps && rps.length > 1) {
rootPath.value = currFilePath.value = rps[0].BindValue;
}
bindOption = ubo.getBindSelectOption(
DmsFileUploadDeleteLogViewDisplayData.DB_FILE_PATH,
"当前目录",
SearchFilterTypePolicy.SINGLE_SELECTION,
SearchFilterOperatorPolicy.SINGLE,
rps,
);
bindOptions.value.push(bindOption);
bindOption = ubo.getBindOption(
DmsFileUploadDeleteLogViewDisplayData.DB_CREATED_DATETIME,
"创建日期",
SearchFilterTypePolicy.DATE,
SearchFilterOperatorPolicy.DATE_BETWEEN,
);
bindOptions.value.push(bindOption);
setBindOptionValue(
DmsFileUploadDeleteLogViewDisplayData.DB_FILE_PATH,
currFilePath.value,
"",
[currFilePath.value],
false,
);
};
//table from-----------------------------------------------------------------------------------------------------------------------------------
result.value.Page = pagingParams.value.page ?? 1;
result.value.Limit = pagingParams.value.pageSize;
const onSubmit = async (filters: SearchFilter[]) => {
loading.value = true;
let c: DmsFileSearchCriteria = new DmsFileSearchCriteria();
currentFilter.value = filters;
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 onPrint = async () => {
loading.value = true;
let c: DmsFileSearchCriteria = new DmsFileSearchCriteria();
if (currentFilter.value.length > 0) c.Filter = currentFilter.value;
else c.Filter = [];
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 dmsFileService.PrintBySearchCriteria(c);
} catch (error) {
console.log(error);
}
};
const findBySearchCriteria = async (c: DmsFileSearchCriteria) => {
keys.value = [];
try {
if (c.limit == 0) c.limit = pagingParams.value.pageSize;
if (c.page == 0) c.page = pagingParams.value.page ?? 1;
data.value = await dmsFileService.FindDeleteLogBySearchCriteria(c);
result.value.Total = data.value.Total;
result.value.Page = data.value.Page;
result.value.Limit = data.value.Limit;
if (data.value.Logs)
data.value.Logs = orderBy(
data.value.Logs,
(x) => x.IsDirectoryIndc,
"desc",
);
datas.value = data.value.Logs;
loading.value = false;
} catch (error) {
loading.value = false;
console.log(error);
}
};
const onPagination = async (params: PagingParams) => {
try {
if (!isSearched.value) return;
let c: DmsFileSearchCriteria = new DmsFileSearchCriteria();
if (currentFilter.value.length > 0) c.Filter = currentFilter.value;
else c.Filter = [];
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 = [];
loading.value = true;
currentSearchCriteria.value = c;
await findBySearchCriteria(c);
} catch (error) {
loading.value = false;
console.log(error);
}
};
const onReSearch = async () => {
setBindOptionValue(
DmsFileUploadDeleteLogViewDisplayData.DB_FILE_PATH,
currFilePath.value,
"",
[currFilePath.value],
false,
);
await findBySearchCriteria(unref(currentSearchCriteria));
};
const onReload = async () => {
await initBindOption();
setBindOptionValue(
DmsFileUploadDeleteLogViewDisplayData.DB_FILE_PATH,
currFilePath.value,
"",
[currFilePath.value],
false,
);
await findBySearchCriteria(unref(currentSearchCriteria));
};
//table end-----------------------------------------------------------------------------------------------------------------------------------
</script>
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff