预览文件统一修改
This commit is contained in:
@@ -8,7 +8,7 @@ import { getColorWithOpacity, tagTextColor, tagBgColor, tagBorderColor, download
|
|||||||
import { AiError, aiTypeJudge } from "@/service/api/ai";
|
import { AiError, aiTypeJudge } from "@/service/api/ai";
|
||||||
import { systemConfigApi } from '@/service/api/system';
|
import { systemConfigApi } from '@/service/api/system';
|
||||||
import { getChangeDetailApi } from '@/service/api/change';
|
import { getChangeDetailApi } from '@/service/api/change';
|
||||||
import previewComponent from '@/components/previewFile/index.vue'
|
import PreviewComponent from '@/components/previewFile/index.vue'
|
||||||
import { rejectApi,agreeApi } from '@/service/api/myTasks';
|
import { rejectApi,agreeApi } from '@/service/api/myTasks';
|
||||||
|
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { uploadFileApi, deleteFileApi, fileListApi } from "@/service/api/file";
|
|||||||
import { systemConfigApi, peopleListApi, templateListApi } from '@/service/api/system';
|
import { systemConfigApi, peopleListApi, templateListApi } from '@/service/api/system';
|
||||||
import { getChangeDetailApi } from '@/service/api/change';
|
import { getChangeDetailApi } from '@/service/api/change';
|
||||||
import { myTasksCloseMaterialApi } from '@/service/api/myTasks';
|
import { myTasksCloseMaterialApi } from '@/service/api/myTasks';
|
||||||
import previewComponent from '@/components/previewFile/index.vue'
|
import PreviewComponent from '@/components/previewFile/index.vue'
|
||||||
|
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const { routerPushByKey } = useRouterPush();
|
const { routerPushByKey } = useRouterPush();
|
||||||
@@ -170,7 +170,7 @@ const chooseFiles = () => {
|
|||||||
const ids = new Set(checkedFiles.value);
|
const ids = new Set(checkedFiles.value);
|
||||||
const result = fileList.value.filter((item:any) => ids.has(item.id));
|
const result = fileList.value.filter((item:any) => ids.has(item.id));
|
||||||
// 合并文件列表
|
// 合并文件列表
|
||||||
let files = list.value[currentRowIndex.value]?.attachments.concat(result);
|
const files = list.value[currentRowIndex.value]?.attachments.concat(result);
|
||||||
// 按id去重
|
// 按id去重
|
||||||
const uniqueList = [
|
const uniqueList = [
|
||||||
...new Map(files.map((item:any) => [item.id, item])).values(),
|
...new Map(files.map((item:any) => [item.id, item])).values(),
|
||||||
@@ -349,7 +349,7 @@ const togglePicker = (s: any) => {
|
|||||||
// 添加审批步骤人员
|
// 添加审批步骤人员
|
||||||
const addMember = (s: any, v: string | null) => {
|
const addMember = (s: any, v: string | null) => {
|
||||||
if (v && !s.members.some((m:any) => m.user_name === v)){
|
if (v && !s.members.some((m:any) => m.user_name === v)){
|
||||||
let info = findUserInTree(memberList.value, v);
|
const info = findUserInTree(memberList.value, v);
|
||||||
s.members.push({ user_id: Number(v), user_name: info?.user_name || '', org_id: info?.org_id || '', org_name: info?.org_name || '', specialty: "", is_sign: 0 });
|
s.members.push({ user_id: Number(v), user_name: info?.user_name || '', org_id: info?.org_id || '', org_name: info?.org_name || '', specialty: "", is_sign: 0 });
|
||||||
pick.value = null;
|
pick.value = null;
|
||||||
pickerStepId.value = null;
|
pickerStepId.value = null;
|
||||||
@@ -397,7 +397,7 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NSpace vertical :size="16">
|
<NSpace vertical :size="16">
|
||||||
<!-- 资料关闭详情-->
|
<!-- 资料关闭详情-->
|
||||||
<NSpin :show="loading" size="small">
|
<NSpin :show="loading" size="small">
|
||||||
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
|
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px' }">
|
||||||
@@ -434,13 +434,13 @@ onMounted(async () => {
|
|||||||
class="text-11px flex items-center gap-2 border py-0.5 px-1.5 rounded-md text-[#17407F] border-[#ACC6E5] bg-[#EAF0F9]"
|
class="text-11px flex items-center gap-2 border py-0.5 px-1.5 rounded-md text-[#17407F] border-[#ACC6E5] bg-[#EAF0F9]"
|
||||||
>
|
>
|
||||||
<Icon icon="akar-icons:file" class="size-12px" />
|
<Icon icon="akar-icons:file" class="size-12px" />
|
||||||
{{file.file_name}}
|
{{ file.file_name }}
|
||||||
<NButton text type="primary" class="text-11px" @click="preview(file)">预览</NButton>
|
<NButton text type="primary" class="text-11px" @click="preview(file)">预览</NButton>
|
||||||
<NButton text type="primary" class="text-11px" @click="downloadFile(file,'train')">下载</NButton>
|
<NButton text type="primary" class="text-11px" @click="downloadFile(file,'train')">下载</NButton>
|
||||||
<NPopconfirm
|
<NPopconfirm
|
||||||
positive-text="确定"
|
positive-text="确定"
|
||||||
:positiveButtonProps="{ size: 'tiny' }"
|
:positive-button-props="{ size: 'tiny' }"
|
||||||
:negativeButtonProps="{ size: 'tiny' }"
|
:negative-button-props="{ size: 'tiny' }"
|
||||||
@positive-click="removeFileReal('3', Number(fileIndex))"
|
@positive-click="removeFileReal('3', Number(fileIndex))"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
@@ -456,8 +456,8 @@ onMounted(async () => {
|
|||||||
下载培训签到表
|
下载培训签到表
|
||||||
</NButton>
|
</NButton>
|
||||||
<NUpload
|
<NUpload
|
||||||
class="w-auto"
|
|
||||||
:key="trainFiles.length?'train_'+trainFiles.length+1:'train'"
|
:key="trainFiles.length?'train_'+trainFiles.length+1:'train'"
|
||||||
|
class="w-auto"
|
||||||
accept="image/*,.pdf,.docx,.xlsx,.pptx"
|
accept="image/*,.pdf,.docx,.xlsx,.pptx"
|
||||||
:default-upload="false"
|
:default-upload="false"
|
||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
@@ -481,13 +481,13 @@ onMounted(async () => {
|
|||||||
class="text-11px flex items-center gap-2 border py-0.5 px-1.5 rounded-md text-[#17407F] border-[#ACC6E5] bg-[#EAF0F9]"
|
class="text-11px flex items-center gap-2 border py-0.5 px-1.5 rounded-md text-[#17407F] border-[#ACC6E5] bg-[#EAF0F9]"
|
||||||
>
|
>
|
||||||
<Icon icon="akar-icons:file" class="size-12px" />
|
<Icon icon="akar-icons:file" class="size-12px" />
|
||||||
{{file.file_name}}
|
{{ file.file_name }}
|
||||||
<NButton text type="primary" class="text-11px" @click="preview(file)">预览</NButton>
|
<NButton text type="primary" class="text-11px" @click="preview(file)">预览</NButton>
|
||||||
<NButton text type="primary" class="text-11px" @click="downloadFile(file,'pssr')">下载</NButton>
|
<NButton text type="primary" class="text-11px" @click="downloadFile(file,'pssr')">下载</NButton>
|
||||||
<NPopconfirm
|
<NPopconfirm
|
||||||
positive-text="确定"
|
positive-text="确定"
|
||||||
:positiveButtonProps="{ size: 'tiny' }"
|
:positive-button-props="{ size: 'tiny' }"
|
||||||
:negativeButtonProps="{ size: 'tiny' }"
|
:negative-button-props="{ size: 'tiny' }"
|
||||||
@positive-click="removeFileReal('4', Number(fileIndex))"
|
@positive-click="removeFileReal('4', Number(fileIndex))"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
@@ -503,8 +503,8 @@ onMounted(async () => {
|
|||||||
下载PSSR检查表
|
下载PSSR检查表
|
||||||
</NButton>
|
</NButton>
|
||||||
<NUpload
|
<NUpload
|
||||||
class="w-auto"
|
|
||||||
:key="pssrFiles.length?'pssr_'+pssrFiles.length+1:'pssr'"
|
:key="pssrFiles.length?'pssr_'+pssrFiles.length+1:'pssr'"
|
||||||
|
class="w-auto"
|
||||||
accept="image/*,.pdf,.docx,.xlsx,.pptx"
|
accept="image/*,.pdf,.docx,.xlsx,.pptx"
|
||||||
:default-upload="false"
|
:default-upload="false"
|
||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
@@ -551,13 +551,13 @@ onMounted(async () => {
|
|||||||
class="text-11px flex items-center gap-2 border py-0.5 px-1.5 rounded-md text-[#17407F] border-[#ACC6E5] bg-[#EAF0F9]"
|
class="text-11px flex items-center gap-2 border py-0.5 px-1.5 rounded-md text-[#17407F] border-[#ACC6E5] bg-[#EAF0F9]"
|
||||||
>
|
>
|
||||||
<Icon icon="akar-icons:file" class="size-12px" />
|
<Icon icon="akar-icons:file" class="size-12px" />
|
||||||
{{file.file_name}}
|
{{ file.file_name }}
|
||||||
<NButton text type="primary" class="text-11px" @click="preview(file)">预览</NButton>
|
<NButton text type="primary" class="text-11px" @click="preview(file)">预览</NButton>
|
||||||
<NButton text type="primary" class="text-11px" @click="downloadFile(file,'accept')">下载</NButton>
|
<NButton text type="primary" class="text-11px" @click="downloadFile(file,'accept')">下载</NButton>
|
||||||
<NPopconfirm
|
<NPopconfirm
|
||||||
positive-text="确定"
|
positive-text="确定"
|
||||||
:positiveButtonProps="{ size: 'tiny' }"
|
:positive-button-props="{ size: 'tiny' }"
|
||||||
:negativeButtonProps="{ size: 'tiny' }"
|
:negative-button-props="{ size: 'tiny' }"
|
||||||
@positive-click="removeFileReal('5', Number(fileIndex))"
|
@positive-click="removeFileReal('5', Number(fileIndex))"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
@@ -573,8 +573,8 @@ onMounted(async () => {
|
|||||||
下载验收表
|
下载验收表
|
||||||
</NButton>
|
</NButton>
|
||||||
<NUpload
|
<NUpload
|
||||||
class="w-auto"
|
|
||||||
:key="acceptFiles.length?'accept_'+acceptFiles.length+1:'accept'"
|
:key="acceptFiles.length?'accept_'+acceptFiles.length+1:'accept'"
|
||||||
|
class="w-auto"
|
||||||
accept="image/*,.pdf,.docx,.xlsx,.pptx"
|
accept="image/*,.pdf,.docx,.xlsx,.pptx"
|
||||||
:default-upload="false"
|
:default-upload="false"
|
||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
@@ -622,7 +622,7 @@ onMounted(async () => {
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="(item, index) in list" :key="index">
|
<tr v-for="(item, index) in list" :key="index">
|
||||||
<td>
|
<td>
|
||||||
<p>{{item.doc_name}}</p>
|
<p>{{ item.doc_name }}</p>
|
||||||
<div v-if="item.attachments.length" class="flex flex-wrap gap-2">
|
<div v-if="item.attachments.length" class="flex flex-wrap gap-2">
|
||||||
<div
|
<div
|
||||||
v-for="(file, fileIndex) in item.attachments"
|
v-for="(file, fileIndex) in item.attachments"
|
||||||
@@ -630,13 +630,13 @@ onMounted(async () => {
|
|||||||
class="text-11px flex items-center gap-2 border py-0.5 px-1.5 rounded-md text-[#17407F] border-[#ACC6E5] bg-[#EAF0F9]"
|
class="text-11px flex items-center gap-2 border py-0.5 px-1.5 rounded-md text-[#17407F] border-[#ACC6E5] bg-[#EAF0F9]"
|
||||||
>
|
>
|
||||||
<Icon icon="akar-icons:file" class="size-12px" />
|
<Icon icon="akar-icons:file" class="size-12px" />
|
||||||
{{file.file_name}}
|
{{ file.file_name }}
|
||||||
<NButton text type="primary" class="text-11px" @click="preview(file)">预览</NButton>
|
<NButton text type="primary" class="text-11px" @click="preview(file)">预览</NButton>
|
||||||
<NButton text type="primary" class="text-11px" @click="downloadFile(file,'close')">下载</NButton>
|
<NButton text type="primary" class="text-11px" @click="downloadFile(file,'close')">下载</NButton>
|
||||||
<NPopconfirm
|
<NPopconfirm
|
||||||
positive-text="确定"
|
positive-text="确定"
|
||||||
:positiveButtonProps="{ size: 'tiny' }"
|
:positive-button-props="{ size: 'tiny' }"
|
||||||
:negativeButtonProps="{ size: 'tiny' }"
|
:negative-button-props="{ size: 'tiny' }"
|
||||||
@positive-click="removeFileReal(item.doc_name, Number(fileIndex), Number(index))"
|
@positive-click="removeFileReal(item.doc_name, Number(fileIndex), Number(index))"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
@@ -657,8 +657,8 @@ onMounted(async () => {
|
|||||||
<div v-if="item.status === 1" class="flex justify-center gap-2">
|
<div v-if="item.status === 1" class="flex justify-center gap-2">
|
||||||
<NButton size="small" class="text-xs" @click="openFileList(Number(index))">从变更附件选择</NButton>
|
<NButton size="small" class="text-xs" @click="openFileList(Number(index))">从变更附件选择</NButton>
|
||||||
<NUpload
|
<NUpload
|
||||||
class="w-auto"
|
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
|
class="w-auto"
|
||||||
accept="image/*,.pdf,.docx,.xlsx,.pptx"
|
accept="image/*,.pdf,.docx,.xlsx,.pptx"
|
||||||
:default-upload="false"
|
:default-upload="false"
|
||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
@@ -697,12 +697,12 @@ onMounted(async () => {
|
|||||||
<p class="text-xs leading-relaxed text-slate-400">审批链:{{ approvalFlowText }}。专业会签人由申请表「涉及专业」勾选自动增减,可在此调整人选;重要变更自动追加安全审查与分管领导终批。</p>
|
<p class="text-xs leading-relaxed text-slate-400">审批链:{{ approvalFlowText }}。专业会签人由申请表「涉及专业」勾选自动增减,可在此调整人选;重要变更自动追加安全审查与分管领导终批。</p>
|
||||||
<div v-for="(s, idx) in flowList" :key="s.id" class="rounded-lg border border-slate-100 bg-slate-50/40 p-3">
|
<div v-for="(s, idx) in flowList" :key="s.id" class="rounded-lg border border-slate-100 bg-slate-50/40 p-3">
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<NTag type="info" size="small">{{Number(idx)+1}}</NTag>
|
<NTag type="info" size="small">{{ Number(idx)+1 }}</NTag>
|
||||||
<p class="text-sm">{{s.name}}</p>
|
<p class="text-sm">{{ s.name }}</p>
|
||||||
<NCheckbox :checked="s.countersign" size="small" @update:checked="(v) => setStepCountersign(s, v)">
|
<NCheckbox :checked="s.countersign" size="small" @update:checked="(v) => setStepCountersign(s, v)">
|
||||||
<span class="text-xs text-slate-500">专业会签步骤</span>
|
<span class="text-xs text-slate-500">专业会签步骤</span>
|
||||||
</NCheckbox>
|
</NCheckbox>
|
||||||
<NButton ghost type="primary" size="tiny" @click="togglePicker(s)" title="添加预设人员" class="ml-auto">
|
<NButton ghost type="primary" size="tiny" title="添加预设人员" class="ml-auto" @click="togglePicker(s)">
|
||||||
<Icon icon="ic:round-plus" class="size-16px" />
|
<Icon icon="ic:round-plus" class="size-16px" />
|
||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
@@ -714,11 +714,13 @@ onMounted(async () => {
|
|||||||
<span class="shrink-0 truncate text-xs font-medium text-slate-700">{{ m.user_name }}</span>
|
<span class="shrink-0 truncate text-xs font-medium text-slate-700">{{ m.user_name }}</span>
|
||||||
<template v-if="s.countersign">
|
<template v-if="s.countersign">
|
||||||
<span class="text-xs text-slate-400"><span class="text-red-500">*</span>专业:</span>
|
<span class="text-xs text-slate-400"><span class="text-red-500">*</span>专业:</span>
|
||||||
<NSelect :value="m.specialty || null" clearable size="small" placeholder="请选择专业" class="w-28"
|
<NSelect
|
||||||
|
:value="m.specialty || null" clearable size="small" placeholder="请选择专业" class="w-28"
|
||||||
:options="SPECIALTIES"
|
:options="SPECIALTIES"
|
||||||
@update:value="(v) => selectSpecialty(Number(idx), Number(mIdx), v)"
|
@update:value="(v) => selectSpecialty(Number(idx), Number(mIdx), v)"
|
||||||
/>
|
/>
|
||||||
<NCheckbox :checked="m.is_sign" size="small"
|
<NCheckbox
|
||||||
|
:checked="m.is_sign" size="small"
|
||||||
:checked-value="1"
|
:checked-value="1"
|
||||||
:unchecked-value="0"
|
:unchecked-value="0"
|
||||||
@update:checked="(v) => selectSign(Number(idx), Number(mIdx), v)"
|
@update:checked="(v) => selectSign(Number(idx), Number(mIdx), v)"
|
||||||
@@ -726,16 +728,18 @@ onMounted(async () => {
|
|||||||
<span class="text-xs text-slate-500">必须参与会签</span>
|
<span class="text-xs text-slate-500">必须参与会签</span>
|
||||||
</NCheckbox>
|
</NCheckbox>
|
||||||
</template>
|
</template>
|
||||||
<NButton text type="error" @click="delMember(s, m.user_id)" title="删除该人员">
|
<NButton text type="error" title="删除该人员" @click="delMember(s, m.user_id)">
|
||||||
<Icon icon="iconamoon:trash" class="size-16px" />
|
<Icon icon="iconamoon:trash" class="size-16px" />
|
||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="s.countersign && s.members.length" class="text-[11px] leading-5 text-slate-400">
|
<p v-if="s.countersign && s.members.length" class="text-[11px] leading-5 text-slate-400">
|
||||||
必须参与会签:勾选的人员必须参与专业会签,未勾选的人员为可选会签(按需参加)
|
必须参与会签:勾选的人员必须参与专业会签,未勾选的人员为可选会签(按需参加)
|
||||||
</p>
|
</p>
|
||||||
<NCascader v-if="pickerStepId === s.id" :value="pick" :options="memberList"
|
<NCascader
|
||||||
|
v-if="pickerStepId === s.id" :value="pick" :options="memberList"
|
||||||
placeholder="请选择人员" size="small" check-strategy="child" :show-path="false" expand-trigger="click"
|
placeholder="请选择人员" size="small" check-strategy="child" :show-path="false" expand-trigger="click"
|
||||||
class="w-56" :on-update:value="(v) => addMember(s, v)" />
|
class="w-56" :on-update:value="(v) => addMember(s, v)"
|
||||||
|
/>
|
||||||
|
|
||||||
<span v-if="s.members.length === 0" class="text-xs leading-7 text-slate-300">未预设</span>
|
<span v-if="s.members.length === 0" class="text-xs leading-7 text-slate-300">未预设</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -795,13 +799,13 @@ onMounted(async () => {
|
|||||||
<span>上传时间:{{ previewFile?.created_at }}</span>
|
<span>上传时间:{{ previewFile?.created_at }}</span>
|
||||||
<span v-if="previewFile?.version">{{ `版本:${previewFile?.version}(受控)` }}</span>
|
<span v-if="previewFile?.version">{{ `版本:${previewFile?.version}(受控)` }}</span>
|
||||||
</div>
|
</div>
|
||||||
<previewComponent v-if="previewFile" :previewFile="previewFile" />
|
<PreviewComponent v-if="previewFile" :preview-file="previewFile" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</NModal>
|
</NModal>
|
||||||
</NCard>
|
</NCard>
|
||||||
</NSpin>
|
</NSpin>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { AiError, aiTypeJudge } from "@/service/api/ai";
|
|||||||
import { systemConfigApi, peopleListApi, templateListApi } from '@/service/api/system';
|
import { systemConfigApi, peopleListApi, templateListApi } from '@/service/api/system';
|
||||||
import { getChangeDetailApi, urgeApi } from '@/service/api/change';
|
import { getChangeDetailApi, urgeApi } from '@/service/api/change';
|
||||||
import { myTasksDisposalSubmitApi, myTasksAddItemApi } from '@/service/api/myTasks';
|
import { myTasksDisposalSubmitApi, myTasksAddItemApi } from '@/service/api/myTasks';
|
||||||
import previewComponent from '@/components/previewFile/index.vue'
|
import PreviewComponent from '@/components/previewFile/index.vue'
|
||||||
|
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const { routerPushByKey } = useRouterPush();
|
const { routerPushByKey } = useRouterPush();
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { tagTextColor, tagBgColor, tagBorderColor, downloadFileFun } from '@/uti
|
|||||||
import { AiError, aiTypeJudge } from "@/service/api/ai";
|
import { AiError, aiTypeJudge } from "@/service/api/ai";
|
||||||
import { systemConfigApi } from '@/service/api/system';
|
import { systemConfigApi } from '@/service/api/system';
|
||||||
import { getChangeDetailApi, urgeApi } from '@/service/api/change';
|
import { getChangeDetailApi, urgeApi } from '@/service/api/change';
|
||||||
import previewComponent from '@/components/previewFile/index.vue'
|
import PreviewComponent from '@/components/previewFile/index.vue'
|
||||||
|
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const { routerPushByKey } = useRouterPush();
|
const { routerPushByKey } = useRouterPush();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ref, onMounted, h } from 'vue';
|
|||||||
import { Icon } from '@iconify/vue'
|
import { Icon } from '@iconify/vue'
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { NButton, NTag, NDropdown, useDialog } from 'naive-ui';
|
import { NButton, NTag, NDropdown, useDialog } from 'naive-ui';
|
||||||
import previewComponent from '@/components/previewFile/index.vue'
|
import PreviewComponent from '@/components/previewFile/index.vue'
|
||||||
import { downloadFileFun } from '@/utils/common';
|
import { downloadFileFun } from '@/utils/common';
|
||||||
import {
|
import {
|
||||||
docTreeListApi,
|
docTreeListApi,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ref, onMounted, h } from 'vue';
|
|||||||
import { Icon } from '@iconify/vue'
|
import { Icon } from '@iconify/vue'
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { formatTimestamp, getTimeRangeStamp, downloadFileFun } from "@/utils/common";
|
import { formatTimestamp, getTimeRangeStamp, downloadFileFun } from "@/utils/common";
|
||||||
import previewComponent from '@/components/previewFile/index.vue'
|
import PreviewComponent from '@/components/previewFile/index.vue'
|
||||||
import { systemConfigApi } from '@/service/api/system';
|
import { systemConfigApi } from '@/service/api/system';
|
||||||
import { orgListApi } from '@/service/api/user';
|
import { orgListApi } from '@/service/api/user';
|
||||||
import { docArchiveTreeListApi, docArchiveDetailsApi } from "@/service/api/doc";
|
import { docArchiveTreeListApi, docArchiveDetailsApi } from "@/service/api/doc";
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useRouterPush } from '@/hooks/common/router';
|
|||||||
import { getColorWithOpacity, tagTextColor, tagBgColor, tagBorderColor, downloadFileFun } from '@/utils/common';
|
import { getColorWithOpacity, tagTextColor, tagBgColor, tagBorderColor, downloadFileFun } from '@/utils/common';
|
||||||
import { systemConfigApi } from "@/service/api/system";
|
import { systemConfigApi } from "@/service/api/system";
|
||||||
import { homeSafeApi } from "@/service/api/statistics";
|
import { homeSafeApi } from "@/service/api/statistics";
|
||||||
import previewComponent from '@/components/previewFile/index.vue'
|
import PreviewComponent from '@/components/previewFile/index.vue'
|
||||||
|
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
const { routerPushByKey } = useRouterPush();
|
const { routerPushByKey } = useRouterPush();
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { localStg } from '@/utils/storage';
|
|||||||
import { AiError, aiTypeJudge, aiLevelScore, aiApplication } from "@/service/api/ai";
|
import { AiError, aiTypeJudge, aiLevelScore, aiApplication } from "@/service/api/ai";
|
||||||
import { uploadFileApi, deleteFileApi } from "@/service/api/file";
|
import { uploadFileApi, deleteFileApi } from "@/service/api/file";
|
||||||
import Footer from './footer.vue'
|
import Footer from './footer.vue'
|
||||||
import previewComponent from '@/components/previewFile/index.vue'
|
import PreviewComponent from '@/components/previewFile/index.vue'
|
||||||
|
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ const filterMembersBySpecialties = (steps: any, specialties: string[]) => {
|
|||||||
}
|
}
|
||||||
// 更新当前模板步骤
|
// 更新当前模板步骤
|
||||||
const updateCurrentTemplate = () => {
|
const updateCurrentTemplate = () => {
|
||||||
let template = props.templateList.find((item: any) =>
|
const template = props.templateList.find((item: any) =>
|
||||||
item.level === form.value?.change_level &&
|
item.level === form.value?.change_level &&
|
||||||
item.category === form.value?.change_type &&
|
item.category === form.value?.change_type &&
|
||||||
item.duration === form.value?.duration_type
|
item.duration === form.value?.duration_type
|
||||||
@@ -266,7 +266,7 @@ const typeAi = ref<{ type: string; confidence: number; conclusion: string; hit_r
|
|||||||
const typeAiLoading = ref<boolean>(false);
|
const typeAiLoading = ref<boolean>(false);
|
||||||
// 打开变更类型抽屉
|
// 打开变更类型抽屉
|
||||||
const openTypeSheet = async () => {
|
const openTypeSheet = async () => {
|
||||||
let form = props.formInfo;
|
const form = props.formInfo;
|
||||||
if (!form?.description) {
|
if (!form?.description) {
|
||||||
return window.$message?.warning("缺少变更描述,请先在 [变更预识别] 里面输入变更描述");
|
return window.$message?.warning("缺少变更描述,请先在 [变更预识别] 里面输入变更描述");
|
||||||
}
|
}
|
||||||
@@ -296,7 +296,7 @@ const levelAiBasis = ref<string[]>([]);
|
|||||||
const levelAiLoading = ref<boolean>(false);
|
const levelAiLoading = ref<boolean>(false);
|
||||||
// 打开等级判定表抽屉
|
// 打开等级判定表抽屉
|
||||||
const openLevelSheet = async () => {
|
const openLevelSheet = async () => {
|
||||||
let form = props.formInfo;
|
const form = props.formInfo;
|
||||||
if (!form?.description && props.isAiOpen) {
|
if (!form?.description && props.isAiOpen) {
|
||||||
return window.$message?.warning("缺少变更描述,请先在 [变更预识别] 里面输入变更描述");
|
return window.$message?.warning("缺少变更描述,请先在 [变更预识别] 里面输入变更描述");
|
||||||
}
|
}
|
||||||
@@ -305,7 +305,7 @@ const openLevelSheet = async () => {
|
|||||||
};
|
};
|
||||||
// 使用AI 评分
|
// 使用AI 评分
|
||||||
const useAiLevel = async () => {
|
const useAiLevel = async () => {
|
||||||
let form = props.formInfo;
|
const form = props.formInfo;
|
||||||
if (levelAiLoading.value) return;
|
if (levelAiLoading.value) return;
|
||||||
levelAiLoading.value = true;
|
levelAiLoading.value = true;
|
||||||
try {
|
try {
|
||||||
@@ -447,7 +447,7 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<div class="space-y-4 scroll">
|
<div class="space-y-4 scroll">
|
||||||
<!-- 申请部门 / 申请人 -->
|
<!-- 申请部门 / 申请人 -->
|
||||||
<div class="grid gap-4 grid-cols-2">
|
<div class="grid gap-4 grid-cols-2">
|
||||||
@@ -482,7 +482,7 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
|
|||||||
<!-- 预期效果 -->
|
<!-- 预期效果 -->
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">预期效果</div>
|
<div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">预期效果</div>
|
||||||
<NInput type="textarea" v-model:value="form.effect" rows="2" placeholder="" />
|
<NInput v-model:value="form.effect" type="textarea" rows="2" placeholder="" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 主要变更内容 -->
|
<!-- 主要变更内容 -->
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
@@ -494,8 +494,8 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
|
|||||||
<NPopconfirm
|
<NPopconfirm
|
||||||
v-if="form.main_changes.length > 1"
|
v-if="form.main_changes.length > 1"
|
||||||
positive-text="确定"
|
positive-text="确定"
|
||||||
:positiveButtonProps="{ size: 'tiny' }"
|
:positive-button-props="{ size: 'tiny' }"
|
||||||
:negativeButtonProps="{ size: 'tiny' }"
|
:negative-button-props="{ size: 'tiny' }"
|
||||||
@positive-click="form.main_changes.splice(i, 1)"
|
@positive-click="form.main_changes.splice(i, 1)"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
@@ -521,8 +521,8 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
|
|||||||
<NPopconfirm
|
<NPopconfirm
|
||||||
v-if="form.related_changes.length > 1"
|
v-if="form.related_changes.length > 1"
|
||||||
positive-text="确定"
|
positive-text="确定"
|
||||||
:positiveButtonProps="{ size: 'tiny' }"
|
:positive-button-props="{ size: 'tiny' }"
|
||||||
:negativeButtonProps="{ size: 'tiny' }"
|
:negative-button-props="{ size: 'tiny' }"
|
||||||
@positive-click="form.related_changes.splice(i, 1)"
|
@positive-click="form.related_changes.splice(i, 1)"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
@@ -546,7 +546,7 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
|
|||||||
<div class="flex flex-1 items-center gap-4 rounded-lg bg-slate-50 px-3 py-2">
|
<div class="flex flex-1 items-center gap-4 rounded-lg bg-slate-50 px-3 py-2">
|
||||||
<NRadioGroup v-model:value="form.change_type">
|
<NRadioGroup v-model:value="form.change_type">
|
||||||
<div class="flex gap-x-2">
|
<div class="flex gap-x-2">
|
||||||
<NRadio v-for="t in props.typeList" :key="t.value" :value="t.value">{{t.label}}</NRadio>
|
<NRadio v-for="t in props.typeList" :key="t.value" :value="t.value">{{ t.label }}</NRadio>
|
||||||
</div>
|
</div>
|
||||||
</NRadioGroup>
|
</NRadioGroup>
|
||||||
<NButton v-if="props.isAiOpen" text type="primary" class="ml-auto text-xs hover:underline" @click="openTypeSheet">
|
<NButton v-if="props.isAiOpen" text type="primary" class="ml-auto text-xs hover:underline" @click="openTypeSheet">
|
||||||
@@ -561,7 +561,7 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
|
|||||||
<div class="flex flex-1 items-center gap-4 rounded-lg bg-slate-50 px-3 py-2">
|
<div class="flex flex-1 items-center gap-4 rounded-lg bg-slate-50 px-3 py-2">
|
||||||
<NRadioGroup v-model:value="form.change_level" disabled>
|
<NRadioGroup v-model:value="form.change_level" disabled>
|
||||||
<div class="flex gap-x-2">
|
<div class="flex gap-x-2">
|
||||||
<NRadio v-for="t in props.levelList" :key="t.value" :value="t.value">{{t.label}}</NRadio>
|
<NRadio v-for="t in props.levelList" :key="t.value" :value="t.value">{{ t.label }}</NRadio>
|
||||||
</div>
|
</div>
|
||||||
</NRadioGroup>
|
</NRadioGroup>
|
||||||
<NButton text type="primary" class="ml-auto text-xs hover:underline" @click="openLevelSheet">
|
<NButton text type="primary" class="ml-auto text-xs hover:underline" @click="openLevelSheet">
|
||||||
@@ -588,14 +588,15 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
|
|||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">风险分析</div>
|
<div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">风险分析</div>
|
||||||
<div class="flex flex-1 flex-wrap items-center gap-2 rounded-lg bg-slate-50 px-3 py-2">
|
<div class="flex flex-1 flex-wrap items-center gap-2 rounded-lg bg-slate-50 px-3 py-2">
|
||||||
<NButton size="small" round v-for="t in RISK_TOOLS_FORM" :key="t.key" @click="toggleTool(t)" title="点击选用 / 取消;AI 辅助分析在各工具插件内使用"
|
<NButton
|
||||||
class="text-sm"
|
v-for="t in RISK_TOOLS_FORM" :key="t.key" size="small" round title="点击选用 / 取消;AI 辅助分析在各工具插件内使用" class="text-sm"
|
||||||
:ghost="form.risk_tools?.includes(t.key) ? false : true"
|
:ghost="form.risk_tools?.includes(t.key) ? false : true"
|
||||||
:type="form.risk_tools?.includes(t.key) ? 'primary' : 'default'"
|
:type="form.risk_tools?.includes(t.key) ? 'primary' : 'default'"
|
||||||
|
@click="toggleTool(t)"
|
||||||
>
|
>
|
||||||
{{ t.label }}
|
{{ t.label }}
|
||||||
</NButton>
|
</NButton>
|
||||||
<NButton type="primary" ghost dashed size="small" round @click="recommendTools" class="text-xs">
|
<NButton type="primary" ghost dashed size="small" round class="text-xs" @click="recommendTools">
|
||||||
按等级重新推荐
|
按等级重新推荐
|
||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
@@ -623,7 +624,7 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
|
|||||||
<div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">所需材料</div>
|
<div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">所需材料</div>
|
||||||
<div class="flex-1 space-y-2">
|
<div class="flex-1 space-y-2">
|
||||||
<div class="text-[11px] text-slate-400">施工所需材料(生成变更申请表时系统自动生成,支持手动修改)</div>
|
<div class="text-[11px] text-slate-400">施工所需材料(生成变更申请表时系统自动生成,支持手动修改)</div>
|
||||||
<NInput type="textarea" v-model:value="form.materials" rows="4" placeholder="点击「生成变更申请单」后由系统按变更内容自动生成,也可直接填写" />
|
<NInput v-model:value="form.materials" type="textarea" rows="4" placeholder="点击「生成变更申请单」后由系统按变更内容自动生成,也可直接填写" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 资料上传 -->
|
<!-- 资料上传 -->
|
||||||
@@ -631,9 +632,9 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
|
|||||||
<div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">资料上传</div>
|
<div class="w-24 shrink-0 pt-2 text-sm font-medium text-slate-600 text-right">资料上传</div>
|
||||||
<div class="flex flex-1 flex-wrap items-center gap-2 rounded-lg border border-dashed p-3">
|
<div class="flex flex-1 flex-wrap items-center gap-2 rounded-lg border border-dashed p-3">
|
||||||
<div
|
<div
|
||||||
class="relative"
|
v-for="(f,index) in uploadFile"
|
||||||
:style="{'--theme-color':themeStore.themeColor}"
|
:key="f.id"
|
||||||
v-for="(f,index) in uploadFile" :key="f.id"
|
class="relative" :style="{'--theme-color':themeStore.themeColor}"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="text-xs cursor-pointer flex items-center border py-1.5 px-2 rounded-md hover:border-[var(--theme-color)] hover:text-[var(--theme-color)]"
|
class="text-xs cursor-pointer flex items-center border py-1.5 px-2 rounded-md hover:border-[var(--theme-color)] hover:text-[var(--theme-color)]"
|
||||||
@@ -644,8 +645,8 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
|
|||||||
</div>
|
</div>
|
||||||
<NPopconfirm
|
<NPopconfirm
|
||||||
positive-text="确定"
|
positive-text="确定"
|
||||||
:positiveButtonProps="{ size: 'tiny' }"
|
:positive-button-props="{ size: 'tiny' }"
|
||||||
:negativeButtonProps="{ size: 'tiny' }"
|
:negative-button-props="{ size: 'tiny' }"
|
||||||
@positive-click="removeFile(index)"
|
@positive-click="removeFile(index)"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
@@ -691,30 +692,30 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="signers && signers.length" class="mt-1.5 flex flex-wrap items-center gap-1.5 text-xs text-slate-500">
|
<div v-if="signers && signers.length" class="mt-1.5 flex flex-wrap items-center gap-1.5 text-xs text-slate-500">
|
||||||
<Icon icon="lucide:users" class="size-12px" />已联动专业会签:
|
<Icon icon="lucide:users" class="size-12px" />已联动专业会签:
|
||||||
<NTag size="small" type="info" v-for="(p, d) in signers" :key="d">{{ p?.member?.specialty }} · {{ p?.member?.user_name }}{{p?.stepName ? ` (${p.stepName})` : ''}}</NTag>
|
<NTag v-for="(p, d) in signers" :key="d" size="small" type="info">{{ p?.member?.specialty }} · {{ p?.member?.user_name }}{{ p?.stepName ? ` (${p.stepName})` : '' }}</NTag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Footer
|
<Footer
|
||||||
ref="footerRef"
|
ref="footerRef"
|
||||||
:type="props.type"
|
:type="props.type"
|
||||||
:currentId="props.currentId"
|
:current-id="props.currentId"
|
||||||
:title="props.title"
|
:title="props.title"
|
||||||
:isAiOpen="props.isAiOpen"
|
:is-ai-open="props.isAiOpen"
|
||||||
:currentForm="form"
|
:current-form="form"
|
||||||
:confirmed="props.confirmed"
|
:confirmed="props.confirmed"
|
||||||
:members="props.members"
|
:members="props.members"
|
||||||
:steps="currentSteps"
|
:steps="currentSteps"
|
||||||
@save="saveDraft"
|
@save="saveDraft"
|
||||||
@submit="submitConfirm"
|
@submit="submitConfirm"
|
||||||
@confirmTab="confirmTab"
|
@confirm-tab="confirmTab"
|
||||||
@regenerate="onRegenerate"
|
@regenerate="onRegenerate"
|
||||||
@update:steps="updateSteps"
|
@update:steps="updateSteps"
|
||||||
/>
|
/>
|
||||||
<!-- 侧边抽屉:AI 类型判定 / 等级判定表 -->
|
<!-- 侧边抽屉:AI 类型判定 / 等级判定表 -->
|
||||||
<NDrawer :show="sheet !== ''" @update:show="(v) => !v && (sheet = '')" placement="right" :width="sheet === 'level' ? '45rem' : '28rem'" :auto-focus="false">
|
<NDrawer :show="sheet !== ''" placement="right" :width="sheet === 'level' ? '45rem' : '28rem'" :auto-focus="false" @update:show="(v) => !v && (sheet = '')">
|
||||||
<NDrawerContent>
|
<NDrawerContent>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div v-if="sheet === 'type'" class="text-base font-semibold text-slate-800">AI 变更类型判定说明</div>
|
<div v-if="sheet === 'type'" class="text-base font-semibold text-slate-800">AI 变更类型判定说明</div>
|
||||||
@@ -813,17 +814,17 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</NDrawerContent>
|
</NDrawerContent>
|
||||||
</NDrawer>
|
</NDrawer>
|
||||||
|
|
||||||
<!-- 附件在线预览弹窗 -->
|
<!-- 附件在线预览弹窗 -->
|
||||||
<NModal
|
<NModal
|
||||||
:show="previewFile !== null"
|
:show="previewFile !== null"
|
||||||
preset="card"
|
preset="card"
|
||||||
:auto-focus="false"
|
:auto-focus="false"
|
||||||
:style="{ width: '80%', height: 'auto' }"
|
:style="{ width: '80%', height: 'auto' }"
|
||||||
:segmented="{ content: true, footer: true }"
|
:segmented="{ content: true, footer: true }"
|
||||||
@update:show="(v) => !v && (previewFile = null)"
|
@update:show="(v) => !v && (previewFile = null)"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex items-center gap-2 text-base font-semibold text-slate-800">
|
<div class="flex items-center gap-2 text-base font-semibold text-slate-800">
|
||||||
<Icon icon="akar-icons:file" class="size-18px" /> {{ previewFile?.file_name }}
|
<Icon icon="akar-icons:file" class="size-18px" /> {{ previewFile?.file_name }}
|
||||||
@@ -836,7 +837,7 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
|
|||||||
<span>上传时间:{{ previewFile?.created_at }}</span>
|
<span>上传时间:{{ previewFile?.created_at }}</span>
|
||||||
<span v-if="previewFile?.version">{{ `版本:${previewFile?.version}(受控)` }}</span>
|
<span v-if="previewFile?.version">{{ `版本:${previewFile?.version}(受控)` }}</span>
|
||||||
</div>
|
</div>
|
||||||
<previewComponent v-if="previewFile" :previewFile="previewFile" />
|
<PreviewComponent v-if="previewFile" :preview-file="previewFile" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -847,8 +848,9 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
|
|||||||
<NButton size="small" type="primary" @click="previewFile = null">关闭</NButton>
|
<NButton size="small" type="primary" @click="previewFile = null">关闭</NButton>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</NModal>
|
</NModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
:deep(.n-radio) {
|
:deep(.n-radio) {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Icon } from '@iconify/vue'
|
|||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import { formatTimestamp, getTimeRangeStamp, downloadFileFun } from '@/utils/common';
|
import { formatTimestamp, getTimeRangeStamp, downloadFileFun } from '@/utils/common';
|
||||||
import previewComponent from '@/components/previewFile/index.vue'
|
import PreviewComponent from '@/components/previewFile/index.vue'
|
||||||
import { pssrListApi } from '@/service/api/pssr';
|
import { pssrListApi } from '@/service/api/pssr';
|
||||||
|
|
||||||
const themeStore = useThemeStore();
|
const themeStore = useThemeStore();
|
||||||
|
|||||||
Reference in New Issue
Block a user