| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063 |
- <template>
- <div class="bid-result-page">
- <div class="page-tabs">
- <span
- v-for="tab in tabs"
- :key="tab.value"
- :class="['page-tab', { active: activeTab === tab.value }]"
- @click="handleTabClick(tab.value)"
- >
- {{ tab.label }}
- </span>
- <div class="btn-right">
- <el-button type="primary" class="btn enter-btn" @click="handleBidRegistration">中标登记</el-button>
- </div>
- </div>
- <div class="bid-container">
- <div v-if="activeTab === 'ongoing'" class="pool-stats">
- <div
- v-for="stat in bidStats"
- :key="stat.label"
- class="pool-stat"
- :class="{ highlight: stat.highlight }"
- @click="getTabName(stat)"
- >
- <span>{{ stat.label }}</span>
- <b>{{ stat.value }}</b>
- </div>
- </div>
- <div v-if="activeTab === 'ongoing'" class="monitor-list">
- <div class="panel-card">
- <div class="card-header">
- <span class="card-title">监控中的投标</span>
- </div>
- <div class="empty-state" v-if="total === 0">
- <span class="empty-icon">🔍</span>
- <p>暂无数据</p>
- </div>
- <div v-else class="card-body">
- <div
- v-for="item in allprojects"
- :key="item.id"
- :class="['monitor-card',{ active: selectedBid === item.id }, { 'won': item.c_sfzb == true }]"
- >
- <div class="monitor-head">
- <strong>{{ item.title }}</strong>
- <span :class="['tag', item.c_sfzb == false ? 'tag-danger':'']" style="float: right;">{{ item.c_sfzb == true ? '已中标' : '监控中' }}</span>
- </div>
- <div class="monitor-meta">
- <!-- <span>招标编号 {{ item.c_zbbh }}</span> -->
- <span>开标 {{ moment(item.c_kbsj).format('YYYY-MM-DD HH:mm') }}</span>
- <span>报价 {{ item.c_tbbj || '未报价' }}</span>
- <span v-if="item.days > 0">距开标 {{ item.days || '0' }} 天</span>
- </div>
- <div>
- <span class="monitor-status">
- <span class="pulse-dot"></span>
- 最近检查 {{ moment(item.update_time).format('YYYY-MM-DD HH:mm') }} · 已检查 {{ item.jccs || '0' }} 次
- </span>
- </div>
- <div v-if="item.c_sfzb == true">
- <strong style="color: var(--primary);">
- 中标
- </strong>
- <el-button type="primary" class="btn enter-btn" size="small" style="float: right;cursor: pointer;" @click="handleOpenClick(item)">查看中标详情</el-button>
- <!-- <span style="color: var(--primary);"> · 得分 92.5 · 排名 1</span> -->
-
- </div>
- </div>
-
- <!-- 分页控件 -->
- <div class="pagination-section">
- <span class="total-text">共{{ total }}个</span>
- <el-pagination
- layout="prev, pager, next"
- :total="total"
- :current-page="page"
- :page-size="pageSize"
- @current-change="handleCurrentChange"
- >
- </el-pagination>
- </div>
- </div>
- </div>
- </div>
- <div v-else>
- <div style="margin-bottom: 16px;">
- <el-select v-model="jkObject" placeholder="请选择项目名称" @change="handleFinishProjectChange" value-key="id">
- <el-option v-for="item in finishProjects" :selected="item.id == selectedBid" :key="item.id" :label="item.c_xmmc" :value="item" />
- </el-select>
- </div>
-
- <div class="empty-state" v-if="finishProjects.length < 1">
- <span class="empty-icon">🔍</span>
- <p>暂无数据</p>
- </div>
- <div v-if="finishProjects.length > 0">
- <div class="result-banner" :class="{ lost: !showWin }">
- <div>
- <span style="font-size: large;font-weight: bold;">{{ showWin ? '🎉 中标' : '😔 未中标' }} </span> · <span>{{jkObject.c_xmmc}}</span>
- <!-- {{jkObject.project?.c_winning_suppliers || '未中标'}} -->
- <p>{{ showWin ? '恭喜您的标书在本次招标中脱颖而出!' : '请再接再厉,下次一定成功!' }} · 结果发布:{{ jkObject.project?.c_publish_time || '2025-07-11' }} · 中标单位:上海申勤物业管理服务有限公司</p>
- </div>
- </div>
- <!-- <div class="card">
- <div class="card-head">中标结果详情</div>
- <div class="card-body">
- <dl class="info info-wide">
- <dt>项目名称</dt>
- <dd>XX商业综合体物业管理服务</dd>
- <dt>中标金额</dt>
- <dd>¥5,000,000 /年</dd>
- <dt>服务期限</dt>
- <dd>2026年7月1日 - 2029年6月30日</dd>
- <dt>开标时间</dt>
- <dd>2026年6月20日 14:00</dd>
- </dl>
- </div>
- </div> -->
- <div class="card">
- <div class="card-head">中标结果确认</div>
- <div class="card-body markdown-container">
-
- <!-- <div class="markdown-content" v-html="markdownContent"></div> -->
- <div class="markdown-content" v-if="jkObject.c_gljg" v-html="markdownContent"></div>
- <div class="markdown-content"v-else>人工处理结果:<span style="cursor: pointer;color: var(--primary);" @click="handleOpenPageClick()">点击查看中标结果详情</span></div>
- </div>
- <div style="padding: 20px;" >
- <el-button type="primary" class="btn enter-btn" @click="handleConfirmBidPush" :disabled="jkObject.c_sfts">确认中标 · 一键推送</el-button>
- <el-button type="primary" class="btn enter-btn" @click="openFileForm('1')">上传合同</el-button>
- <el-button type="primary" class="btn enter-btn" @click="openFileForm('2')">上传投标文件</el-button>
- <el-button type="primary" class="btn enter-btn" @click="openFileForm('3')">上传采购资料</el-button>
- </div>
- </div>
- <div class="panel-card" style="margin-bottom: 16px;">
- <div class="card-header">
- <span class="card-title">推送预览</span>
- <span :class="['tag', pushStartsOptions.done ? 'tag-success': 'tag-warn']" style="float: right;">{{pushStartsOptions.done ? '已完成' : '待推送'}}</span>
- </div>
- <div class="card-body">
- <div class="push-grid">
- <div
- v-for="item in pushStartsOptions.data"
- :key="item.name"
- :class="['push-item', { done: pushStartsOptions.done }]"
- >
- <strong :style="{'color': pushStartsOptions.done ? '' : 'var(--text3)'}">{{ item.name }}</strong>
- <span :style="{'color': pushStartsOptions.done ? '' : 'var(--text3)'}">{{ item.desc }}</span><br/>
- <span v-if="pushStartsOptions.done" style="color: var(--text3);">{{ moment(item.time).format('YYYY-MM-DD HH:mm') }}</span>
- </div>
- </div>
- </div>
- </div>
- <div class="card">
- <div class="card-head">中标流程</div>
- <div class="card-body">
- <ul class="timeline">
- <li class="done" v-for="item in jkObject.c_lcsjx">{{moment(item.create_time).format('YYYY-MM-DD')}} · {{item.desc}} · {{item.username}}</li>
- </ul>
- </div>
- </div>
- </div>
- </div>
- <div v-if="addFormVisible" >
- <addContent :flag="addFormVisible" :areaProject="areaProject" :glArea="glArea" :close="closeAddForm"/>
- </div>
- <div v-if="addProjectFormVisible" >
- <addProject :flag="addProjectFormVisible" :glArea="glArea" :projectData="jkObject" :close="closeAddProjectForm"/>
- </div>
-
- <el-dialog v-model="dialogFormFileVisible" draggable title="提示信息" width="300">
- <el-input v-model="FileForm.name" autocomplete="off" placeholder="请输入文件名称"/>
- <div style="margin-top: 10px;">
- <el-upload
- class="upload-demo"
- action=""
- style="width: 100%;"
- :auto-upload="false"
- :on-change="handleFileChange"
- :file-list="fileList"
- limit="1"
- >
- <el-button size="small" type="primary" style="width: 268px;">点击上传</el-button>
- <!-- <template #tip>
- <div class="el-upload__tip">只能上传 jpg/png 文件,且不超过 500kb</div>
- </template> -->
- </el-upload>
- </div>
- <template #footer>
- <div class="dialog-footer">
- <el-button @click="dialogFormFileVisible = false" class="btn">取消</el-button>
- <el-button type="primary" class="btn enter-btn" @click="submitFileForm()">
- 确认
- </el-button>
- </div>
- </template>
- </el-dialog>
- <el-dialog
- v-model="dialogVisible"
- title="提示信息"
- width="300"
- align-center
- draggable
- show-close="false"
- >
- <span>本项目已推送至市场部负责人,后续将由市场部统一分发至各部门负责人,相关后续流程敬请期待。</span>
- <template #footer>
- <div class="dialog-footer">
- <el-button type="primary" @click="handleConfirm">
- 确认
- </el-button>
- </div>
- </template>
- </el-dialog>
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { ref,onMounted } from 'vue'
- import moment from 'moment'
- import api from '@/api/common'
- import CONFIG from '@/utils/config'
- // import type { FormInstance } from 'element-plus'
- import addContent from '@/components/bidResult/addContent.vue'
- import addProject from '@/components/bidResult/addProject.vue'
- import MarkdownIt from 'markdown-it'
- const activeTab = ref('ongoing')
- const selectedBid = ref('')
- // const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}')
- // const userName = userInfo?.username =='user_xtw' ? '徐天炜' : userInfo?.username || ''
- const showWin = ref(true)
- const markdownContent = ref('')
- interface jgForm {
- [key: string]: any
- }
- const jkObject = ref<jgForm>({})
- const dialogFormFileVisible = ref(false)
- const FileForm = ref<jgForm>({})
- const fileList = ref([])
- const pageSize = ref(10)
- const page = ref(1)
- const total = ref(0)
- const md = ref<MarkdownIt>()
- const dialogVisible = ref(false)
- const finishTreeProjects = ref<any[]>([])
- const glArea = ref([{} as TreeItem])
- const areaProject = ref([{} as TreeItem])
- interface TreeItem {
- id: string | number
- value: string
- label: string
- children: TreeItem[],
- [key: string]: any
- }
- onMounted(() => {
- initData()
- getFinishProjects()
- md.value = new MarkdownIt({
- html: true,
- linkify: true,
- typographer: true
- })
- activeTab.value = sessionStorage.getItem('activeTab') || 'ongoing'
- selectedBid.value = sessionStorage.getItem('jkId') || ''
- // openMD('http://localhost:5173/public/static/data/index.html')
- })
- const openMD = async (url: string) => {
- try {
- let proxyUrl = url
-
- const response = await fetch(proxyUrl)
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`)
- }
- let specDoc = await response.text()
-
- let html = specDoc
- .replace(/<html[^>]*>/gi, '')
- .replace(/<\/html>/gi, '')
- .replace(/<body[^>]*>/gi, '')
- .replace(/<\/body>/gi, '')
- .replace(/<head[^>]*>[\s\S]*?<\/head>/gi, '')
- .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
-
- if (md.value) {
- const result = md.value.render(html)
- if (typeof result === 'string') {
- markdownContent.value = result
- } else {
- try {
- markdownContent.value = JSON.stringify(result)
- } catch (e) {
- markdownContent.value = '<p>文档内容解析异常</p>'
- }
- }
- } else {
- markdownContent.value = html
- }
- } catch (error) {
- markdownContent.value = '<p>加载文档失败,请检查网络连接或文件路径</p>'
- }
- }
- const tabs = ref([
- { label: '开标监测', value: 'ongoing' },
- { label: '中标结果', value: 'won' },
- ])
- const bidStats = ref([
- { label: '全部', value: '0', highlight: true },
- { label: '监控中', value: '0', highlight: false },
- { label: '已中标', value: '0', highlight: false }
- ])
- const addFormVisible = ref(false)
- const addProjectFormVisible = ref(false)
- interface ProjectItem {
- id: string | number
- title: string
- [key: string]: any
- }
- const allprojects = ref<ProjectItem[]>([])
- const pendingProjects = ref<ProjectItem[]>([])
- const monitorProjects = ref<ProjectItem[]>([])
- const finishProjects = ref<ProjectItem[]>([])
- const pushStartsOptions = ref(
- {
- done: false,
- data:[
- { name: '运营部',desc:"前介看板新增卡片",time:"2026-06-08 10:40" },
- { name: '人力资源',desc:"招聘联动 · 缺口草稿",time:"2026-06-08 10:40" },
- { name: '财务部',desc:"收入科目 · 开票节点",time:"2026-06-08 10:40" },
- { name: '市场部',desc:"定编方案确认",time:"2026-06-08 10:40" },
- { name: '采购部',desc:"采购需求确认",time:"2026-06-08 10:40" },
- ]
- }
- )
- const getTabName = (stat: { label: string; highlight: boolean }) => {
- stat.highlight = true
- if(stat.label == '全部'){
- getProjects("","");//初始所有列表
- }else if(stat.label == '监控中'){
- getProjects("","1");//初始所有列表
- }else if(stat.label == '已中标'){
- getProjects("","2");//初始所有列表
- }
- bidStats.value.forEach(item => {
- if (item.label !== stat.label) {
- item.highlight = false
- }
- })
- }
- const handleTabClick = (tab: string) => {
- activeTab.value = tab
- sessionStorage.setItem('activeTab',tab)
- updateStatus()
- if(tab == 'won' && jkObject.value.project){
- openMD(jkObject.value.project.c_content_html_path || '')
- }
- }
- const closeAddForm = () => {
- addFormVisible.value = false
- initData()
- }
- const closeAddProjectForm = () => {
- addProjectFormVisible.value = false
- initData()
- }
- const handleBidRegistration = async () => {
- addFormVisible.value = true
- }
- const handleFinishProjectChange = (val: any) => {
- if (val && typeof val === 'object' && val.project) {
- jkObject.value = val
- updateStatus()
- // jkObject.value.c_lcsjx = JSON.parse(val.c_lcsjx || '[]')
- jkObject.value.c_lcsjx = val.c_lcsjx || '[]'
- openMD(jkObject.value.project.c_content_html_path || '')
- }
- }
- const handleOpenClick = (item: ProjectItem) => {
- if(item.c_sfzb == true){
- sessionStorage.setItem('activeTab', 'won')
- activeTab.value = 'won'
- jkObject.value = item
- jkObject.value.c_lcsjx = JSON.parse(item.c_lcsjx || '[]')
- selectedBid.value = item.id.toString()
- }
- }
- const handleOpenPageClick = () => {
- window.open(jkObject.value.c_jgjk_url)
- }
- const initData = () => {
- getProjects("","");//初始所有列表
- getProjects("0","");//初始统计所有数据
- getProjects("1","1");//初始统计已监控数据
- getProjects("2","2");//初始统计已中标数据
- }
- const handleCurrentChange = (val: number) => {
- page.value = val
- bidStats.value.forEach(item => {
- if(item.highlight == true){
- if(item.label == '全部'){
- getProjects("","");//初始所有列表
- }else if(item.label == '监控中'){
- getProjects("","1");//初始所有列表
- }else if(item.label == '已中标'){
- getProjects("","2");//初始所有列表
- }
- }
- })
-
- }
- const getProjects = async (label:string,type:string) => {
- const params: any = {
- columnId: CONFIG.COLUMNIDARR[6],
- orderBy: JSON.stringify([{ field: "update_time", orderByType: 2 }]),
- states: '0,1,2,3,4',
- pageSize: pageSize.value,
- page: (page.value-1)
- }
- if (type) {
- let flag = false
- if(type == '2'){
- flag = true
- }
- params.search = JSON.stringify([
- ...(params.search ? JSON.parse(params.search) : []),
- {
- field: "c_sfzb",
- searchType: 1,
- content: { value: flag },
- },
- ]);
- }
-
- const res = await getDmsDataList(params)
-
- if ((res as any).code === 200) {
- if(label == ''){
- allprojects.value = (res as any).content.data.map((item: any) => ({
- ...item,
- ...calculateRemainingDays(moment().format('YYYY-MM-DD HH:mm'),item.c_kbsj)
- }))
- total.value = (res as any).content.count
- }else if(label == '0'){
- const result = getCountNumber(res as any)
- pendingProjects.value = result.data
- bidStats.value.filter(item => item.label == '全部')[0].value = result.count
- }else if(label == '1'){
- const result = getCountNumber(res as any)
- monitorProjects.value = result.data
- bidStats.value.filter(item => item.label == '监控中')[0].value = result.count
- }else if(label == '2'){
- const result = getCountNumber(res as any)
- const promises = result.data.map(async (item: any) => {
- let project = {}
- if(item.c_gljg){
- project = await getJGProjects(item.c_gljg)
- }
- return {
- ...item,
- c_lcsjx:JSON.parse(item.c_lcsjx || '[]'),
- project:project || {}
- }
- })
- finishProjects.value = await Promise.all(promises)
- bidStats.value.filter(item => item.label == '已中标')[0].value = result.count
- if(selectedBid.value){
- jkObject.value = finishProjects.value.find(item => item.id == selectedBid.value) || {}
- if(jkObject.value.project){
- openMD(jkObject.value.project.c_content_html_path || '')
- }
- }else{
- jkObject.value = finishProjects.value[0] || {}
- }
- updateStatus()
- }
- }else{
- if(label == ''){
- allprojects.value = []
- total.value = 0
- }
- }
- }
- const updateStatus = () => {
- pushStartsOptions.value.done = jkObject.value.c_sfts == true ? true : false
- pushStartsOptions.value.data.forEach(item => {
- item.time = jkObject.value.c_tssj || ''
- })
- }
- const getJGProjects = async (id:string) => {
- const params: any = {
- columnId: CONFIG.COLUMNIDARR[7],
- orderBy: JSON.stringify([{ field: "update_time", orderByType: 2 }]),
- states: '0,1,2,3,4',
- pageSize: pageSize.value,
- page: (page.value-1)
- }
- if (id) {
- params.search = JSON.stringify([
- ...(params.search ? JSON.parse(params.search) : []),
- {
- field: "id",
- searchType: 1,
- content: { value: id },
- },
- ]);
- }
- const res = await getDmsDataList(params)
- return (res as any).content.data[0]
- }
- const getCountNumber = (res:any) =>{
- const result = {
- data:(res as any).content.data.map((item: any) => ({
- ...item,
- ...calculateRemainingDays(moment().format('YYYY-MM-DD HH:mm'),item.kbrq)
- })),
- count:(res as any).content.count.toString()
- }
- return result
- }
- const getDmsDataList = async (params: any) => {
- return await api.getDmsDataList(params)
- }
- const calculateRemainingDays = (st: string, et: string): { days: string;} => {
- if (!et) return { days: '' }
- const startDate = moment(moment(st).format('YYYY-MM-DD HH:mm'), 'YYYY-MM-DD HH:mm')
- const expireDate = moment(moment(et).format('YYYY-MM-DD HH:mm'), 'YYYY-MM-DD HH:mm')
-
- if (!startDate.isValid() || !expireDate.isValid()) {
- return { days: ''}
- }
- const today = moment().startOf('day')
- const diffDays = expireDate.diff(today, 'days')
- return { days: diffDays.toString() };
-
- }
- const handleConfirmBidPush = async () => {
- // let arr = jkObject.value.c_lcsjx ? jkObject.value.c_lcsjx : []
- // arr.push({
- // create_time: moment().format('YYYY-MM-DD HH:mm'),
- // desc:'市场部确认',
- // username:userName,
- // userid:userInfo?.id || ''
- // },{
- // create_time: moment().format('YYYY-MM-DD HH:mm'),
- // desc:'已推送运营 / 人力 / 财务 / 采购',
- // username:userName,
- // userid:userInfo?.id || ''
- // })
- // updateAudit(jkObject.value,4)
- // updateKBJKProject(jkObject.value,arr)
- // console.log(jkObject.value)
- if(jkObject.value.c_type == "续签"){
- updateProject(jkObject.value,true)
- dialogVisible.value = true
- initData()
- }else{//续标
- addProjectFormVisible.value = true
- }
-
- }
- const updateProject = async (item: any,flag:boolean) => {
- const params: any = {
- columnId: CONFIG.COLUMNIDARR[6],
- modelId: CONFIG.MODELIDARR[3],
- content: JSON.stringify({
- id: item.id,
- c_sfts: flag,
- c_tssj: moment().format('YYYY-MM-DD HH:mm:ss'),
- }),
- }
- await api.updateContent(params)
- }
- const handleConfirm = () => {
- dialogVisible.value = false
- }
- // const updateKBJKProject = async (item: any,arr:any[]) => {
- // const params: any = {
- // columnId: CONFIG.COLUMNIDARR[6],
- // modelId: CONFIG.MODELIDARR[3],
- // content: JSON.stringify({
- // id: item.id,
- // c_lcsjx: arr,
- // }),
- // }
- // const res = await api.updateContent(params)
- // if ((res as any).code === 200) {
- // // ElMessage.success('添加成功')
- // initData()
- // }else{
- // // ElMessage.error('添加失败')
- // }
- // }
- // const updateAudit = async (item: any,type:number) => {
- // const params: any = {
- // id: item.id,
- // columnId: CONFIG.COLUMNIDARR[6],
- // state: type,
- // }
- // const res = await getUpdateAudit(params)
- // if ((res as any).code === 200) {
- // // ElMessage.success('操作成功')
- // }else{
- // // ElMessage.error('操作失败')
- // }
- // }
- // const getUpdateAudit = async (params: any) => {
- // return await api.updateAudit(params)
- // }
- const handleFileChange = (file: any) => {
- FileForm.value = file
- }
- const submitFileForm = async () => {
- dialogFormFileVisible.value = false
- }
- const openFileForm = (type: string) => {
- FileForm.value.type = type
- dialogFormFileVisible.value = true
- }
- const getFinishProjects = async () => {
- const params: any = {
- columnId: CONFIG.COLUMNIDARR[0], //原项目数据
- orderBy: JSON.stringify([{ field: "c_zgxmhtdqrq", orderByType: 1 }]),
- states: '0',// 只看草稿箱
- pageSize: 9999,
- page: 0
- }
- await api.getDmsDataList(params).then(res => {
- if ((res as any).code === 200) {
- let finishProjects = (res as any).content.data
- finishTreeProjects.value = buildTree(finishProjects)
- getGLXQData()
- }
- })
- }
- const getGLXQData = async () => {
- const itemList = await getTypeProjects(CONFIG.COLUMNIDARR[9]) // 管理区域
- glArea.value = buildAreaTree(itemList.data || [])
- areaProject.value = itemList.data.filter((item: any) => item?.c_district_name && item?.c_district_code?.startsWith('310')) // 过滤空、null、undefined、''
- .map((item: any) => ({
- label: item.c_district_name,
- value: item.c_district_name,
- children: finishTreeProjects.value.filter((item2: any) => item.c_district_name.includes(item2.c_sq))
- }))
- const uniqueTree = Array.from(
- new Map(areaProject.value.map(item => [item.value, item])).values()
- )
- areaProject.value = uniqueTree
- }
- const getTypeProjects = async (columnId:number) => {
- const params: any = {
- columnId: columnId,
- orderBy: JSON.stringify([{ field: "update_time", orderByType: 2 }]),
- states: '0,1,2,3,4',
- pageSize: 9999,
- page: 0
- }
- const res = await getDmsDataList(params)
- return (res as any).content
- }
- const buildTree = (data: any[]): any[] => {
- const map = new Map()
- const tree: any[] = []
- data.forEach((item: any) => {
- map.set(item.c_guid, {
- ...item,
- value: item.id,
- label: item.c_xmmc,
- children: []
- })
- })
- data.forEach((item: any) => {
- const node = map.get(item.c_guid)
- if (item.c_father_code && map.has(item.c_father_code)) {
- // map.get(item.c_father_code).children.push(node)
- } else {
- tree.push(node)
- }
- })
- return tree
- }
- const buildAreaTree = (list: any[]) => {
- const districtMap = new Map<string, TreeItem>()
- const filteredList = list.filter(item => item?.c_district_code?.startsWith('3101'))
- for (const item of filteredList) {
- const districtCode = item.c_district_code
- const districtName = item.c_district_name
- if (!districtCode || !districtName) continue
- let districtNode = districtMap.get(districtCode)
- if (!districtNode) {
- districtNode = {
- id: districtCode,
- label: districtName,
- value: districtCode,
- children: []
- }
- districtMap.set(districtCode, districtNode)
- }
- const partCode = item.c_part_code
- const partName = item.c_part_name
- if (!partCode || !partName) continue
- const partMap = new Map<string, TreeItem>()
- for (const child of districtNode.children) {
- partMap.set(child.value, child)
- }
- let partNode = partMap.get(partCode)
- if (!partNode) {
- partNode = {
- id: partCode,
- label: partName,
- value: partCode,
- children: []
- }
- districtNode.children.push(partNode)
- }
- }
- return Array.from(districtMap.values())
- }
- </script>
- <style scoped>
- .bid-result-page {
- width: 100%;
- }
- .bid-container{
- height: calc(100vh - 180px);
- overflow-y: auto;
- }
- .markdown-container {
- max-height: 400px;
- overflow-y: auto;
- padding: 16px;
- }
- .markdown-content {
- font-size: 14px;
- line-height: 1.8;
- color: #1a2b3c;
- }
- :root {
- th, td {
- text-align: left;
- padding: 0px !important;
- border-bottom: 1px solid #e8ecef;
- }
- }
- .page-tabs {
- display: flex;
- gap: 0;
- margin-bottom: 16px;
- border-bottom: 1px solid #e8ecef;
- justify-content: space-between;
- align-items: center;
- }
- .btn-right {
- margin-left: auto;
- }
- .page-tab {
- padding: 10px 18px;
- border: none;
- background: none;
- font-family: inherit;
- font-size: 13px;
- color: #5c6f82;
- cursor: pointer;
- border-bottom: 2px solid transparent;
- margin-bottom: -1px;
- }
- .page-tab.active {
- color: #0d9488;
- border-bottom-color: #0d9488;
- font-weight: 600;
- }
- .monitor-list {
- display: flex;
- flex-direction: column;
- gap: 12px;
- }
- .monitor-card {
- border: 1px solid #e8ecef;
- border-radius: 10px;
- padding: 14px 16px;
- background: #fff;
- margin-bottom: 16px;
- transition: border-color 0.15s;
- &:hover{
- border-color: #0d9488;
- box-shadow: 0 0 0 3px #e6f6f4;
- }
- }
- .monitor-card.active {
- border-color: #0d9488;
- box-shadow: 0 0 0 3px #e6f6f4;
- }
- .monitor-card.won {
- border-color: var(--info);
- background: var(--info-soft);
- }
- .monitor-head {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
- gap: 12px;
- flex-wrap: wrap;
- margin-bottom: 10px;
- }
- .monitor-meta {
- font-size: 12px;
- color: var(--text3);
- display: flex;
- flex-wrap: wrap;
- gap: 14px;
- margin-bottom: 10px;
- }
- .monitor-status {
- display: inline-flex;
- align-items: center;
- gap: 6px;
- font-size: 12px;
- margin-bottom: 10px;
- }
- .monitor-status .pulse-dot {
- width: 8px;
- height: 8px;
- border-radius: 50%;
- background: var(--primary);
- animation: pulse 1.5s infinite;
- }
- @keyframes pulse {
- 0%, 100% { opacity: 1; }
- 50% { opacity: 0.4; }
- }
- .push-grid {
- display: grid;
- grid-template-columns: repeat(5, 1fr);
- gap: 10px;
- margin-top: 14px;
- }
- .push-item {
- border: 1px solid #e8ecef;
- border-radius: 8px;
- padding: 12px;
- text-align: center;
- font-size: 12px;
- background: #fafbfc;
- }
- .push-item.done {
- border-color: var(--info);
- background: var(--info-soft);
- }
- .push-item strong {
- display: block;
- font-size: 13px;
- margin-bottom: 4px;
- }
- .result-banner {
- background: linear-gradient(90deg, #ecfdf5, #fff);
- border: 1px solid #a7f3d0;
- border-left: 4px solid var(--info);
- padding: 14px 16px;
- border-radius: 10px;
- margin-bottom: 16px;
- }
- .result-banner.lost {
- background: linear-gradient(90deg, #fef2f2, #fff);
- border-color: var(--danger);
- border-left-color: var(--danger);
- }
- .result-banner h3 {
- font-size: 16px;
- margin-bottom: 4px;
- }
- .result-banner p {
- font-size: 13px;
- color: #5c6f82;
- }
- .card {
- background: #fff;
- border: 1px solid #e8ecef;
- border-radius: 10px;
- margin-bottom: 16px;
- }
- .card-head {
- padding: 12px 16px;
- border-bottom: 1px solid #e8ecef;
- font-weight: 600;
- font-size: 14px;
- }
- .card-body {
- padding: 16px;
- }
- .info {
- display: grid;
- grid-template-columns: 100px 1fr;
- gap: 8px 12px;
- font-size: 13px;
- }
- .info-wide {
- grid-template-columns: minmax(140px, 168px) 1fr;
- }
- .info dt {
- color: #8a9bab;
- }
- .timeline {
- list-style: none;
- margin-top: 12px;
- }
- .timeline li {
- padding: 8px 0 8px 16px;
- border-left: 2px solid #e8ecef;
- margin-left: 6px;
- font-size: 12px;
- color: #5c6f82;
- position: relative;
- }
- .timeline li::before {
- content: '';
- position: absolute;
- left: -5px;
- top: 12px;
- width: 8px;
- height: 8px;
- border-radius: 50%;
- background: var(--primary);
- }
- .timeline li.done::before {
- background: var(--info);
- }
- .pool-stats {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 10px;
- margin-bottom: 16px;
- }
- .pool-stat {
- background: #fff;
- border: 1px solid #e8ecef;
- border-radius: 8px;
- padding: 12px 14px;
- cursor: pointer;
- transition: border-color 0.15s;
- }
- .pool-stat:hover {
- border-color: var(--primary);
- }
- .pool-stat b {
- display: block;
- font-size: 20px;
- margin-top: 2px;
- }
- .pool-stat span {
- font-size: 11px;
- color: var(--text3);
- }
- .pool-stat.highlight {
- border-color: var(--danger);
- background: var(--danger-soft);
- }
- .tag {
- font-size: 11px;
- padding: 2px 8px;
- border-radius: 10px;
- background: var(--info-soft);
- color: var(--info);
- }
- .tag-warn {
- background: var(--warn-soft);
- color: var(--warn);
- }
- .tag-danger {
- background: var(--danger-soft);
- color: var(--danger);
- }
- @media (max-width: 1100px) {
- .push-grid {
- grid-template-columns: repeat(2, 1fr);
- }
- }
- </style>
|