|
@@ -0,0 +1,511 @@
|
|
|
+<script setup>
|
|
|
+import { closeToast, showLoadingToast, showSuccessToast, showFailToast, showConfirmDialog } from 'vant'
|
|
|
+import axios from 'axios'
|
|
|
+import { useUserStore } from "~/store/user";
|
|
|
+const userInfo = useUserStore();
|
|
|
+const loading = ref(false);
|
|
|
+const { currentRoute } = useRouter()
|
|
|
+const route = currentRoute.value
|
|
|
+const xm_id = route.query.xm_id
|
|
|
+const detailData = ref({})
|
|
|
+const jdData = ref([
|
|
|
+ { v: "1", n: "项目立项" },
|
|
|
+ { v: "2", n: "项目招标" },
|
|
|
+ { v: "3", n: "项目施工" },
|
|
|
+ { v: "4", n: "项目付款" }
|
|
|
+])
|
|
|
+const scrollHeight = ref(0);
|
|
|
+const currentStep = ref(0);
|
|
|
+const stepData = ref([])
|
|
|
+
|
|
|
+const currentData = ref({});
|
|
|
+const dialogVisible = ref(false);
|
|
|
+const dialogTitle = ref("");
|
|
|
+const dialogType = ref("");
|
|
|
+const fileData = ref([]);
|
|
|
+
|
|
|
+initDetailInfo();
|
|
|
+function initDetailInfo() {
|
|
|
+ loading.value = true;
|
|
|
+ request({
|
|
|
+ url: '/jdbg/xmgl_xmzlk/detail',
|
|
|
+ data: {
|
|
|
+ xm_id,
|
|
|
+ },
|
|
|
+ }).then((res) => {
|
|
|
+ loading.value = false;
|
|
|
+ closeToast()
|
|
|
+ detailData.value = res.data.one_info;
|
|
|
+ detailData.value.xm_start_time = detailData.value.xm_start_time.substring(0, 10);
|
|
|
+
|
|
|
+ stepData.value = [];
|
|
|
+ currentStep.value = 0;
|
|
|
+ detailData.value.xmlc.map((item, i) => {
|
|
|
+ item.order = i + 1;
|
|
|
+ item.lcIndex = i;
|
|
|
+ item.fileData = [];
|
|
|
+ if (item.lc_fj) {
|
|
|
+ let lc_fj = item.lc_fj.split(";");
|
|
|
+ lc_fj.map(v => {
|
|
|
+ let arr = v.split("|");
|
|
|
+ item.fileData.push({
|
|
|
+ url: arr[0],
|
|
|
+ name: arr[1]
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ if (item.xmlc_wc == 1) {
|
|
|
+ currentStep.value = i + 1;
|
|
|
+ }
|
|
|
+ let index = stepData.value.findIndex(val => val.lc_jd == item.lc_jd);
|
|
|
+ if (index == -1) {
|
|
|
+ let obj = {
|
|
|
+ lc_jd: item.lc_jd,
|
|
|
+ name: "",
|
|
|
+ children: [item]
|
|
|
+ }
|
|
|
+ jdData.value.map(val => {
|
|
|
+ if (obj.lc_jd == val.v) {
|
|
|
+ obj.name = val.n;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ stepData.value.push(obj);
|
|
|
+ } else {
|
|
|
+ stepData.value[index].children.push(item);
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ handleScroll();
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function fillIn(data) {
|
|
|
+ if (currentStep.value != data.lcIndex) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ currentData.value = data;
|
|
|
+ dialogVisible.value = true;
|
|
|
+ if (data.lc_bt == 1) {
|
|
|
+ dialogTitle.value = "必填项";
|
|
|
+ dialogType.value = 2;
|
|
|
+ } else {
|
|
|
+ dialogTitle.value = "填写";
|
|
|
+ dialogType.value = 3;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function beforeCloseDialog(action) {
|
|
|
+ if (action == 'confirm') {
|
|
|
+ if (dialogType.value != 1) {
|
|
|
+ if (currentData.value.lc_bt == 1 && currentData.value.xmlc_bt_value == "") {
|
|
|
+ showFailToast("请输入必填项!");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ handleClose();
|
|
|
+ } else {
|
|
|
+ handleUpload();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ handleClose();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function handleClose() {
|
|
|
+ dialogVisible.value = false;
|
|
|
+ currentData.value = {};
|
|
|
+ dialogType.value = "";
|
|
|
+ dialogTitle.value = "";
|
|
|
+ fileData.value = [];
|
|
|
+}
|
|
|
+
|
|
|
+function handleFinish(data, type) {
|
|
|
+ if (type == 2) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (data.lc_bt == 1) {
|
|
|
+ if (data.xmlc_bt_value == "") {
|
|
|
+ showFailToast("请输入" + data.lc_bt_name + "!");
|
|
|
+ } else {
|
|
|
+ commit(data, type);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (data.lc_bt_name == "") {
|
|
|
+ commit(data, type);
|
|
|
+ } else {
|
|
|
+ if (data.xmlc_bt_value != "") {
|
|
|
+ commit(data, type);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ showConfirmDialog({
|
|
|
+ title: '提示',
|
|
|
+ message: data.lc_bt_name + "未填写,是否确定完成",
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ commit(data, type);
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function commit(data, type, close) {
|
|
|
+ let obj = {
|
|
|
+ xm_id: xm_id,
|
|
|
+ xmlc_id: data.xmlc_id,
|
|
|
+ xmlc_wc: type,
|
|
|
+ lc_bt_name: data.lc_bt_name,
|
|
|
+ xmlc_bt_value: data.xmlc_bt_value,
|
|
|
+ lc_fj: data.lc_fj,
|
|
|
+ lc_wc_name: userInfo.real_name,
|
|
|
+ }
|
|
|
+
|
|
|
+ loading.value = true;
|
|
|
+ request({
|
|
|
+ url: '/jdbg/xmgl_xmzlk/edit_lc',
|
|
|
+ data: obj,
|
|
|
+ }).then(({ code }) => {
|
|
|
+ loading.value = false;
|
|
|
+ if (code == 1) {
|
|
|
+ let con = document.querySelector(".xmgktParentDiv");
|
|
|
+ scrollHeight.value = con.scrollTop;
|
|
|
+
|
|
|
+ if (close) {
|
|
|
+ handleClose();
|
|
|
+ } else {
|
|
|
+ showSuccessToast('事项完成!');
|
|
|
+ }
|
|
|
+ initDetailInfo();
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function handleScroll() {
|
|
|
+ setTimeout(() => {
|
|
|
+ let con = document.querySelector(".xmgktParentDiv");
|
|
|
+ con.scrollTo({
|
|
|
+ top: scrollHeight.value
|
|
|
+ })
|
|
|
+ }, 0)
|
|
|
+}
|
|
|
+
|
|
|
+function handleOpen(data) {
|
|
|
+ currentData.value = data;
|
|
|
+ dialogVisible.value = true;
|
|
|
+ dialogTitle.value = data.lc_sx;
|
|
|
+ dialogType.value = 1;
|
|
|
+ fileData.value = data.fileData;
|
|
|
+}
|
|
|
+
|
|
|
+function handleAfterRead(file) {
|
|
|
+ if (file.file) {
|
|
|
+ const formData = new FormData();
|
|
|
+ formData.append("api", "json");
|
|
|
+ formData.append("token", userInfo.token);
|
|
|
+ formData.append("urltype", "0");
|
|
|
+ formData.append("filedata", file.file);
|
|
|
+ loading.value = true;
|
|
|
+ axios({
|
|
|
+ method: 'post',
|
|
|
+ url: window.globalVariables.api + "/upload/main/file",
|
|
|
+ headers: { 'Content-Type': 'multipart/form-data' },
|
|
|
+ data: formData
|
|
|
+ })
|
|
|
+ .then((res) => {
|
|
|
+ loading.value = false;
|
|
|
+ if (res.data.code === '1') {
|
|
|
+ fileData.value.unshift({
|
|
|
+ url: res.data.data.url,
|
|
|
+ name: res.data.data.file_name
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ showFailToast("上传失败!");
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(error => {
|
|
|
+ console.log(error)
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function handleUpload() {
|
|
|
+ let arr = fileData.value.map(item => {
|
|
|
+ return `${item.url}|${item.name}`
|
|
|
+ })
|
|
|
+ currentData.value.lc_fj = arr.join(";");
|
|
|
+ commit(currentData.value, currentData.value.xmlc_wc, true);
|
|
|
+}
|
|
|
+
|
|
|
+function handleRemove(index) {
|
|
|
+ fileData.value.splice(index, 1);
|
|
|
+}
|
|
|
+
|
|
|
+const dialogPreviewVisible = ref(false);
|
|
|
+const showUrl = ref("");
|
|
|
+const suffix = ref("");
|
|
|
+const previewUrl = ref("https://view.officeapps.live.com/op/view.aspx?src=");
|
|
|
+function handlePreview(data) {
|
|
|
+ showUrl.value = getFullUrl(data.url);
|
|
|
+ suffix.value = showUrl.value.substr(showUrl.value.lastIndexOf(".") + 1).toLowerCase();
|
|
|
+ // dialogPreviewVisible.value = true;
|
|
|
+
|
|
|
+ if (suffix.value == "mp3" || suffix.value == "mp4" || suffix.value == "pdf" || suffix.value == "jpg" || suffix.value == "jpeg" || suffix.value == "png") {
|
|
|
+ window.open(showUrl.value);
|
|
|
+ } else {
|
|
|
+ window.open(previewUrl.value + showUrl.value);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function getFullUrl(url) {
|
|
|
+ if (!url) return '';
|
|
|
+ return url.startsWith('http') ? url : window.GLOBAL_CONFIG.api + '/' + url
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <div v-show="loading" class="h-100vh w-full absolute z-3000 bg-hex-000000b3 flex items-center justify-center">
|
|
|
+ <van-loading color="#ffffffcc" text-color="#ffffffcc" vertical>加载中...</van-loading>
|
|
|
+ </div>
|
|
|
+ <div class="xmgktParentDiv h-100vh overflow-auto">
|
|
|
+ <div class="tableContainer">
|
|
|
+ <div class="topPart">
|
|
|
+ <table class="Tb" width="100%" cellspacing="0" cellpadding="0">
|
|
|
+ <tr>
|
|
|
+ <td class="titleOpt">
|
|
|
+ 项目名称:
|
|
|
+ </td>
|
|
|
+ <td>{{ detailData.xm_name }}</td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td class="titleOpt">
|
|
|
+ 项目类别:
|
|
|
+ </td>
|
|
|
+ <td>{{ detailData.xm_type_option_n }}</td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td class="titleOpt">
|
|
|
+ 经办科室:
|
|
|
+ </td>
|
|
|
+ <td>{{ detailData.department_name }}</td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td class="titleOpt">
|
|
|
+ 经办人:
|
|
|
+ </td>
|
|
|
+ <td>{{ detailData.xm_zrr }}</td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td class="titleOpt">
|
|
|
+ 项目开始时间:
|
|
|
+ </td>
|
|
|
+ <td>{{ detailData.xm_start_time }}</td>
|
|
|
+ </tr>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="xmgktManageContent">
|
|
|
+ <div class="stepDiv" v-for="(item, index) in stepData" :key="index">
|
|
|
+ <div class="parent-head nowrap" :class="item.children[0].xmlc_wc==1?'finished':''">
|
|
|
+ <div class="step-head">
|
|
|
+ <div class="parent-head-line"></div>
|
|
|
+ </div>
|
|
|
+ <div class="parent-name whitespace-nowrap">{{item.name}}</div>
|
|
|
+ </div>
|
|
|
+ <div class="parent">
|
|
|
+ <div class="child d-flex" :class="value.xmlc_wc==1?'finished':''" v-for="(value, i) in item.children" :key="i">
|
|
|
+ <div class="step-head">
|
|
|
+ <div class="step-line" v-if="index!=stepData.length-1 || i!=item.children.length-1"></div>
|
|
|
+ <div class="step-check d-flex flex-v-mid flex-l-mid">
|
|
|
+ <van-icon name="success" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="mainCon flex-1 flex-row">
|
|
|
+ <div>
|
|
|
+ <div class="flex">
|
|
|
+ <div class="w-130px">{{value.order}}.{{value.lc_sx}}</div>
|
|
|
+ <div class="w-80px">事项权值 {{value.lc_sxqz}}</div>
|
|
|
+ </div>
|
|
|
+ <div class="flex mt-5px">
|
|
|
+ <div v-if="value.lc_fj && value.lc_fj!=''" class="w-130px text-hex-265CD4" @click="handleOpen(value)">查看附件</div>
|
|
|
+ <div v-else class="w-130px">未上传附件</div>
|
|
|
+ <div v-if="value.lc_bt_name=='' || value.lc_bt_name==undefined"></div>
|
|
|
+ <div v-else class="d-flex whitespace-nowrap">
|
|
|
+ <div>{{value.lc_bt_name}}: </div>
|
|
|
+ <div class="hand" @click="fillIn(value)">{{value.xmlc_bt_value == "" ? "待填" : value.xmlc_bt_value + "万元"}}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="attach">*{{value.lc_fj_bz}}</div>
|
|
|
+ </div>
|
|
|
+ <div class="text-right">
|
|
|
+ <div class="whitespace-nowrap text-hex-A2A2A2 text-8px mt-3px" v-if="value.xmlc_wc==1">{{value.lc_wc_name}}点击完成</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <van-dialog v-model:show="dialogVisible" :title="dialogTitle" show-cancel-button :show-confirm-button="false" :before-close="beforeCloseDialog">
|
|
|
+ <!-- <div v-if="dialogType!=1" class="flex items-center flex-v-mid px-2 py-3">
|
|
|
+ <div class="whitespace-nowrap"><span class="" style="color: red;" v-if="currentData.lc_bt==1">*</span>{{currentData.lc_bt_name}}:</div>
|
|
|
+ <input class="flex-1 border px-1 py-5px rounded-6px" type="number" v-model="currentData.xmlc_bt_value" oninput="if(value.length>10)value=value.slice(0,10)" />
|
|
|
+ </div> -->
|
|
|
+ <div v-if="dialogType==1" class="px-2 py-3">
|
|
|
+ <!-- <van-uploader accept="*" :after-read="handleAfterRead">
|
|
|
+ <van-button size="small" type="primary">点击上传</van-button>
|
|
|
+ </van-uploader> -->
|
|
|
+ <div class="mt-8px text-14px text-hex-666 leading-30px">
|
|
|
+ <div v-for="(item, index) in fileData" :key="index">
|
|
|
+ <div class="flex items-center space-x-8px">
|
|
|
+ <van-icon name="description" />
|
|
|
+ <div class="flex-1 truncate" @click="handlePreview(item)">{{ item.name }}</div>
|
|
|
+ <!-- <van-icon name="cross" @click="handleRemove(index)" /> -->
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </van-dialog>
|
|
|
+
|
|
|
+ <van-dialog class="previewDiv" v-model:show="dialogPreviewVisible" title="查看" show-cancel-button :show-confirm-button="false">
|
|
|
+ <div class="h-full">
|
|
|
+ <video ref="videoPlay" v-if="suffix=='mp4' || suffix=='MP4'" controls>
|
|
|
+ <source :src="showUrl" type="video/mp4" />
|
|
|
+ <source :src="showUrl" type="video/ogg" />
|
|
|
+ </video>
|
|
|
+ <audio ref="audioPlay" v-else-if="suffix=='mp3' || suffix=='MP3'" controls :src="showUrl"></audio>
|
|
|
+ <img v-else-if="suffix=='jpg' || suffix=='JPG' || suffix=='jpeg' || suffix=='JPEG' || suffix=='png' || suffix=='PNG'" :src="showUrl" alt="" style="width: 100%;max-height: 100%;">
|
|
|
+ <iframe v-else-if="suffix=='pdf' || suffix=='PDF'" class="iframe" :src="showUrl" width="100%" frameborder="0" scrolling="auto"></iframe>
|
|
|
+ <iframe v-else class="iframe" :src="previewUrl+showUrl" width="100%" frameborder="0" scrolling="auto"></iframe>
|
|
|
+ </div>
|
|
|
+ </van-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+$color: #4351ff;
|
|
|
+$color1: #d2e7fd;
|
|
|
+.flex-row {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+.d-flex {
|
|
|
+ display: flex;
|
|
|
+}
|
|
|
+.xmgktManageContent {
|
|
|
+ padding: 10px;
|
|
|
+ font-size: 12px;
|
|
|
+ .stepDiv {
|
|
|
+ .step-head {
|
|
|
+ width: 30px;
|
|
|
+ height: 100%;
|
|
|
+ position: relative;
|
|
|
+ }
|
|
|
+ .parent-head {
|
|
|
+ position: relative;
|
|
|
+ display: inline-block;
|
|
|
+ margin-bottom: -5px;
|
|
|
+ .parent-head-line {
|
|
|
+ width: 1px;
|
|
|
+ height: 50px;
|
|
|
+ margin: auto;
|
|
|
+ background: #e2e2e2;
|
|
|
+ }
|
|
|
+ .parent-name {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: -15px;
|
|
|
+ background: #fff;
|
|
|
+ padding: 5px 0;
|
|
|
+ left: 60%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ }
|
|
|
+ &.finished {
|
|
|
+ .parent-head-line {
|
|
|
+ background: $color1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .parent {
|
|
|
+ .child {
|
|
|
+ height: 70px;
|
|
|
+ .step-line {
|
|
|
+ width: 1px;
|
|
|
+ height: 100%;
|
|
|
+ margin: auto;
|
|
|
+ background: #e2e2e2;
|
|
|
+ }
|
|
|
+ .step-check {
|
|
|
+ width: 22px;
|
|
|
+ height: 22px;
|
|
|
+ border-radius: 50%;
|
|
|
+ color: #fff;
|
|
|
+ background: #c7c7c7;
|
|
|
+ box-shadow: 0 0 0 3px #e2e2e2;
|
|
|
+ position: absolute;
|
|
|
+ top: 5px;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ .mainCon {
|
|
|
+ border-bottom: 1px solid #e6e6e6;
|
|
|
+ margin-left: 10px;
|
|
|
+ margin-top: -15px;
|
|
|
+ height: 100%;
|
|
|
+ .attach {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #ff0000;
|
|
|
+ min-width: 100px;
|
|
|
+ // width: 200px;
|
|
|
+ }
|
|
|
+ .van-button {
|
|
|
+ width: 60px;
|
|
|
+ height: 28px;
|
|
|
+ padding: 0;
|
|
|
+ &.van-button--info {
|
|
|
+ background: #969696;
|
|
|
+ border-color: #969696;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ &.finished {
|
|
|
+ .step-line {
|
|
|
+ background: $color1;
|
|
|
+ }
|
|
|
+ .step-check {
|
|
|
+ background: #0976f8;
|
|
|
+ box-shadow: 0 0 0 3px $color1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+:deep() {
|
|
|
+ .previewDiv {
|
|
|
+ height: 80%;
|
|
|
+ top: 50%;
|
|
|
+ .van-dialog__header {
|
|
|
+ padding: 0;
|
|
|
+ line-height: 50px;
|
|
|
+ }
|
|
|
+ .van-dialog__content {
|
|
|
+ padding: 0 10px;
|
|
|
+ height: calc(100% - 100px);
|
|
|
+ }
|
|
|
+ video,
|
|
|
+ audio,
|
|
|
+ iframe {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|