first commit

This commit is contained in:
2025-08-19 10:19:29 +08:00
commit 3b61e84a7f
3014 changed files with 2640574 additions and 0 deletions

View File

@ -0,0 +1,137 @@
<template>
<div class="book_file">
<!-- 添加或修改公司对话框 -->
<el-dialog v-model="isShowDialog" width="80vw" custom-class="book_file_loading" :close-on-click-modal="false" :destroy-on-close="true">
<template #header>
<div>查看资料文件</div>
</template>
<el-form ref="queryRef" :inline="true" label-width="100px">
<el-row>
<el-col :span="8" class="colBlock">
<el-form-item label="文件名称" prop="fileName">
<el-input v-model="formData.fileName" placeholder="请输入文件名称搜索" clearable @keyup.enter.native="getDataFileQuery" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item>
<el-button type="primary" @click="getDataFileQuery">
<el-icon><Search /></el-icon>搜索
</el-button>
<el-button @click="resetQuery" type="danger">
<el-icon><Refresh /></el-icon>清空
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="content">
<div class="left_box" :style="treeList.length ? 'width: 70%' : 'width: 100%'">
<el-table v-loading="loading" :data="tableData" border height="63vh" :empty-text="emptyText">
<el-table-column label="序号" align="center" type="index" width="80px" />
<el-table-column label="文件名称" align="center" prop="fileName" min-width="100px" />
<el-table-column label="文件类型" align="center" prop="fileSuffix" width="100px" />
<el-table-column label="文件路径" align="center" min-width="100px">
<template #default="scope">
<span>{{ filterfilenPath(scope.row.filePath) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding" width="250px" fixed="right">
<template #default="scope">
<el-button type="success" link @click="onExport(scope.row)">
<el-icon><Download /></el-icon>下载
</el-button>
<el-button type="primary" link @click="onBook(scope.row)">
<el-icon><View /></el-icon>查看
</el-button>
</template>
</el-table-column>
</el-table>
<pagination :total="total" v-model:page="formData.pageNum" v-model:limit="formData.pageSize" @pagination="getDataFileQuery" />
</div>
</div>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { listKnowledgeDocument } from '@/api/design/technicalStandard';
import { useUserStoreHook } from '@/store/modules/user';
const emit = defineEmits(['onExport', 'onExportView', 'onBook']);
const stores = useUserStoreHook();
const loading = ref(false);
const tableData = ref<any[]>([]);
const isShowDialog = ref(false);
const formData = reactive({
fileName: '',
projectId: stores.selectedProject.id,
pageNum: 1,
pageSize: 10
});
const total = ref(0);
const emptyText = ref('暂无数据');
const treeList = ref<any[]>([]);
const openDialog = () => {
isShowDialog.value = true;
getDataFileQuery();
emptyText.value = '请输入文件名称进行搜索!';
resetForm();
};
const resetForm = () => {
tableData.value = [];
formData.fileName = '';
treeList.value = [];
emptyText.value = '暂无数据';
};
const getDataFileQuery = () => {
loading.value = true;
emptyText.value = '数据加载中……';
listKnowledgeDocument(formData).then((res: any) => {
loading.value = false;
tableData.value = [];
if (res.code == 200 && res.rows?.length) {
tableData.value = res.rows;
total.value = res.total;
} else {
emptyText.value = '没有查询到数据,请重新输入搜索';
}
});
};
const onBook = (row: any) => {
emit('onBook', row);
};
const onExport = (row: any) => {
emit('onExportView', row);
};
const resetQuery = () => {
tableData.value = [];
formData.fileName = '';
loading.value = false;
emptyText.value = '暂无数据';
};
const filterfilenPath = (val: string): string => {
return val.replace(/^.*?知识库\//, '知识库/');
};
defineExpose({ openDialog });
</script>
<style scoped lang="scss">
.book_file {
.content {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
}
}
</style>

View File

@ -0,0 +1,190 @@
<template>
<div class="document_detail" id="document_detail">
<div class="move_pop" id="detial_pop">
<!-- <span>{{ title }}</span> -->
<div class="box">
<img v-if="type == 2" src="../icon/suo.png" @click="onFull(1)" />
<img v-else src="../icon/full.png" @click="onFull(2)" />
<span class="close" @click="onClose"></span>
</div>
</div>
<div class="box_app" id="box_app"></div>
</div>
</template>
<script lang="ts">
import { setMove } from '@/utils/moveDiv';
declare const CXO_API: any;
export default defineComponent({
name: 'index',
setup(props, { emit }) {
const { proxy } = <any>getCurrentInstance();
const state = reactive({
title: '',
type: 2
});
onMounted(() => {
setMove('detial_pop', 'document_detail');
});
// 打开弹窗
const openDialog = (obj) => {
state.title = obj.fileName;
init(obj);
};
const onError = function (event) {
//举例,强制保存后,判断文档内容是否保存成功
if (event.data) {
if (event.data.errorCode == 'forcesave') {
var desc = event.data.errorDescription;
desc = JSON.parse(desc);
if (desc.error == 0) {
//保存成功
} else {
//保存失败或异常
}
} else if (event.data.errorCode == 'setallcellvalue') {
var desc = event.data.errorDescription;
desc = JSON.parse(desc);
if (desc.error == 0) {
//填充成功
} else if (desc.error == -1) {
//当前文档正处于协同编辑中
} else {
//填充异常
}
} else if (event.data.errorCode == 'clearsheet') {
var desc = event.data.errorDescription;
desc = JSON.parse(desc);
if (desc.error == 0) {
//清除成功
} else if (desc.error == -1) {
//当前文档正处于协同编辑中
} else {
//清除异常
}
}
}
};
const onDocumentReady = function () {
// console.log('文档加载完成');
};
const init = (obj) => {
let documentKey = obj.id.toString() + new Date().getTime();
console.log('🚀 ~ init ~ url:', obj.fileUrl);
let type = obj.fileSuffix;
if (obj.fileSuffix.includes('.')) {
type = obj.fileSuffix.substring(1);
}
let documentType = 'word'; // docx doc
if (type == 'xlsx' || type == 'xls') {
documentType = 'cell'; //电子表格
} else if (type == 'ppt' || type == 'pptx') {
documentType = 'slide'; //演示文档文件
}
new CXO_API.CXEditor('box_app', {
document: {
fileType: type,
key: documentKey,
title: obj.fileName,
url: obj.fileUrl
},
documentType,
editorConfig: {
mode: 'view',
callbackUrl: ''
},
height: '100%',
events: {
onDocumentReady: onDocumentReady,
onError: onError
},
zoom: -1
});
};
const onClose = () => {
emit('onClose', false);
};
const onFull = (type) => {
// 全屏
let document_detail = document.getElementById('document_detail');
state.type = type;
if (type == 2) {
document_detail.style.width = '100%';
document_detail.style.height = '100%';
} else {
document_detail.style.width = '1200px';
document_detail.style.height = '80vh';
}
};
return {
proxy,
openDialog,
onClose,
onFull,
...toRefs(state)
};
}
});
</script>
<style lang="scss" scoped>
.document_detail {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 999999;
width: 100%;
height: 100%;
border: 1px solid #9f9f9f;
.box_app {
// width: 1300px !important;
// height: 80vh !important;
background-color: #f1f1f1;
}
.move_pop {
width: 100%;
// position: absolute;
// top: 0;
// right: 0%;
height: 24px;
// background: linear-gradient(#2a5095, #213f7b, #111e48);
background-color: #f4f5f6;
display: grid;
place-items: center;
> span {
color: #000000;
font-weight: bold;
font-size: 20px;
letter-spacing: 2px;
}
.box {
display: flex;
width: 60px;
position: absolute;
top: 0;
right: 10px;
// height: 100%;
align-items: center;
img {
width: 22px;
margin-top: 6px;
cursor: pointer;
}
.close {
position: absolute;
right: 2px;
/* top: -8px; */
color: #8d8d8d;
width: 24px;
height: 24px;
font-size: 20px;
//border: 2px solid #0ff;
border-radius: 50%;
display: grid;
place-items: center;
cursor: pointer;
}
}
}
}
</style>

View File

@ -0,0 +1,203 @@
<template>
<div class="document_detail_eidt" id="document_detail_eidt">
<div class="move_pop" id="detial_edit">
<!-- <span>{{ title }}</span> -->
<div class="box">
<img v-if="type == 2" src="../icon/full.png" @click="onFull(1)" />
<img v-else src="../icon/suo.png" @click="onFull(2)" />
<span class="close" @click="onClose"></span>
</div>
</div>
<div class="box_app" id="box_app_edit"></div>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, ref, defineComponent, watch, getCurrentInstance } from 'vue';
import { setMove } from '@/utils/moveDiv';
import { useUserStoreHook } from '@/store/modules/user';
// Ensure CXO_API is available globally or import it if it's a module
// Example for global usage (e.g., included via script tag in index.html):
declare const CXO_API: any;
export default defineComponent({
name: 'index',
setup(props, { emit }) {
const stores = useUserStoreHook();
const { proxy } = <any>getCurrentInstance();
const state = reactive({
title: '',
projectId: stores.selectedProject.id,
type: 2,
postUrl: ''
});
onMounted(() => {
setMove('detial_edit', 'document_detail_eidt');
});
// 打开弹窗
const openDialog = (obj, url) => {
state.postUrl = url;
state.title = obj.name;
init(obj);
};
const onError = function (event) {
console.log('编辑器错误: code ' + event.data.errorCode + ', 描述' + event.data.errorDescription);
//举例,强制保存后,判断文档内容是否保存成功
if (event.data) {
if (event.data.errorCode == 'forcesave') {
var desc = event.data.errorDescription;
desc = JSON.parse(desc);
if (desc.error == 0) {
//保存成功
} else {
//保存失败或异常
}
} else if (event.data.errorCode == 'setallcellvalue') {
var desc = event.data.errorDescription;
desc = JSON.parse(desc);
if (desc.error == 0) {
//填充成功
} else if (desc.error == -1) {
//当前文档正处于协同编辑中
} else {
//填充异常
}
} else if (event.data.errorCode == 'clearsheet') {
var desc = event.data.errorDescription;
desc = JSON.parse(desc);
if (desc.error == 0) {
//清除成功
} else if (desc.error == -1) {
//当前文档正处于协同编辑中
} else {
//清除异常
}
}
}
};
const onDocumentReady = function () {
console.log('文档加载完成');
};
const init = (obj) => {
let documentKey = obj.id.toString() + new Date().getTime();
let baseURL = import.meta.env.VITE_APP_BASE_API;
let type = obj.fileSuffix;
if (obj.fileSuffix.includes('.')) {
type = obj.fileSuffix.substring(1);
}
let documentType = 'word'; // docx doc
if (type == 'xlsx' || type == 'xls') {
documentType = 'cell'; //电子表格
} else if (type == 'ppt' || type == 'pptx') {
documentType = 'slide'; //演示文档文件
}
console.log(baseURL + state.postUrl + '?path=' + obj.filePath + '&id=' + obj.id);
new CXO_API.CXEditor('box_app_edit', {
document: {
fileType: obj.fileSuffix,
key: documentKey,
title: obj.fileName,
url: obj.fileUrl
},
documentType,
token: stores.token,
editorConfig: {
callbackUrl: baseURL + state.postUrl + obj.id + '?path=' + obj.filePath
},
events: {
onDocumentReady: onDocumentReady,
onError: onError
}
});
};
const onClose = () => {
emit('onClose', false);
};
const onFull = (type) => {
// 全屏
let document_detail = document.getElementById('document_detail_eidt');
state.type = type;
if (type == 2) {
// 弹框放大
document_detail.style.width = '100%';
document_detail.style.height = '100%';
} else {
// 弹框缩小
document_detail.style.width = '1200px';
document_detail.style.height = '80vh';
}
};
return {
proxy,
onFull,
openDialog,
onClose,
...toRefs(state)
};
}
});
</script>
<style lang="scss" scoped>
.document_detail_eidt {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 999999;
width: 100%;
height: 100%;
border: 1px solid #9f9f9f;
.box_app {
width: 1200px !important;
height: 80vh !important;
background-color: #f1f1f1;
margin-top: 100px;
}
.move_pop {
width: 100%;
// position: absolute;
// top: 0;
// right: 0%;
height: 24px;
// background: linear-gradient(#2a5095, #213f7b, #111e48);
background-color: #f4f5f6;
display: grid;
place-items: center;
> span {
color: #000000;
font-weight: bold;
font-size: 20px;
letter-spacing: 2px;
}
.box {
display: flex;
width: 60px;
position: absolute;
top: 0;
right: 10px;
// height: 100%;
align-items: center;
img {
width: 22px;
margin-top: 6px;
cursor: pointer;
}
.close {
position: absolute;
right: 2px;
/* top: -8px; */
color: #8d8d8d;
width: 24px;
height: 24px;
font-size: 20px;
//border: 2px solid #0ff;
border-radius: 50%;
display: grid;
place-items: center;
cursor: pointer;
}
}
}
}
</style>

View File

@ -0,0 +1,149 @@
<template>
<div class="system-document-container">
<el-card shadow="hover">
<div class="system-document-search mb-5">
<el-form :model="param" ref="queryRef" :inline="true" label-width="100px">
<el-row>
<el-col :span="2">
<el-button type="success" :disabled="multiple" @click="onRecyclingStation(null, true)">
<el-icon><RefreshRight /></el-icon>批量恢复
</el-button>
</el-col>
<el-col :span="2">
<el-button type="danger" :disabled="multiple" @click="onRecyclingStation(null, false)">
<el-icon><DeleteFilled /></el-icon>批量删除
</el-button>
</el-col>
</el-row>
</el-form>
</div>
<el-table v-loading="loading" :data="tableData" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" align="center" type="index" min-width="30px" />
<el-table-column label="文件名称" align="center" prop="fileName" min-width="100px" />
<el-table-column label="文件路径" align="center" min-width="100px">
<template #default="scope">
<span>{{ filterfilenPath(scope.row.filePath) }}</span>
</template>
</el-table-column>
<el-table-column label="删除时间" align="center" prop="createTime" min-width="100px" />
<el-table-column label="操作" align="center" class-name="small-padding" min-width="80px" fixed="right">
<template #default="scope">
<div>
<el-button type="success" link @click="onRecyclingStation(scope.row, true)">
<el-icon><RefreshRight /></el-icon>恢复
</el-button>
</div>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="param.pageNum" v-model:limit="param.pageSize" @pagination="getDocumentDataList" />
</el-card>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, getCurrentInstance, computed } from 'vue';
import { ElMessageBox, ElMessage, ElLoading } from 'element-plus';
import { documentDataAllList, templateRecycleBin, dataRecyclingStation } from '@/api/design/technicalStandard';
import { useUserStoreHook } from '@/store/modules/user';
const userStore = useUserStoreHook();
const currentProject = computed(() => userStore.selectedProject);
const loading = ref(false);
const queryRef = ref();
const multiple = ref(true);
const ids = ref<string>('');
const tableData = ref<any[]>([]);
const param = reactive({
type: 2,
projectId: currentProject.value?.id,
pageNum: 1,
pageSize: 10
});
const total = ref(0);
const value = ref('2');
const getDocumentDataList = () => {
loading.value = true;
tableData.value = [];
value.value = '2';
documentDataAllList(param).then((res: any) => {
tableData.value = res.rows ?? [];
total.value = res.total;
loading.value = false;
});
};
const handleSelectionChange = (selection: any[]) => {
ids.value = selection.map((item) => item.id).join(',');
multiple.value = !selection.length;
};
const onRecyclingStation = (row: any, flag: boolean) => {
let type = 2;
let selectedIds: string = '';
let title = '删除';
let msg = '你确定要删除所选文件或文件夹?';
if (row) {
selectedIds = row.id;
} else {
selectedIds = ids.value;
}
if (flag) {
type = 1;
title = '恢复';
msg = '你确定要恢复所选文件或文件夹?';
}
ElMessageBox.confirm(msg, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
const loadingInstance = ElLoading.service({
lock: true,
text: `正在${title}中……`,
background: 'rgba(0, 0, 0, 0.7)'
});
if (flag) {
dataRecyclingStation(selectedIds).then((res) => {
loadingInstance.close();
if (res.code == 200) {
getDocumentDataList();
ElMessage.success('操作成功');
}
});
} else {
templateRecycleBin(selectedIds).then((res) => {
loadingInstance.close();
if (res.code == 200) {
getDocumentDataList();
ElMessage.success('操作成功');
}
});
}
})
.catch(() => {});
};
const filterfilenPath = (val: string): string => {
return val.replace(/^.*?知识库\//, '知识库/');
};
defineExpose({
getDocumentDataList
});
</script>
<style lang="scss" scoped>
.colBlock {
display: block;
}
.colNone {
display: none;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,603 @@
<template>
<div>
<el-tabs v-model="activeName" class="demo-tabs p5" @tab-click="handleCheckMian">
<el-tab-pane label="资料" name="first">
<div class="profile_engin">
<div class="box_info">
<div class="tree_left1" id="tree_left1">
<div class="file_upload check_select">
<div class="box_btn">
<file-upload
v-model="state.paramsQuery.file"
:limit="100"
:uploadUrl="uploadUrl"
:params="uploadParams"
:on-upload-success="uploadFile"
:fileType="[]"
>
<el-button type="primary" style="float: left" :disabled="!state.parentPid">
<el-icon size="small"><Plus /></el-icon>上传文件
</el-button>
</file-upload>
</div>
<el-button type="primary" :disabled="!state.parentPid" @click="onExport"
><el-icon><Download /></el-icon>下载</el-button
>
<el-button type="primary" @click="onBook"
><el-icon><View /></el-icon>查看全项目文件</el-button
>
</div>
<div class="file_upload check_select">
<el-input class="input_left" v-model="filterText" size="small" placeholder="请输入文件名称" />
</div>
<el-tree
ref="treeRef"
highlight-current
:default-expand-all="state.checked"
:filter-node-method="filterFolder"
:data="state.treeList"
node-key="id"
accordion
:expand-on-click-node="false"
@node-click="handleNodeClick"
:current-node-key="state.selectedNodeId"
>
<template #default="{ node, data }">
<span class="custom-tree-node">
<el-icon color="#f1a81a"><FolderOpened /></el-icon>
<span>{{ node.label }}</span>
</span>
</template>
</el-tree>
<div class="resize-handle resize-handle-right right"></div>
</div>
<div class="list_right" id="list_right1">
<div>
<el-form :model="state.paramsQuery" ref="queryRef" :inline="true" label-width="100px">
<el-row>
<el-col :span="7" class="colBlock">
<el-form-item label="文件名称" prop="fileName">
<el-input
v-model="state.paramsQuery.fileName"
placeholder="请输入文件名称"
clearable
@keyup.enter.native="getdocumentDataList"
/>
</el-form-item>
</el-col>
<el-col :span="6" class="m-l10">
<el-form-item>
<el-button type="primary" @click="searchInfo"
><el-icon><Search /></el-icon>搜索</el-button
>
<el-button @click="resetQuery"
><el-icon><Refresh /></el-icon>重置</el-button
>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<el-table v-loading="state.loading" :data="state.infoList" height="67vh" border>
<el-table-column label="序号" align="center" type="index" min-width="50px" />
<el-table-column label="文件名称" align="center" prop="fileName"></el-table-column>
<el-table-column label="文件类型" align="center" prop="fileSuffix" width="100px" />
<el-table-column label="流程状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="wf_business_status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="上传时间" align="center" prop="createTime"> </el-table-column>
<el-table-column label="操作" align="center" width="300">
<template #default="scope">
<el-button type="primary" link @click="handleApproval(scope.row)"
><el-icon><Plus /></el-icon>审批</el-button
>
<el-button type="primary" link @click="handleViewApproval(scope.row)"
><el-icon><View /></el-icon>流程查看</el-button
>
<el-button type="primary" link @click="handleView(scope.row)" v-if="acceptType.includes(scope.row.fileSuffix)"
><el-icon><View /></el-icon>查看</el-button
>
<el-button type="primary" v-if="state.wordType.includes(scope.row.fileSuffix)" link @click="updataView(scope.row)"
><el-icon><EditPen /></el-icon>修改文件</el-button
>
<el-button type="primary" link @click="onExportView(scope.row)"
><el-icon><Download /></el-icon>下载</el-button
>
<el-button type="success" link @click="updateName(scope.row)"
><el-icon><EditPen /></el-icon>修改名称</el-button
>
<el-button type="danger" link @click="handleDelete(scope.row)"
><el-icon><DeleteFilled /></el-icon>删除</el-button
>
</template>
</el-table-column>
</el-table>
<pagination
:total="state.total"
v-model:page="state.paramsQuery.pageNum"
v-model:limit="state.paramsQuery.pageSize"
@pagination="getdocumentDataList"
/>
</div>
</div>
<documentsDeailsVue ref="documentDetailRef" v-if="state.showDocumentDetail" @onClose="onClose"></documentsDeailsVue>
<documentsEdit ref="documentDataEditRef" v-if="state.showdocumentDataEdit" @onClose="onCloseEdit"></documentsEdit>
<bookFile ref="bookFileRef" @onExportView="onExportView" @onBook="handleView" @onExport="onExport"></bookFile>
<el-dialog draggable title="上传文件" v-model="uploadFileder" width="30%">
<file-upload v-model="state.paramsQuery.file"></file-upload>
<template #footer>
<span>
<el-button @click="uploadFileder = false">取消</el-button>
<el-button type="primary" @click="subMitUpload">确定</el-button>
</span>
</template>
</el-dialog>
</div>
<el-image-viewer
ref="imageRef"
style="width: 100%; height: 100%"
:url-list="[imgUrl]"
v-if="imgUrl"
show-progress
fit="cover"
@close="imgUrl = ''"
/>
</el-tab-pane>
<el-tab-pane label="回收站" name="second">
<RecyclingStation ref="recylingRef"></RecyclingStation>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup name="KnowledgeDocument" lang="ts">
import {
listKnowledgeDocument,
delKnowledgeDocument,
addKnowledgeDocument,
getUniFolderDownloadList,
treeStructureData,
documentDataEdit
} from '@/api/design/technicalStandard';
import documentsEdit from './component/documentsEdit.vue';
import documentsDeailsVue from './component/documentsDeails.vue';
import RecyclingStation from './component/recyclingStation.vue';
import { useUserStoreHook } from '@/store/modules/user';
import bookFile from './component/bookFile.vue';
const activeName = ref('first');
const { proxy } = getCurrentInstance() as any;
// 获取用户 store
const userStore = useUserStoreHook();
// 从 store 中获取项目列表和当前选中的项目
const currentProject = computed(() => userStore.selectedProject);
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
const uploadUrl = computed(() => {
return `/design/technicalStandard/file`;
});
const uploadParams = computed(() => {
return {
pid: state.paramsQuery.folderId,
projectId: state.projectId
};
});
const imgUrl = ref<string>('');
const filterText = ref('');
const treeRef = ref();
const documentDetailRef = ref();
const documentDataEditRef = ref();
const uploadFileder = ref(false);
const imageRef = ref();
const recylingRef = ref();
const bookFileRef = ref();
const state = reactive({
treeList: [] as any,
arrayList: [] as any,
infoMap: new Map(),
infoList: [] as any,
total: 0,
paramsQuery: {
folderId: '',
fileName: '',
pageNum: 1,
pageSize: 20,
projectId: currentProject.value?.id,
file: null,
pid: null
},
loading: false,
checked: true,
showDocumentDetail: false,
showdocumentDataEdit: false,
showUploadFileder: false,
parentRow: null,
parentPid: null,
parentName: '',
selectedNodeId: null,
projectId: currentProject.value?.id || '',
imgType: ['jpg', 'png', 'jpeg', 'gif', 'svg'],
wordType: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'],
browserViewableType: ['html', 'htm', 'txt', 'log', 'md', 'json', 'xml', 'css', 'js'],
draggableCheck: true
});
const acceptType = computed(() => [...state.imgType, ...state.wordType, ...state.browserViewableType]);
watch(filterText, (val: any) => {
treeRef.value!.filter(val);
});
// 上传文件
const uploadFile = (files: any[]) => {
proxy.$modal.success('上传成功');
state.paramsQuery.file = null;
getdocumentDataList();
// uploadFileder.value = true;
};
// 提交上传文件
const subMitUpload = () => {
if (!state.paramsQuery.file) {
ElMessage.warning('请选择文件!');
return;
}
addKnowledgeDocument(state.paramsQuery, { projectId: state.projectId, pid: state.parentPid }).then((res: any) => {
if (res.code == 200) {
ElMessage.success('上传成功');
uploadFileder.value = false;
state.paramsQuery.file = null; //清空文件
getdocumentDataList();
} else {
ElMessage.error(res.message);
}
});
};
const searchInfo = () => {
// 搜索
getdocumentDataList();
};
const resetQuery = () => {
// 重置
state.paramsQuery.fileName = '';
getdocumentDataList();
};
// 获取树形结构文件夹目录
const gettreeStructureData = () => {
state.parentPid = null;
activeName.value = 'first';
const loading = ElLoading.service({
lock: true,
text: '正在加载中……',
background: 'rgba(0, 0, 0, 0.7)',
target: '.tree_left1'
});
treeStructureData(state.projectId).then((res: any) => {
loading.close();
if (res.code == 200 && res.data && res.data.length) {
state.selectedNodeId = '';
state.treeList = res.data;
state.paramsQuery.folderId = res.data[0].id;
getdocumentDataList();
// setInfo(res.data);
}
});
};
// 处理数据
const setInfo = (arr) => {
arr.forEach((element) => {
state.arrayList.push(element);
state.infoMap.set(element.folderId, element.id);
if (element.treeStructureDataRes && element.treeStructureDataRes.length) {
setInfo(element.treeStructureDataRes);
}
});
};
// 选择目录文件
const handleNodeClick = (row) => {
state.parentRow = row;
state.parentPid = row.parentId;
console.log(row);
state.parentName = row.label;
state.paramsQuery.folderId = row.id;
getdocumentDataList();
if (row.id === state.selectedNodeId) {
// 如果当前节点已经选中,则取消选中
state.selectedNodeId = null;
state.parentPid = null; //关闭父级选择的id
state.parentRow = null; //获取父级对象
state.parentName = ''; //获取父级对应的名称
state.paramsQuery.folderId = ''; //
} else {
// 否则选中当前节点 重新赋值
state.selectedNodeId = row.id;
}
};
// 获取文档列表数据
const getdocumentDataList = () => {
if (!state.paramsQuery.folderId) {
return;
}
state.loading = true;
listKnowledgeDocument(state.paramsQuery).then((res: any) => {
state.loading = false;
if (res.code == 200) {
state.infoList = res.rows;
state.total = res.total;
}
});
};
// 查询tree树形结构数据
const filterFolder = (value: string, data: any) => {
if (!value) return true;
return data.name.includes(value);
};
const handleDelete = (row) => {
// 删除文档
let msg = '你确定要删除所选文件?';
delFile(msg, row, () => {
getdocumentDataList();
});
};
//切换tab
const handleCheckMian = (tab, event) => {
activeName.value = tab.name;
if (activeName.value === 'first') {
gettreeStructureData();
} else {
// 回收站
recylingRef.value.getDocumentDataList();
}
};
const onExportView = (row) => {
console.log(row);
proxy.$download.direct(row.fileUrl, row.originalName);
};
const updateName = (row) => {
// 修改名称
editName(row, '请输入文件名称', 1);
};
const handleView = (row) => {
if (state.imgType.includes(row.fileSuffix)) {
imgUrl.value = row.fileUrl;
return;
} else if (state.wordType.includes(row.fileSuffix)) {
state.showDocumentDetail = true;
nextTick(() => {
documentDetailRef.value.openDialog(row);
});
} else {
window.open(row.fileUrl);
}
};
// 关闭在线编辑弹框
const onClose = () => {
state.showDocumentDetail = false;
};
// 关闭修改的在线文档弹框
const onCloseEdit = () => {
state.showdocumentDataEdit = false;
};
const updataView = (row) => {
// 修改文档
state.showdocumentDataEdit = true;
nextTick(() => {
documentDataEditRef.value.openDialog(row, '/design/technicalStandard/changxie/callback/');
});
};
// 删除文件及文件夹
const delFile = (msg, data, cb) => {
ElMessageBox.confirm(msg, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
delKnowledgeDocument([data.id]).then((res: any) => {
if (res.code == 200) {
ElMessage.success('删除成功');
cb();
}
});
})
.catch(() => {});
};
const editName = (data, title, type) => {
ElMessageBox.prompt(title, '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputErrorMessage: title,
inputValue: data.fileName
})
.then(({ value }) => {
documentDataEdit({ id: data.id, fileName: value }).then((res: any) => {
if (res.code == 200) {
ElMessage({
type: 'success',
message: '修改成功'
});
// 数据重新刷新
if (type == 2) {
gettreeStructureData();
} else {
getdocumentDataList();
}
} else {
ElMessage({
type: 'error',
message: res.message
});
}
});
})
.catch(() => {});
};
const onExport = () => {
getUniFolderDownloadList(state.paramsQuery.folderId).then((res: any) => {
if (res.code == 200) {
console.log(state.paramsQuery.fileName);
proxy.$download.downloadFilesAsZip(res.data, { urlKey: 'fileUrl', nameKey: 'originalName', zipName: state.parentName + '.zip' });
}
});
};
// 查看所有资料
const onBook = () => {
bookFileRef.value.openDialog();
};
onMounted(() => {
gettreeStructureData();
});
//监听项目id刷新数据
const listeningProject = watch(
() => currentProject.value?.id,
(nid, oid) => {
state.projectId = nid;
state.paramsQuery.projectId = nid;
if (activeName.value === 'first') {
gettreeStructureData();
} else {
// 回收站
recylingRef.value.getDocumentDataList();
}
}
);
onUnmounted(() => {
listeningProject();
});
const handleApproval = (row) => {
proxy.$tab.closePage(proxy.$route);
proxy.$router.push({
path: `/approval/technicalStandard/indexEdit`,
query: {
id: row.id,
type: 'update'
}
});
};
const handleViewApproval = (row) => {
proxy.$tab.closePage(proxy.$route);
proxy.$router.push({
path: `/approval/technicalStandard/indexEdit`,
query: {
id: row.id,
type: 'view'
}
});
};
</script>
<style lang="scss" scoped>
.profile_engin {
height: 80vh;
.set-tool {
display: none;
}
.el-tree-node__content:hover,
.el-tree-node__content:active {
.set-tool {
display: inline-block;
}
}
.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
background-color: #354e67 !important;
color: #fff;
}
.box_info {
display: flex;
justify-content: space-between;
}
.pagination-container {
padding: 10px 0 !important;
}
> div {
height: 100%;
width: 100%;
}
.tree_left1 {
width: 20%;
background-color: #fff;
border: 1px solid #dddddd;
border-radius: 6px;
padding: 6px 0px;
position: relative;
min-width: 26%;
border-right: 6px solid;
border-right-color: rgba(204, 230, 255, 0);
.resize-handle-right {
top: 0;
width: 6px;
height: 100%;
right: -10px;
cursor: ew-resize;
position: absolute;
z-index: 999;
}
.check_select {
display: flex;
align-items: center;
width: 100%;
// justify-content: space-between;
padding: 4px;
border-bottom: 1px solid #f1f1f1;
.box_btn {
margin: 0 10px 0 20px;
position: relative;
> span {
padding: 4px 10px;
background: #67c23a;
color: #fff;
border-radius: 2px;
}
.btn {
position: absolute;
left: 20%;
display: none;
top: -2px;
width: 220px;
.el-button {
float: left;
}
}
}
.box_btn:hover,
.box_btn:active {
cursor: pointer;
.btn {
display: block;
}
}
}
.file_upload {
margin: 2px 0;
}
.input_left {
padding: 6px;
box-sizing: border-box;
// border-bottom: 1px solid #cbcbcb;
}
}
.list_right {
width: 79.5%;
background: white;
border: 1px solid #ededed;
padding: 10px;
box-sizing: border-box;
}
.el-tree {
height: calc(80vh - 160px);
width: 100%;
overflow: auto !important;
}
// .el-tree-node__children {
// overflow: visible !important;
// }
}
</style>

View File

@ -0,0 +1,356 @@
<template>
<div class="p-4 bg-gray-50">
<div class="max-w-4xl mx-auto">
<!-- 顶部按钮区域 -->
<el-card class="mb-4 rounded-lg shadow-sm bg-white border border-gray-100 transition-all hover:shadow-md">
<approvalButton
@submitForm="submitForm"
@approvalVerifyOpen="approvalVerifyOpen"
@handleApprovalRecord="handleApprovalRecord"
:buttonLoading="buttonLoading"
:id="form.id"
:status="form.status"
:pageType="routeParams.type"
/>
</el-card>
<!-- 表单区域 -->
<el-card class="rounded-lg shadow-sm bg-white border border-gray-100 transition-all hover:shadow-md overflow-hidden">
<div class="p-4 bg-gradient-to-r from-blue-50 to-indigo-50 border-b border-gray-100">
<h3 class="text-lg font-semibold text-gray-800">设计原则</h3>
</div>
<div class="p-6">
<el-form
ref="leaveFormRef"
v-loading="loading"
:disabled="routeParams.type === 'view' || form.status == 'waiting'"
:model="form"
:rules="rules"
label-width="100px"
class="space-y-4"
>
<div class="grid grid-cols-1 gap-4">
<el-row>
<el-col :span="12">
<el-form-item label="文件名称" prop="formNo">
<el-input disabled v-model="form.fileName" placeholder="请输入文件名称" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="文件" prop="formNo">
<div style="display: flex">
<span style="color: rgb(50, 142, 248)">{{ form.originalName }}</span>
<!-- <el-button type="primary" link @click="handleView(scope.row)"
><el-icon><View /></el-icon>查看</el-button
> -->
</div>
</el-form-item>
</el-col>
</el-row>
</div>
</el-form>
</div>
</el-card>
<!-- 提交组件 -->
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
<approvalRecord ref="approvalRecordRef"></approvalRecord>
<!-- 流程选择对话框 -->
<el-dialog
draggable
v-model="dialogVisible.visible"
:title="dialogVisible.title"
:before-close="handleClose"
width="500"
class="rounded-lg shadow-lg"
>
<div class="p-4">
<p class="text-gray-600 mb-4">请选择要启动的流程</p>
<el-select v-model="flowCode" placeholder="请选择流程" style="width: 100%">
<el-option v-for="item in flowCodeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<template #footer>
<div class="dialog-footer p-4 border-t border-gray-100 flex justify-end space-x-3">
<el-button @click="handleClose" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 transition-colors"
>取消</el-button
>
<el-button type="primary" @click="submitFlow()" class="px-4 py-2 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors"
>确认</el-button
>
</div>
</template>
</el-dialog>
</div>
</div>
</template>
<script setup name="Leave" lang="ts">
import { LeaveForm, LeaveQuery, LeaveVO } from '@/api/workflow/leave/types';
import { startWorkFlow } from '@/api/workflow/task';
import SubmitVerify from '@/components/Process/submitVerify.vue';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import ApprovalButton from '@/components/Process/approvalButton.vue';
import { StartProcessBo } from '@/api/workflow/workflowCommon/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
import { useUserStoreHook } from '@/store/modules/user';
const { design_change_reason_type } = toRefs<any>(proxy?.useDict('design_change_reason_type'));
import { getKnowledgeDocument } from '@/api/design/technicalStandard';
// 获取用户 store
const userStore = useUserStoreHook();
// 从 store 中获取项目列表和当前选中的项目
const currentProject = computed(() => userStore.selectedProject);
const buttonLoading = ref(false);
const loading = ref(true);
//路由参数
const routeParams = ref<Record<string, any>>({});
const flowCode = ref<string>('');
const status = ref<string>('');
const dialogVisible = reactive<DialogOption>({
visible: false,
title: '流程定义'
});
//提交组件
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
//审批记录组件
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
//按钮组件
const flowCodeOptions = [
{
value: currentProject.value?.id + '_principletechnical',
label: '设计原则审批'
},
{
value: currentProject.value?.id + '_requirementstechnica',
label: '业主需求清单审批'
}
];
const leaveFormRef = ref<ElFormInstance>();
const dialog = reactive({
visible: false,
title: '',
isEdit: false
});
const submitFormData = ref<StartProcessBo>({
businessId: '',
flowCode: '',
variables: {}
});
const taskVariables = ref<Record<string, any>>({});
const initFormData = {
id: undefined,
fileName: undefined,
fileUrl: undefined,
status: undefined,
originalName: undefined
};
const data = reactive({
form: { ...initFormData },
rules: {}
});
const handleClose = () => {
dialogVisible.visible = false;
flowCode.value = '';
buttonLoading.value = false;
};
const { form, rules } = toRefs(data);
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
leaveFormRef.value?.resetFields();
};
/** 获取详情 */
const getInfo = () => {
loading.value = true;
buttonLoading.value = false;
nextTick(async () => {
const res = await getKnowledgeDocument(routeParams.value.id);
Object.assign(form.value, res.data);
loading.value = false;
buttonLoading.value = false;
});
};
/** 提交按钮 */
const submitForm = (status1: string) => {
status.value = status1;
submit(status.value, form.value);
};
const submitFlow = async () => {
handleStartWorkFlow(form.value);
dialogVisible.visible = false;
};
//提交申请
const handleStartWorkFlow = async (data: LeaveForm) => {
try {
submitFormData.value.flowCode = flowCode.value;
submitFormData.value.businessId = data.id;
//流程变量
taskVariables.value = {
// leave4/5 使用的流程变量
userList: ['1', '3', '4']
};
submitFormData.value.variables = taskVariables.value;
const resp = await startWorkFlow(submitFormData.value);
if (submitVerifyRef.value) {
buttonLoading.value = false;
submitVerifyRef.value.openDialog(resp.data.taskId);
}
} finally {
buttonLoading.value = false;
}
};
//审批记录
const handleApprovalRecord = () => {
approvalRecordRef.value.init(form.value.id);
};
//提交回调
const submitCallback = async () => {
await proxy.$tab.closePage(proxy.$route);
proxy.$router.go(-1);
};
//审批
const approvalVerifyOpen = async () => {
submitVerifyRef.value.openDialog(routeParams.value.taskId);
};
// 图纸上传成功之后 开始提交
const submit = async (status, data) => {
form.value = data;
if (status === 'draft') {
buttonLoading.value = false;
proxy?.$modal.msgSuccess('暂存成功');
proxy.$tab.closePage(proxy.$route);
proxy.$router.go(-1);
} else {
if ((form.value.status === 'draft' && (flowCode.value === '' || flowCode.value === null)) || routeParams.value.type === 'add') {
flowCode.value = flowCodeOptions[0].value;
dialogVisible.visible = true;
return;
}
//说明启动过先随意穿个参数
if (flowCode.value === '' || flowCode.value === null) {
flowCode.value = 'xx';
}
await handleStartWorkFlow(data);
}
};
onMounted(() => {
nextTick(async () => {
routeParams.value = proxy.$route.query;
reset();
loading.value = false;
if (routeParams.value.type === 'update' || routeParams.value.type === 'view' || routeParams.value.type === 'approval') {
getInfo();
}
});
});
</script>
<style scoped lang="scss">
/* 全局样式 */
:root {
--primary: #409eff;
--primary-light: #66b1ff;
--primary-dark: #3a8ee6;
--success: #67c23a;
--warning: #e6a23c;
--danger: #f56c6c;
--info: #909399;
}
/* 表单样式优化 */
.el-form-item {
.el-form-item__label {
color: #606266;
font-weight: 500;
}
.el-input__inner,
.el-select .el-input__inner {
border-radius: 4px;
transition:
border-color 0.2s,
box-shadow 0.2s;
&:focus {
border-color: var(--primary-light);
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
}
}
.el-textarea__inner {
border-radius: 4px;
transition:
border-color 0.2s,
box-shadow 0.2s;
&:focus {
border-color: var(--primary-light);
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
}
}
}
/* 按钮样式优化 */
.el-button {
border-radius: 4px;
transition: all 0.2s;
&.is-primary {
background-color: var(--primary);
border-color: var(--primary);
&:hover {
background-color: var(--primary-light);
border-color: var(--primary-light);
}
&:active {
background-color: var(--primary-dark);
border-color: var(--primary-dark);
}
}
&.is-text {
color: var(--primary);
&:hover {
color: var(--primary-light);
background-color: rgba(64, 158, 255, 0.05);
}
}
}
/* 卡片样式优化 */
.el-card {
transition: all 0.3s ease;
&:hover {
/* transform: translateY(-2px); */
}
}
/* 对话框样式优化 */
.el-dialog {
.el-dialog__header {
background-color: #f5f7fa;
border-bottom: 1px solid #ebeef5;
padding: 15px 20px;
}
.el-dialog__title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
.el-dialog__footer {
padding: 15px 20px;
border-top: 1px solid #ebeef5;
}
}
</style>