| | |
| | | }; |
| | | |
| | | // 获取非政府订单处理详情 |
| | | export const getTenderOrderDetail = () => { |
| | | return http.request<Result>("get", baseUrlApi("/api/tenderOrder/detail")); |
| | | export const getTenderOrderDetail = params => { |
| | | return http.request<Result>("get", baseUrlApi("/api/tenderOrder/detail"), { |
| | | params |
| | | }); |
| | | }; |
| | | |
| | | // 下载非政府订单处理数据导入模板 |
| | |
| | | data |
| | | }); |
| | | }; |
| | | |
| | | // 采购代理人发布招标 |
| | | export const fabuzhaobiao = (data?: object) => { |
| | | return http.request<Result>( |
| | | "post", |
| | | baseUrlApi("/api/tenderOrder/fabuzhaobiao"), |
| | | { |
| | | data |
| | | } |
| | | ); |
| | | }; |
| | | |
| | | // 采购代理人发布招标 |
| | | export const changezhaobiao = (data?: object) => { |
| | | return http.request<Result>( |
| | | "post", |
| | | baseUrlApi("/api/tenderOrder/changezhaobiao"), |
| | | { |
| | | data |
| | | } |
| | | ); |
| | | }; |
| | |
| | | */ |
| | | |
| | | import { http } from "@/utils/http"; |
| | | import { baseUrlApi } from "../util"; |
| | | import { baseUrlApi, uploadUrlApi } from "../util"; |
| | | import type { Result } from "../types"; |
| | | |
| | | //上传和识别营业执照 |
| | | export const upBizLicense = (data?: object) => { |
| | | return http.request<Result>("post", baseUrlApi("/api/upFile/upBizLicense"), { |
| | | data |
| | | }); |
| | | // 获取上传Token |
| | | export const getUploadToken = () => { |
| | | return http.request<Result>("get", baseUrlApi("/api/upFile/token")); |
| | | }; |
| | | |
| | | // 上传身份证 |
| | | export const uploadIdCord = (params?: object) => { |
| | | return http.request<Result>("post", baseUrlApi("/api/upFile/uploadIdCord"), { |
| | | params |
| | | }); |
| | | export const uploadFileAli11 = (data, key) => { |
| | | return http.request<Result>( |
| | | "post", |
| | | uploadUrlApi(`/${key}`), |
| | | { data }, |
| | | { |
| | | headers: { |
| | | "Content-Type": "multipart/form-data", // 设置内容类型为 multipart/form-data,这对于文件上传是必须的 |
| | | "Access-Control-Allow-Origin": "*" |
| | | } |
| | | } |
| | | ); |
| | | }; |
| | | |
| | | export const uploadFileAli = (data, url) => { |
| | | return http.request<Result>( |
| | | "post", |
| | | `${url}`, |
| | | { data }, |
| | | { |
| | | headers: { |
| | | "Content-Type": "multipart/form-data", // 设置内容类型为 multipart/form-data,这对于文件上传是必须的 |
| | | "Access-Control-Allow-Origin": "*" |
| | | } |
| | | } |
| | | ); |
| | | }; |
| | |
| | | process.env.NODE_ENV === "development" |
| | | ? `/api${url}` |
| | | : `192.168.18.52:5005${url}`; |
| | | export const uploadUrlApi = (url: string) => |
| | | process.env.NODE_ENV === "development" |
| | | ? `/oss${url}` |
| | | : `192.168.18.52:5005${url}`; |
New file |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="zh-CN"> |
| | | <head> |
| | | <meta charset="UTF-8" /> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| | | <title>服务端生成签名上传文件到OSS</title> |
| | | </head> |
| | | <body> |
| | | <div class="container"> |
| | | <form> |
| | | <div class="mb-3"> |
| | | <label for="file" class="form-label">选择文件:</label> |
| | | <input type="file" class="form-control" id="file" name="file" required /> |
| | | </div> |
| | | <button type="submit" class="btn btn-primary">上传</button> |
| | | </form> |
| | | </div> |
| | | |
| | | <script type="text/javascript"> |
| | | document.addEventListener('DOMContentLoaded', function () { |
| | | const form = document.querySelector("form"); |
| | | const fileInput = document.querySelector("#file"); |
| | | |
| | | form.addEventListener("submit", (event) => { |
| | | event.preventDefault(); |
| | | |
| | | const file = fileInput.files[0]; |
| | | |
| | | if (!file) { |
| | | alert('请选择一个文件再上传。'); |
| | | return; |
| | | } |
| | | |
| | | const filename = file.name; |
| | | |
| | | fetch("http://192.168.18.52:5005/api/upFile/token", { method: "GET",headers: { |
| | | "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjY5MDg3Njg4MTk3MzMxNywiVGVuYW50SWQiOjEzMDAwMDAwMDAwMDEsIkFjY291bnQiOiIxMzIxOTEyMjMyMCIsIlJlYWxOYW1lIjoi5byg5LiJIiwiVXNlclR5cGUiOiJDdXN0b21lciIsIkN1c3RvbWVyTG9nb2luVHlwZSI6IkRMSkciLCJpYXQiOjE3NTUxMzYwODcsIm5iZiI6MTc1NTEzNjA4NywiZXhwIjoxNzU1NzQwODg3LCJpc3MiOiJBZG1pbi5ORVQiLCJhdWQiOiJBZG1pbi5ORVQifQ.bnB_Tlq856XfOMhtVMI8QP1TTG7JqgnEFeXTdha3Sso" |
| | | } }) |
| | | .then((response) => { |
| | | if (!response.ok) { |
| | | throw new Error("获取签名失败"); |
| | | } |
| | | return response.json(); |
| | | }) |
| | | .then((data) => { |
| | | let formData = new FormData(); |
| | | formData.append("success_action_status", "200"); |
| | | formData.append("policy", data.policy); |
| | | formData.append("x-oss-signature", data.signature); |
| | | formData.append("x-oss-signature-version", "OSS4-HMAC-SHA256"); |
| | | formData.append("x-oss-credential", data.x_oss_credential); |
| | | formData.append("x-oss-date", data.x_oss_date); |
| | | formData.append("key", data.dir + file.name); // 文件名 |
| | | formData.append("x-oss-security-token", data.security_token); |
| | | formData.append("file", file); // file 必须为最后一个表单域 |
| | | |
| | | return fetch(`https://feizhengcai.oss-cn-chengdu.aliyuncs.com/cylsg/20250814094826_8462.pdf`, { |
| | | method: "POST", |
| | | body: formData |
| | | }); |
| | | }) |
| | | .then((response) => { |
| | | if (response.ok) { |
| | | console.log("上传成功"); |
| | | alert("文件已上传"); |
| | | } else { |
| | | console.log("上传失败", response); |
| | | alert("上传失败,请稍后再试"); |
| | | } |
| | | }) |
| | | .catch((error) => { |
| | | console.error("发生错误:", error); |
| | | }); |
| | | }); |
| | | }); |
| | | </script> |
| | | </body> |
| | | </html> |
| | |
| | | |
| | | /** 路由白名单 */ |
| | | const whiteList = ["/login"]; |
| | | const noLoginList = ["/index", "/register", "/registersucess"]; |
| | | const noLoginList = ["/index", "/register", "/registernav", "/registersucess"]; |
| | | // const whiteList = []; |
| | | |
| | | const { VITE_HIDE_HOME } = import.meta.env; |
| | |
| | | } |
| | | } else { |
| | | if (to.path !== "/login") { |
| | | if (whiteList.indexOf(to.path) !== -1) { |
| | | if (noLoginList.indexOf(to.path) !== -1) { |
| | | next(); |
| | | } else { |
| | | removeToken(); |
| | | next({ path: "/login" }); |
| | | next({ path: "/index" }); |
| | | } |
| | | } else { |
| | | next(); |
| | |
| | | name: "aboutItem", |
| | | meta: { |
| | | title: "关注项目", |
| | | roles: ["DLJG", "GYS"] |
| | | roles: ["GYS"] |
| | | // showLink:false |
| | | } |
| | | } |
| | |
| | | return Str.replace(/^\s+|\s+$/g, ""); |
| | | }; |
| | | // 点击复制文本 |
| | | // const copyText = (text: string) => { |
| | | // return new Promise((resolve, reject) => { |
| | | // try { |
| | | // //复制 |
| | | // toClipboard(text); |
| | | // //下面可以设置复制成功的提示框等操作 |
| | | // ElMessage.success("复制成功"); |
| | | // resolve(text); |
| | | // } catch (e) { |
| | | // //复制失败 |
| | | // ElMessage.error("复制失败"); |
| | | // reject(e); |
| | | // } |
| | | // }); |
| | | // }; |
| | | const copyText = (text: string) => { |
| | | // return new Promise((resolve, reject) => { |
| | | // try { |
| | | // //复制 |
| | | // toClipboard(text); |
| | | // //下面可以设置复制成功的提示框等操作 |
| | | // ElMessage.success("复制成功"); |
| | | // resolve(text); |
| | | // } catch (e) { |
| | | // //复制失败 |
| | | // ElMessage.error("复制失败"); |
| | | // reject(e); |
| | | // } |
| | | // }); |
| | | }; |
| | | // 去掉Html标签(取前面5个字符) |
| | | const removeHtmlSub = (value: string) => { |
| | | const str = value.replace(/<[^>]+>/g, ""); |
| | |
| | | class="hover:cursor-pointer" |
| | | @click="toApply(item)" |
| | | ><span v-if="item.name !== '评审专家'">申请</span></el-link |
| | | ><span v-if="item.name !== '评审专家'" class="m-2">|</span> |
| | | ><span v-if="!state.accessToken" class="m-2">|</span> |
| | | <el-link |
| | | v-if="state.rolesCode.indexOf(item.code) !== -1" |
| | | :underline="false" |
| | |
| | | <script setup lang="ts"> |
| | | <script setup lang="tsx"> |
| | | import { ref, reactive, onMounted, computed, PropType } from "vue"; |
| | | import { useRenderIcon } from "@/components/ReIcon/src/hooks"; |
| | | import { useDept } from "./utils/hook"; |
| | | import { FormItemProps } from "./utils/types"; |
| | | |
| | | import { useDetail } from "./utils/detail"; |
| | | import { getTenderOrderDetail } from "@/api/item/index"; |
| | | const isLoading = ref(false); |
| | | import { addDialog } from "@/components/ReDialog"; |
| | | interface Emits { |
| | | (e: "backListPage"): void; |
| | | } |
| | | const emit = defineEmits<Emits>(); |
| | | const props = defineProps({ |
| | | nowInfo: Object as PropType<FormItemProps> |
| | | nowID: null |
| | | }); |
| | | import Delete from "~icons/ep/delete"; |
| | | import EditPen from "~icons/ep/edit-pen"; |
| | | import IconParkSolidBack from "~icons/icon-park-solid/back"; |
| | | |
| | | const { |
| | | state, |
| | | loading, |
| | | selectedNum, |
| | | dataList, |
| | | onSearch, |
| | | resetForm, |
| | | openDialog, |
| | | handleDelete, |
| | | handleSelectionChange, |
| | | handleSizeChange, |
| | | handleCurrentChange, |
| | | pagination, |
| | | checkboxAsRadio, |
| | | onSelectionCancel, |
| | | onbatchDel |
| | | } = useDept(ref({})); |
| | | const { openDialog } = useDept(ref({})); |
| | | const { openUploadDialog, state } = useDetail(); |
| | | defineOptions({ |
| | | name: "itemdetail" |
| | | }); |
| | | onMounted(() => { |
| | | console.log(props.nowInfo); |
| | | onMounted(async () => { |
| | | let res = await getTenderOrderDetail({ id: props.nowID }); |
| | | state.nowInfo = res.result; |
| | | }); |
| | | const backListPage = () => { |
| | | emit("backListPage"); |
| | | }; |
| | | const previewPdf = pdfUrl => { |
| | | addDialog({ |
| | | width: "80%", |
| | | title: "确认信息", |
| | | contentRenderer: () => ( |
| | | <iframe |
| | | type="application/pdf" |
| | | src={pdfUrl} |
| | | width="800" |
| | | height="600" |
| | | ></iframe> |
| | | ), // jsx 语法 (注意在.vue文件启用jsx语法,需要在script开启lang="tsx") |
| | | closeCallBack: ({ options, args }) => { |
| | | // options.props 是响应式的 |
| | | // const { formInline } = options.props as FormProps; |
| | | // const text = `姓名:${formInline.user} 城市:${formInline.region}`; |
| | | if (args?.command === "cancel") { |
| | | // 您点击了取消按钮 |
| | | // active.value -= 1; |
| | | } else if (args?.command === "sure") { |
| | | } else { |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | </script> |
| | | |
| | |
| | | <el-card> |
| | | <template v-slot:header> |
| | | <div class="flex justify-between"> |
| | | <div>{{ nowInfo.projectName }}</div> |
| | | <div>{{ state.nowInfo.projectName }}</div> |
| | | <div> |
| | | <el-button |
| | | class="reset-margin" |
| | | link |
| | | type="primary" |
| | | :icon="useRenderIcon(EditPen)" |
| | | @click="openDialog('修改', nowInfo)" |
| | | /> |
| | | <el-button |
| | | class="reset-margin" |
| | | link |
| | | type="primary" |
| | | :icon="useRenderIcon(Delete)" |
| | | @click="openDialog('修改', nowInfo)" |
| | | @click="openDialog('修改', state.nowInfo)" |
| | | /> |
| | | <el-button |
| | | class="reset-margin" |
| | |
| | | </div> |
| | | </template> |
| | | <div> |
| | | <el-descriptions class="margin-top" :column="3" :size="size" border> |
| | | <el-descriptions class="margin-top" :column="3" border> |
| | | <el-descriptions-item label="项目编号"> |
| | | {{ nowInfo.projectCode }} |
| | | {{ state.nowInfo.projectCode }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="行业品目" |
| | | >18100000000</el-descriptions-item |
| | | > |
| | | <el-descriptions-item label="行业品目"> |
| | | {{ state.nowInfo.hangyepinmuName }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="采购方式"> |
| | | {{ nowInfo.caigoufangshiName }} |
| | | {{ state.nowInfo.caigoufangshiName }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="联合体投标"> |
| | | {{ nowInfo.lianhetitoubiao }} |
| | | {{ state.nowInfo.lianhetitoubiao }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="Remarks"> |
| | | <el-tag size="small">School</el-tag> |
| | | <!-- <el-descriptions-item label="Remarks"> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="Address" /> |
| | | <el-descriptions-item label="Address" /> --> |
| | | </el-descriptions> |
| | | </div> |
| | | </el-card> |
| | | <div class="mt-4.5"> |
| | | <el-card> |
| | | <el-tabs |
| | | tab-position="left" |
| | | style="height: calc(100vh - 380px)" |
| | | class="demo-tabs" |
| | | type="border-card" |
| | | > |
| | | <el-tab-pane label="公告文件上传"> |
| | | <h4>公告文件上传</h4> |
| | | <el-divider /> |
| | | <el-scrollbar height="calc(100vh - 430px)"> |
| | | <div> |
| | | <el-form label-width="auto" style="max-width: 1000px"> |
| | | <el-form-item label="项目信息:"> |
| | | 投标报名开始时间: |
| | | {{ state.nowInfo.toubiaoStartDate ?? "暂无" }} |
| | | 投标报名截止时间: |
| | | {{ state.nowInfo.toubiaoEndDate }} |
| | | 开标时间:{{ state.nowInfo.kaibiaoDate }} |
| | | </el-form-item> |
| | | <el-form-item label="上传公告:"> |
| | | <el-button |
| | | :disabled="!!state.nowInfo.zhaobiaowenjian" |
| | | type="primary" |
| | | plain |
| | | size="small" |
| | | @click="openUploadDialog('上传')" |
| | | > |
| | | 上传公告 |
| | | </el-button> |
| | | <el-button |
| | | v-if="state.nowInfo.zhaobiaowenjian" |
| | | type="primary" |
| | | plain |
| | | size="small" |
| | | @click="openUploadDialog('上传变更', state.nowInfo)" |
| | | > |
| | | 上传变更公告 |
| | | </el-button> |
| | | </el-form-item> |
| | | <el-form-item v-if="state.nowInfo.zhaobiaowenjian" label=" "> |
| | | <div class="border-1 w-[100%] rounded-md p-3"> |
| | | <p>{{ state.nowInfo.projectName }}</p> |
| | | <p>上传时间:2025-8-12 13:41:00</p> |
| | | <el-button |
| | | type="primary" |
| | | plain |
| | | size="small" |
| | | @click="previewPdf(state.nowInfo.zhaobiaowenjian)" |
| | | > |
| | | 点击预览 |
| | | </el-button> |
| | | <el-button type="primary" plain size="small"> |
| | | 修改标题 |
| | | </el-button> |
| | | </div> |
| | | </el-form-item> |
| | | <!-- <el-form-item label="磋商文件:"> |
| | | <el-button type="primary" plain size="small"> |
| | | 上传磋商文件 |
| | | </el-button> |
| | | </el-form-item> |
| | | <el-form-item label=" "> |
| | | <div class="border-1 w-[100%] rounded-md p-3 pl-3"> |
| | | <p>磋商文件名称</p> |
| | | <p>发布时间:2025-8-12 13:41:00</p> |
| | | <el-button type="primary" plain size="small"> |
| | | 点击预览 |
| | | </el-button> |
| | | <el-button type="primary" plain size="small"> |
| | | 点击下载 |
| | | </el-button> |
| | | </div> |
| | | </el-form-item> --> |
| | | <!-- <el-form-item label="澄清与答疑文件:"> |
| | | <el-button type="primary" plain size="small"> |
| | | 文件管理 |
| | | </el-button> |
| | | </el-form-item> --> |
| | | </el-form> |
| | | </div> |
| | | <!-- <el-row :gutter="10" align="right"> |
| | | <el-col :span="3"> |
| | | <el-text size="large"> 项目信息: </el-text> |
| | | </el-col> |
| | | <el-col :span="20"> |
| | | <el-button type="primary" plain>上传公告</el-button> |
| | | <el-button type="primary" plain>上传变更公告</el-button> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="10" align="right"> |
| | | <el-col :span="3"> |
| | | <el-text size="large"> 公告上传: </el-text> |
| | | </el-col> |
| | | <el-col :span="20"> |
| | | <el-button type="primary" plain>上传公告</el-button> |
| | | <el-button type="primary" plain>上传变更公告</el-button> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="10" align="right"> |
| | | <el-col :span="3"> |
| | | <el-text size="large"> 磋商文件: </el-text> |
| | | </el-col> |
| | | <el-col :span="20"> |
| | | <el-button type="primary" plain>上传磋商文件</el-button> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="10" align="right"> |
| | | <el-col :span="3"> |
| | | <el-text size="large"> 澄清与答疑文件: </el-text> |
| | | </el-col> |
| | | <el-col :span="20"> |
| | | <el-button type="primary" plain>文件管理</el-button> |
| | | </el-col> |
| | | </el-row> --> |
| | | </el-scrollbar> |
| | | </el-tab-pane> |
| | | <!-- <el-tab-pane label="开评标管理">开评标管理</el-tab-pane> --> |
| | | <el-tab-pane label="中标公示">中标公示</el-tab-pane> |
| | | <el-tab-pane label="项目完结">项目完结</el-tab-pane> |
| | | </el-tabs> |
| | | </el-card> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <style lang="scss" scoped></style> |
| | | <style lang="scss" scoped> |
| | | .el-divider--horizontal { |
| | | margin: 11px 0; |
| | | } |
| | | </style> |
| | |
| | | <el-form-item label="项目编号"> |
| | | <el-input |
| | | v-model="newFormInline.projectCode" |
| | | disabled |
| | | |
| | | clearable |
| | | placeholder="请输入项目编号" |
| | | /> |
| | |
| | | import EditPen from "~icons/ep/edit-pen"; |
| | | import Refresh from "~icons/ep/refresh"; |
| | | import AddFill from "~icons/ri/add-circle-line"; |
| | | import IonEnterOutline from "~icons/ion/enter-outline"; |
| | | import IconamoonEnterDuotone from "~icons/iconamoon/enter-duotone"; |
| | | import { useUserStoreHook } from "@/store/modules/user"; |
| | | |
| | | defineOptions({ |
| | |
| | | const formRef = ref(); |
| | | const tableRef = ref(); |
| | | const isList = ref(true); |
| | | const nowItem = ref({}); |
| | | const nowItem = ref({ |
| | | id: "" |
| | | }); |
| | | const { |
| | | form, |
| | | state, |
| | |
| | | link |
| | | type="primary" |
| | | :size="size" |
| | | :icon="useRenderIcon(IonEnterOutline)" |
| | | :icon="useRenderIcon(IconamoonEnterDuotone)" |
| | | @click="enterDetail(row)" |
| | | /> |
| | | <!-- <el-popconfirm |
| | | <el-popconfirm |
| | | :title="`是否确认删除项目名称为${row.projectName}的这条数据`" |
| | | @confirm="handleDelete(row)" |
| | | > |
| | |
| | | :icon="useRenderIcon(Delete)" |
| | | /> |
| | | </template> |
| | | </el-popconfirm> --> |
| | | </el-popconfirm> |
| | | </template> |
| | | </pure-table> |
| | | </template> |
| | | </PureTableBar> |
| | | </template> |
| | | <template v-else> |
| | | <detail :now-info="nowItem" @backListPage="backListPage" /> |
| | | <detail :nowID="nowItem?.id" @backListPage="backListPage" /> |
| | | </template> |
| | | </div> |
| | | </template> |
New file |
| | |
| | | <script setup lang="ts"> |
| | | import { onMounted, ref, reactive } from "vue"; |
| | | import ReCol from "@/components/ReCol"; |
| | | import { message } from "@/utils/message"; |
| | | import { TenderProps } from "./utils/types"; |
| | | import { useDetail } from "./utils/detail"; |
| | | import { usePublicHooks } from "../hooks"; |
| | | import type { |
| | | FormInstance, |
| | | FormRules, |
| | | UploadProps, |
| | | UploadInstance, |
| | | UploadRawFile |
| | | } from "element-plus"; |
| | | import { baseUrlApi } from "@/api/util"; |
| | | import { genFileId } from "element-plus"; |
| | | const { openUploadDialog, state, generateTimestampWithRandom } = useDetail(); |
| | | import { getUploadToken, uploadFileAli, uploadFileAli11 } from "@/api/upload"; |
| | | const props = withDefaults(defineProps<TenderProps>(), { |
| | | formInline: () => ({ |
| | | id: "", // 主键Id(必填) |
| | | projectName: "", |
| | | toubiaoStartDate: "", // 投标报名开始时间(必填,格式:yyyy-MM-dd HH:mm:ss) |
| | | toubiaoEndDate: "", // 投标报名结束时间(必填,格式:yyyy-MM-dd HH:mm:ss) |
| | | kaibiaoDate: "", // 开标时间(必填,格式:yyyy-MM-dd HH:mm:ss) |
| | | zhaobiaowenjian: "", // 招标文件(必填,长度1-512字符) |
| | | biangengwenjian: "", |
| | | fujian: "", // 附件(可选,最大长度512字符,可为空) |
| | | kaibiaodidian: "" // 开标地点(必填,长度1-250字符) |
| | | }), |
| | | isChange: null |
| | | }); |
| | | |
| | | const gonggaoRules = reactive({ |
| | | projectName: [{ required: true, message: "请输入标题", trigger: "change" }], |
| | | toubiaoStartDate: [ |
| | | { required: true, message: "请选择投标报名开始时间", trigger: "change" } |
| | | ], |
| | | toubiaoEndDate: [ |
| | | { required: true, message: "请选择投标报名结束时间", trigger: "change" }, |
| | | { |
| | | validator: (rule, value, callback) => { |
| | | if (value && newFormInline.value.toubiaoStartDate) { |
| | | if ( |
| | | new Date(value).getTime() < |
| | | new Date(newFormInline.value.toubiaoStartDate).getTime() |
| | | ) { |
| | | callback(new Error("结束时间不能早于开始时间")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | trigger: "change" |
| | | } |
| | | ], |
| | | kaibiaoDate: [ |
| | | { required: true, message: "请选择开标时间", trigger: "change" }, |
| | | { |
| | | validator: (rule, value, callback) => { |
| | | if (value && newFormInline.value.toubiaoEndDate) { |
| | | if ( |
| | | new Date(value).getTime() < |
| | | new Date(newFormInline.value.toubiaoEndDate).getTime() |
| | | ) { |
| | | callback(new Error("开标时间不能早于投标结束时间")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | trigger: "change" |
| | | } |
| | | ], |
| | | zhaobiaowenjian: [ |
| | | { required: true, message: "请上传招标文件", trigger: "blur" } |
| | | ], |
| | | biangengwenjian: [ |
| | | { required: true, message: "请上传变更文件", trigger: "blur" } |
| | | ], |
| | | fujian: [{ max: 512, message: "附件长度不能超过512个字符", trigger: "blur" }], |
| | | kaibiaodidian: [ |
| | | { required: true, message: "请输入开标地点", trigger: "blur" } |
| | | ] |
| | | }); |
| | | const isLoading = ref(false); |
| | | const ruleFormRef = ref(); |
| | | const { switchStyle } = usePublicHooks(); |
| | | const newFormInline = ref(props.formInline); |
| | | const validateForm = reactive({ |
| | | fileList: [], |
| | | date: "" |
| | | }); |
| | | const upload = ref<UploadInstance>(); |
| | | const file = ref(null); |
| | | const uploadedUrl = ref(""); |
| | | function getRef() { |
| | | return ruleFormRef.value; |
| | | } |
| | | const handleAvatarSuccess: UploadProps["onSuccess"] = ( |
| | | response, |
| | | uploadFile |
| | | ) => { |
| | | if (response.code == "200") { |
| | | // state.ruleForm.businessLicense = URL.createObjectURL(uploadFile.raw!); |
| | | if (props.isChange) { |
| | | newFormInline.value.biangengwenjian = response.result; |
| | | } else { |
| | | newFormInline.value.zhaobiaowenjian = response.result; |
| | | } |
| | | } else { |
| | | message(response.message, { |
| | | type: "error" |
| | | }); |
| | | } |
| | | isLoading.value = false; |
| | | }; |
| | | const handleChange = file => { |
| | | console.log(file); |
| | | |
| | | if (file.status !== "ready") return; |
| | | let suffName = file.name.substring(file.name.lastIndexOf(".") + 1); |
| | | const extension = suffName === "pdf"; |
| | | // const isLt10M = file.size / 1024 / 1024 < 10; |
| | | if (!extension) { |
| | | message(`仅支持pdf格式,请上传pdf`, { |
| | | type: "error" |
| | | }); |
| | | validateForm.fileList = []; |
| | | return false; |
| | | } |
| | | fetchCredentials(file); |
| | | // upload.value!.submit(); |
| | | }; |
| | | // state.formDataNew = { |
| | | // policy: res.result.policy, //表单域 |
| | | // "x-oss-signature-version": res.result.x_oss_signature_version, //指定签名的版本和算法 |
| | | // "x-oss-credential": res.result.x_oss_credential, //指明派生密钥的参数集 |
| | | // "x-oss-date": res.result.x_oss_date, //请求的时间 |
| | | // "x-oss-signature": res.result.signature, //签名认证描述信息 |
| | | // "x-oss-security-token": res.result.security_token, //安全令牌 |
| | | // success_action_status: "200" //上传成功后响应状态码 |
| | | // }; |
| | | const fetchCredentials = async file => { |
| | | // 这里应调用你自己的后端接口获取临时凭证 |
| | | let res = await getUploadToken(); |
| | | if (res.code == 200) { |
| | | let keyVal = generateTimestampWithRandom(res.result.DirPath, file.name); |
| | | let formData = new FormData(); |
| | | formData.append("policy", res.result.policy); |
| | | formData.append( |
| | | "x-oss-signature-version", |
| | | res.result.x_oss_signature_version |
| | | ); |
| | | formData.append("x-oss-credential", res.result.x_oss_credential); |
| | | formData.append("x-oss-date", res.result.x_oss_date); |
| | | // formData.append("Signature", res.result.signature); |
| | | formData.append("x-oss-signature", res.result.signature); |
| | | formData.append("x-oss-security-token", res.result.security_token); |
| | | // formData.append("x-oss-content-type", "application/pdf"); |
| | | formData.append("success_action_status", "200"); |
| | | formData.append("key", keyVal); // 文件名 |
| | | formData.append("file", file.raw); // file 必须为最后一个表单域 |
| | | uploadFileAli(formData, res.result.url).then(res => { |
| | | let path = res.result.url + "/" + res.result.DirPath + keyVal; |
| | | console.log(path); |
| | | |
| | | if (props.isChange) { |
| | | newFormInline.value.biangengwenjian = path; |
| | | } else { |
| | | newFormInline.value.zhaobiaowenjian = path; |
| | | } |
| | | }); |
| | | } |
| | | }; |
| | | const beforeAvatarUpload: UploadProps["beforeUpload"] = rawFile => { |
| | | isLoading.value = true; |
| | | if (rawFile.type !== "application/pdf") { |
| | | message(`仅支持pdf格式,请上传pdf`, { |
| | | type: "error" |
| | | }); |
| | | return false; |
| | | } |
| | | |
| | | // else if (rawFile.size / 1024 / 1024 > 2) { |
| | | // message(`图片大小不能超过 2MB!`, { |
| | | // type: "error" |
| | | // }); |
| | | // return false; |
| | | // } |
| | | return true; |
| | | }; |
| | | const handleExceed: UploadProps["onExceed"] = (files, fileList) => { |
| | | upload.value!.clearFiles(); |
| | | const file = files[0] as UploadRawFile; |
| | | file.uid = genFileId(); |
| | | upload.value!.handleStart(file); |
| | | }; |
| | | defineExpose({ getRef }); |
| | | onMounted(async () => {}); |
| | | </script> |
| | | |
| | | <template> |
| | | <el-form |
| | | ref="ruleFormRef" |
| | | :model="newFormInline" |
| | | :rules="gonggaoRules" |
| | | label-width="140px" |
| | | > |
| | | <el-row :gutter="30"> |
| | | <div v-if="props.isChange"> |
| | | <re-col :value="24" :xs="24" :sm="24"> |
| | | <el-form-item label="标题" prop="projectName"> |
| | | <el-input |
| | | v-model="newFormInline.projectName" |
| | | clearable |
| | | placeholder="请输入标题" |
| | | /> |
| | | </el-form-item> |
| | | </re-col> |
| | | <re-col :value="24" :xs="24" :sm="24"> |
| | | <el-form-item label="变更文件" prop="biangengwenjian"> |
| | | <el-upload |
| | | ref="upload" |
| | | v-model:file-list="validateForm.fileList" |
| | | :limit="1" |
| | | :on-exceed="handleExceed" |
| | | :on-success="handleAvatarSuccess" |
| | | :before-upload="beforeAvatarUpload" |
| | | :headers="state.headers" |
| | | :auto-upload="false" |
| | | accept=".pdf" |
| | | @change="handleChange" |
| | | > |
| | | <el-button type="primary">点击上传</el-button> |
| | | <template #tip> |
| | | <div class="el-upload__tip"> |
| | | 只能上传单个文件,仅支持pdf格式 |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | </el-form-item> |
| | | </re-col> |
| | | </div> |
| | | |
| | | <re-col :value="24" :xs="24" :sm="24"> |
| | | <el-form-item label="投标报名开始时间" prop="toubiaoStartDate"> |
| | | <el-date-picker |
| | | v-model="newFormInline.toubiaoStartDate" |
| | | type="datetime" |
| | | clearable |
| | | placeholder="请选择投标报名开始时间" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | /> |
| | | </el-form-item> |
| | | </re-col> |
| | | <re-col :value="24" :xs="24" :sm="24"> |
| | | <el-form-item label="投标报名结束时间" prop="toubiaoEndDate"> |
| | | <el-date-picker |
| | | v-model="newFormInline.toubiaoEndDate" |
| | | type="datetime" |
| | | clearable |
| | | placeholder="请选择投标报名结束时间" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | /> |
| | | </el-form-item> |
| | | </re-col> |
| | | <re-col :value="24" :xs="24" :sm="24"> |
| | | <el-form-item label="开标时间" prop="kaibiaoDate"> |
| | | <el-date-picker |
| | | v-model="newFormInline.kaibiaoDate" |
| | | type="datetime" |
| | | clearable |
| | | placeholder="请选择开标时间" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | /> |
| | | </el-form-item> |
| | | </re-col> |
| | | <re-col v-if="!props.isChange" :value="24" :xs="24" :sm="24"> |
| | | <el-form-item label="招标文件" prop="zhaobiaowenjian"> |
| | | <el-upload |
| | | ref="upload" |
| | | v-model:file-list="validateForm.fileList" |
| | | :limit="1" |
| | | :on-exceed="handleExceed" |
| | | :auto-upload="false" |
| | | :on-success="handleAvatarSuccess" |
| | | :before-upload="beforeAvatarUpload" |
| | | :headers="state.headers" |
| | | accept=".pdf" |
| | | @change="handleChange" |
| | | > |
| | | <el-button type="primary">点击上传</el-button> |
| | | <template #tip> |
| | | <div class="el-upload__tip">只能上传单个文件,仅支持pdf格式</div> |
| | | </template> |
| | | </el-upload> |
| | | </el-form-item> |
| | | </re-col> |
| | | <re-col :value="24" :xs="24" :sm="24"> |
| | | <el-form-item label="开标地点" prop="kaibiaodidian"> |
| | | <el-input |
| | | v-model="newFormInline.kaibiaodidian" |
| | | clearable |
| | | placeholder="请输入开标地点" |
| | | /> |
| | | </el-form-item> |
| | | </re-col> |
| | | </el-row> |
| | | </el-form> |
| | | </template> |
| | | |
| | | <style lang="scss" scoped> |
| | | :deep .el-date-editor.el-input, |
| | | .el-date-editor.el-input__wrapper { |
| | | width: 100%; |
| | | } |
| | | .upload__tip { |
| | | width: 100%; |
| | | } |
| | | </style> |
New file |
| | |
| | | import tenderForm from "../uploadform.vue"; |
| | | import { ref, h, reactive } from "vue"; |
| | | import { addDialog } from "@/components/ReDialog"; |
| | | import type { TenderInfo } from "./types"; |
| | | import { message } from "@/utils/message"; |
| | | import { cloneDeep, deviceDetection } from "@pureadmin/utils"; |
| | | import type { FormItemProps } from "./types"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import { |
| | | fabuzhaobiao, |
| | | changezhaobiao, |
| | | getTenderOrderDetail |
| | | } from "@/api/item/index"; |
| | | |
| | | const formRef = ref(); |
| | | |
| | | export function useDetail() { |
| | | function openUploadDialog(title = "上传", row?: TenderInfo) { |
| | | addDialog({ |
| | | title: `${title}公告`, |
| | | props: { |
| | | formInline: { |
| | | tenderId: title == "上传变更" ? row?.id : "", |
| | | id: title == "上传" ? row?.id : "", |
| | | projectName: row?.projectName ?? "", |
| | | toubiaoStartDate: row?.toubiaoStartDate ?? "", //投标报名开始时间 |
| | | toubiaoEndDate: row?.toubiaoEndDate ?? "", //投标报名结束时间 |
| | | kaibiaoDate: row?.kaibiaoDate ?? "", //开标时间 |
| | | biangengwenjian: "", //招标文件 |
| | | fujian: row?.fujian ?? "", //附件 |
| | | kaibiaodidian: row?.kaibiaodidian ?? "" //开标地点 |
| | | } |
| | | }, |
| | | width: "30%", |
| | | draggable: true, |
| | | fullscreen: deviceDetection(), |
| | | fullscreenIcon: true, |
| | | sureBtnLoading: true, |
| | | closeOnClickModal: false, |
| | | contentRenderer: () => |
| | | h(tenderForm, { |
| | | ref: formRef, |
| | | formInline: null, |
| | | isChange: title == "上传" ? false : true |
| | | }), |
| | | beforeSure: (done, { options, closeLoading }) => { |
| | | const FormRef = formRef.value.getRef(); |
| | | const curData = cloneDeep(options.props.formInline as TenderInfo); |
| | | async function chores() { |
| | | // message(`您${title}了项目名称为${curData.projectName}的这条数据`, { |
| | | // type: "success" |
| | | // }); |
| | | // curData.dingbiaoguize = curData.dingbiaoguize.join(""); |
| | | let res; |
| | | if (title == "上传") { |
| | | curData.id = state.nowInfo.id; |
| | | res = await fabuzhaobiao(curData); |
| | | } else { |
| | | res = await changezhaobiao(curData); |
| | | } |
| | | if (res.code == "200") { |
| | | getTenderOrderDetail({ id: row?.id }); |
| | | done(); // 关闭弹框 |
| | | } else { |
| | | closeLoading(); |
| | | message(res.message, { |
| | | type: "error" |
| | | }); |
| | | } |
| | | } |
| | | FormRef.validate((valid, obj) => { |
| | | if (valid) { |
| | | // 表单规则校验通过 |
| | | if (title === "新增") { |
| | | // 实际开发先调用新增接口,再进行下面操作 |
| | | chores(); |
| | | } else { |
| | | // 实际开发先调用修改接口,再进行下面操作 |
| | | chores(); |
| | | } |
| | | } else { |
| | | closeLoading(); |
| | | const fail = []; |
| | | for (const key in obj) { |
| | | fail.push(obj[key][0].message); |
| | | } |
| | | message(fail[0], { |
| | | type: "warning" |
| | | }); |
| | | return false; |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | const state = reactive({ |
| | | headers: { |
| | | // Accept: "application/json, text/plain, */*", |
| | | // "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", |
| | | // "X-Requested-With": "XMLHttpRequest", |
| | | Authorization: `Bearer ${getToken()?.accessToken}` |
| | | }, |
| | | nowInfo: Object as PropType<FormItemProps>, |
| | | formDataNew: {} |
| | | }); |
| | | //生成时间戳+随机数 |
| | | // 生成时间戳+随机数的函数 |
| | | function generateTimestampWithRandom(dirPath, filename) { |
| | | // 获取当前日期和时间 |
| | | const now = new Date(); |
| | | // 获取年份,确保为四位数 |
| | | const year = now.getFullYear(); |
| | | // 获取月份,确保为两位数 |
| | | const month = String(now.getMonth() + 1).padStart(2, "0"); |
| | | // 获取日期,确保为两位数 |
| | | const day = String(now.getDate()).padStart(2, "0"); |
| | | // 获取小时,确保为两位数 |
| | | const hours = String(now.getHours()).padStart(2, "0"); |
| | | // 获取分钟,确保为两位数 |
| | | const minutes = String(now.getMinutes()).padStart(2, "0"); |
| | | // 获取秒数,确保为两位数 |
| | | const seconds = String(now.getSeconds()).padStart(2, "0"); |
| | | // 拼接时间戳 |
| | | const timestamp = `${year}${month}${day}${hours}${minutes}${seconds}`; |
| | | // 生成一个 0 到 9999 之间的随机数,并格式化为四位数 |
| | | const randomNumber = String(Math.floor(Math.random() * 10000)).padStart( |
| | | 4, |
| | | "0" |
| | | ); |
| | | // 拼接时间戳和随机数 |
| | | return ( |
| | | `${dirPath}/${timestamp}_${randomNumber}` + filename.match(/\.[^.]+$/) |
| | | ); |
| | | } |
| | | return { |
| | | openUploadDialog, |
| | | state, |
| | | generateTimestampWithRandom |
| | | }; |
| | | } |
| | |
| | | } |
| | | |
| | | function openDialog(title = "新增", row?: FormItemProps) { |
| | | console.log(row, "-"); |
| | | |
| | | addDialog({ |
| | | title: `${title}项目`, |
| | | props: { |
| | |
| | | projectCode: string; // 项目编号(必填) |
| | | projectName: string; // 项目名称(必填) |
| | | hangyepinmu: any | null; // 行业品目(可选) |
| | | hangyepinmuName?: any | null; |
| | | caigoufangshi: any | null; // 采购方式(可选) |
| | | caigoufangshiName: any | null; // 采购方式(可选) |
| | | caigouyusuan: any | null; // 采购预算(可选) |
| | |
| | | dailiXiangmujingli: string | null; // 代理机构项目经理(可选) |
| | | dailijingliLianxidianhua: string | null; // 代理机构项目经理联系电话(可选) |
| | | } |
| | | |
| | | // 招标信息类型定义 |
| | | interface TenderInfo { |
| | | // 主键Id |
| | | id: string; |
| | | // 投标报名开始时间,格式为yyyy-MM-dd HH:mm:ss |
| | | projectName: string; |
| | | toubiaoStartDate: string; |
| | | // 投标报名结束时间,格式为yyyy-MM-dd HH:mm:ss |
| | | toubiaoEndDate: string; |
| | | // 开标时间,格式为yyyy-MM-dd HH:mm:ss |
| | | kaibiaoDate: string; |
| | | // 招标文件,长度1-512字符 |
| | | zhaobiaowenjian: string; |
| | | biangengwenjian: string; |
| | | // 附件,可为空,最大长度512字符 |
| | | fujian?: string; |
| | | // 开标地点,长度1-250字符 |
| | | kaibiaodidian: string; |
| | | } |
| | | interface FormProps { |
| | | formInline: FormItemProps; |
| | | } |
| | | |
| | | export type { FormItemProps, FormProps }; |
| | | interface TenderProps { |
| | | formInline: TenderInfo; |
| | | isChange: boolean; |
| | | } |
| | | |
| | | export type { FormItemProps, FormProps, TenderInfo, TenderProps }; |
| | |
| | | target: "http://192.168.18.52:5005", |
| | | changeOrigin: true, |
| | | rewrite: path => path.replace(/^\/api/, "") |
| | | }, |
| | | "/oss": { |
| | | target: "https://feizhengcai.oss-cn-chengdu.aliyuncs.com", |
| | | changeOrigin: true, |
| | | rewrite: path => path.replace(/^\/oss/, "") |
| | | } |
| | | }, |
| | | // 预热文件以提前转换和缓存结果,降低启动期间的初始页面加载时长并防止转换瀑布 |