批量推送代码
This commit is contained in:
@@ -1,87 +1,6 @@
|
||||
<template>
|
||||
<div class="home-page">
|
||||
<header class="top-nav" :class="topNavClass">
|
||||
<a class="logo" title="返回首页" @click.prevent="onHome">
|
||||
<div class="logo-icon"><img src="/logo.png" alt="logo" /></div>
|
||||
<div class="logo-text">
|
||||
<span class="logo-title">北京整合医学学会</span>
|
||||
<span class="logo-subtitle">Beijing Association of Holistic Integrative Medicine</span>
|
||||
</div>
|
||||
</a>
|
||||
<ul class="nav-list">
|
||||
<li class="nav-item"><span class="nav-link active">年度项目规划</span></li>
|
||||
<li class="nav-item"><a class="nav-link" @click.prevent="goPublicity">项目公示</a></li>
|
||||
</ul>
|
||||
<div class="top-tools">
|
||||
<template v-if="!loggedIn">
|
||||
<span class="login-btn" @click="goLogin">登录</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-dropdown trigger="click" @command="onUserCmd">
|
||||
<a class="user-link" @click.prevent>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="12" cy="7" r="4"/>
|
||||
</svg>
|
||||
<span>{{ userName }}</span>
|
||||
</a>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="account">我的主页</el-dropdown-item>
|
||||
<el-dropdown-item command="logout" divided>退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</div>
|
||||
<!-- 手机端汉堡按钮 (桌面隐藏) -->
|
||||
<button class="hamburger" type="button" aria-label="打开菜单" @click="drawerOpen = true">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"/>
|
||||
<line x1="3" y1="12" x2="21" y2="12"/>
|
||||
<line x1="3" y1="18" x2="21" y2="18"/>
|
||||
</svg>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<!-- 手机端抽屉菜单 (桌面隐藏) -->
|
||||
<transition name="drawer-fade">
|
||||
<div v-if="drawerOpen" class="drawer-mask" @click="drawerOpen = false"></div>
|
||||
</transition>
|
||||
<transition name="drawer-slide">
|
||||
<aside v-if="drawerOpen" class="drawer-panel" role="dialog" aria-label="导航菜单">
|
||||
<div class="drawer-header">
|
||||
<span class="drawer-title">导航菜单</span>
|
||||
<button class="drawer-close" type="button" aria-label="关闭菜单" @click="drawerOpen = false">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||||
<line x1="6" y1="6" x2="18" y2="18"/>
|
||||
<line x1="6" y1="18" x2="18" y2="6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<nav class="drawer-nav">
|
||||
<a class="drawer-link" @click.prevent="goHomeAndClose">年度项目规划</a>
|
||||
<a class="drawer-link" @click.prevent="goPublicityAndClose">项目公示</a>
|
||||
</nav>
|
||||
<div class="drawer-divider"></div>
|
||||
<div class="drawer-tools">
|
||||
<template v-if="!loggedIn">
|
||||
<span class="drawer-login" @click="goLoginAndClose">登录</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="drawer-user">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="12" cy="7" r="4"/>
|
||||
</svg>
|
||||
<span>{{ userName }}</span>
|
||||
</div>
|
||||
<a class="drawer-link" @click.prevent="goAccountAndClose">我的主页</a>
|
||||
<a class="drawer-link danger" @click.prevent="goLogoutAndClose">退出登录</a>
|
||||
</template>
|
||||
</div>
|
||||
</aside>
|
||||
</transition>
|
||||
<PortalNavbar />
|
||||
|
||||
<div class="container">
|
||||
<section class="hero">
|
||||
@@ -135,16 +54,14 @@ import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { logout as logoutApi } from '@/api/auth'
|
||||
import request from '@/utils/request'
|
||||
import PortalFooter from '@/components/PortalFooter.vue'
|
||||
import PortalNavbar from '@/components/PortalNavbar.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const isScrolled = ref(false)
|
||||
const specialPlans = ref([])
|
||||
const drawerOpen = ref(false)
|
||||
|
||||
async function loadSpecialPlans() {
|
||||
try {
|
||||
@@ -156,11 +73,15 @@ async function loadSpecialPlans() {
|
||||
}
|
||||
|
||||
function openPlan(id) {
|
||||
// 点击计划详情需登录: 未登录先跳登录, 登录后回跳详情
|
||||
if (!loggedIn.value) {
|
||||
ElMessage.warning('请先登录系统')
|
||||
router.push({ path: '/login', query: { redirect: `/special-plan/${id}` } })
|
||||
return
|
||||
}
|
||||
window.open(`${import.meta.env.BASE_URL}#/special-plan/${id}`, '_blank')
|
||||
}
|
||||
const topNavClass = computed(() => isScrolled.value ? 'is-scrolled' : '')
|
||||
const loggedIn = computed(() => !!userStore.token)
|
||||
const userName = computed(() => userStore.user?.userName || '用户')
|
||||
// 投稿角色: admin/manager 不开放投稿 → 隐藏「项目提案」按钮; 未登录 user 为 null → 显示(点击引导登录)
|
||||
const canPropose = computed(() => {
|
||||
const r = userStore.user?.role || ''
|
||||
@@ -224,42 +145,15 @@ const planSvg = `<svg viewBox="0 150 1200 370" xmlns="http://www.w3.org/2000/svg
|
||||
</g>
|
||||
</svg>`
|
||||
|
||||
function handleScroll() {
|
||||
isScrolled.value = window.scrollY > 10
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('scroll', handleScroll, { passive: true })
|
||||
loadSpecialPlans()
|
||||
// portal home 移动端适配: 解除 body min-width (避免手机端 1354px 横向滚动)
|
||||
document.body.classList.add('home-page-body')
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
document.body.classList.remove('home-page-body')
|
||||
})
|
||||
|
||||
function onHome() { isScrolled.value = false }
|
||||
function goLogin() { router.push('/login') }
|
||||
function goUser() { /* placeholder */ }
|
||||
async function goLogout() { try { await logoutApi() } catch {}; userStore.logout(); router.replace('/login') }
|
||||
|
||||
// 抽屉版导航: 跳转后关闭抽屉
|
||||
function goHomeAndClose() { drawerOpen.value = false; onHome() }
|
||||
function goPublicityAndClose() { drawerOpen.value = false; goPublicity() }
|
||||
function goLoginAndClose() { drawerOpen.value = false; goLogin() }
|
||||
function goAccountAndClose() { drawerOpen.value = false; onUserCmd('account') }
|
||||
async function goLogoutAndClose() { drawerOpen.value = false; await goLogout() }
|
||||
async function onUserCmd(cmd) {
|
||||
if (cmd === 'logout') return goLogout()
|
||||
if (cmd === 'account') {
|
||||
const r = (userStore.user?.role || '')
|
||||
const map = { admin: '/admin/workbench', manager: '/manager/workbench', doctor: '/doctor/home', executor: '/executor/meetings', sponsor: '/sponsor/home' }
|
||||
router.push(map[r] || '/admin/workbench')
|
||||
}
|
||||
}
|
||||
function goPublicity() { router.push('/publicity') }
|
||||
|
||||
function onSubmit() {
|
||||
if (!loggedIn.value) {
|
||||
ElMessage.warning('请先登录系统')
|
||||
|
||||
@@ -1,40 +1,6 @@
|
||||
<template>
|
||||
<div class="pub-page">
|
||||
<header class="top-nav" :class="topNavClass">
|
||||
<a class="logo" title="返回首页" @click.prevent="goHome">
|
||||
<div class="logo-icon"><img src="/logo.png" alt="logo" /></div>
|
||||
<div class="logo-text">
|
||||
<span class="logo-title">北京整合医学学会</span>
|
||||
<span class="logo-subtitle">Beijing Association of Holistic Integrative Medicine</span>
|
||||
</div>
|
||||
</a>
|
||||
<ul class="nav-list">
|
||||
<li class="nav-item"><a class="nav-link" @click.prevent="goHome">年度项目规划</a></li>
|
||||
<li class="nav-item"><span class="nav-link active">项目公示</span></li>
|
||||
</ul>
|
||||
<div class="top-tools">
|
||||
<template v-if="!loggedIn">
|
||||
<span class="login-btn" @click="goLogin">登录</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-dropdown trigger="click" @command="onUserCmd">
|
||||
<a class="user-link" @click.prevent>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="12" cy="7" r="4"/>
|
||||
</svg>
|
||||
<span>{{ userName }}</span>
|
||||
</a>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="account">我的主页</el-dropdown-item>
|
||||
<el-dropdown-item command="logout" divided>退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</div>
|
||||
</header>
|
||||
<PortalNavbar />
|
||||
|
||||
<main class="container">
|
||||
<div class="page-header">
|
||||
@@ -106,23 +72,16 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { logout as logoutApi } from '@/api/auth'
|
||||
import request from '@/utils/request'
|
||||
import { bizList } from '@/api/public'
|
||||
import PortalFooter from '@/components/PortalFooter.vue'
|
||||
import PortalNavbar from '@/components/PortalNavbar.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const PAGE_SIZE = 10
|
||||
|
||||
const isScrolled = ref(false)
|
||||
const topNavClass = computed(() => isScrolled.value ? 'is-scrolled' : '')
|
||||
const loggedIn = computed(() => !!userStore.token)
|
||||
const userName = computed(() => userStore.user?.userName || '用户')
|
||||
|
||||
const ALL_NOTICES = []
|
||||
|
||||
const currentKeyword = ref('')
|
||||
@@ -203,19 +162,6 @@ function prevPage() { if (currentPage.value > 1) { currentPage.value--; window.s
|
||||
function nextPage() { if (currentPage.value < totalPages.value) { currentPage.value++; window.scrollTo({ top: 0, behavior: 'smooth' }) } }
|
||||
function goPage(p) { currentPage.value = p; window.scrollTo({ top: 0, behavior: 'smooth' }) }
|
||||
|
||||
function goHome() { router.push('/') }
|
||||
function goPublicity() { router.push('/publicity') }
|
||||
function goLogin() { router.push('/login') }
|
||||
function goUser() { /* placeholder */ }
|
||||
async function goLogout() { try { await logoutApi() } catch {}; userStore.logout(); router.replace('/login') }
|
||||
async function onUserCmd(cmd) {
|
||||
if (cmd === 'logout') return goLogout()
|
||||
if (cmd === 'account') {
|
||||
const r = (userStore.user?.role || '')
|
||||
const map = { admin: '/admin/workbench', manager: '/manager/workbench', doctor: '/doctor/home', executor: '/executor/meetings', sponsor: '/sponsor/home' }
|
||||
router.push(map[r] || '/admin/workbench')
|
||||
}
|
||||
}
|
||||
function goDetail(n) {
|
||||
if (!n || !n.annId) return
|
||||
router.push({ name: 'publicity-detail', params: { projectId: n.annId } })
|
||||
@@ -229,9 +175,6 @@ function fmtDate(d) {
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${y}-${m}-${day}`
|
||||
}
|
||||
function handleScroll() { isScrolled.value = window.scrollY > 10 }
|
||||
onMounted(() => window.addEventListener('scroll', handleScroll, { passive: true }))
|
||||
onBeforeUnmount(() => window.removeEventListener('scroll', handleScroll))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,40 +1,6 @@
|
||||
<template>
|
||||
<div class="detail-page">
|
||||
<header class="top-nav" :class="topNavClass">
|
||||
<a class="logo" title="返回首页" @click.prevent="goHome">
|
||||
<div class="logo-icon"><img src="/logo.png" alt="logo" /></div>
|
||||
<div class="logo-text">
|
||||
<span class="logo-title">北京整合医学学会</span>
|
||||
<span class="logo-subtitle">Beijing Association of Holistic Integrative Medicine</span>
|
||||
</div>
|
||||
</a>
|
||||
<ul class="nav-list">
|
||||
<li class="nav-item"><a class="nav-link" @click.prevent="goHome">年度项目规划</a></li>
|
||||
<li class="nav-item"><a class="nav-link active" @click.prevent="goPublicity">项目公示</a></li>
|
||||
</ul>
|
||||
<div class="top-tools">
|
||||
<template v-if="!loggedIn">
|
||||
<span class="login-btn" @click="goLogin">登录</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-dropdown trigger="click" @command="onUserCmd">
|
||||
<a class="user-link" @click.prevent>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="12" cy="7" r="4"/>
|
||||
</svg>
|
||||
<span>{{ userName }}</span>
|
||||
</a>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="account">我的主页</el-dropdown-item>
|
||||
<el-dropdown-item command="logout" divided>退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</div>
|
||||
</header>
|
||||
<PortalNavbar />
|
||||
|
||||
<main class="container">
|
||||
<a class="back-link" @click.prevent="goPublicity">
|
||||
@@ -183,8 +149,8 @@ import { ref, computed, watch, reactive, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { logout as logoutApi } from '@/api/auth'
|
||||
import PortalFooter from '@/components/PortalFooter.vue'
|
||||
import PortalNavbar from '@/components/PortalNavbar.vue'
|
||||
import { listBizPerson } from '@/api/business/person'
|
||||
import { bizGet } from '@/api/public'
|
||||
import {
|
||||
@@ -199,10 +165,7 @@ const route = useRoute()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const isScrolled = ref(false)
|
||||
const topNavClass = computed(() => isScrolled.value ? 'is-scrolled' : '')
|
||||
const loggedIn = computed(() => !!userStore.token)
|
||||
const userName = computed(() => userStore.user?.userName || '用户')
|
||||
// 意向按钮按角色显隐:
|
||||
// sponsor (含 MAIN/SUB) → 仅支持意向; executor (含 MAIN/SUB) → 仅执行意向
|
||||
// admin / manager / doctor → 两个都隐藏
|
||||
@@ -359,19 +322,7 @@ async function load() {
|
||||
}
|
||||
}
|
||||
|
||||
function goHome() { router.push('/') }
|
||||
function goPublicity() { router.push('/publicity') }
|
||||
function goLogin() { router.push('/login') }
|
||||
function goUser() { /* placeholder */ }
|
||||
async function goLogout() { try { await logoutApi() } catch {}; userStore.logout(); router.replace('/login') }
|
||||
async function onUserCmd(cmd) {
|
||||
if (cmd === 'logout') return goLogout()
|
||||
if (cmd === 'account') {
|
||||
const r = (userStore.user?.role || '')
|
||||
const map = { admin: '/admin/workbench', manager: '/manager/workbench', doctor: '/doctor/home', executor: '/executor/meetings', sponsor: '/sponsor/home' }
|
||||
router.push(map[r] || '/admin/workbench')
|
||||
}
|
||||
}
|
||||
|
||||
const signed = ref(false)
|
||||
async function checkSigned() {
|
||||
@@ -572,7 +523,6 @@ function onViewBidAnnouncement() {
|
||||
}
|
||||
|
||||
function onKeydown(e) { if (e.key === 'Escape') showQr.value = false }
|
||||
function handleScroll() { isScrolled.value = window.scrollY > 10 }
|
||||
|
||||
// 下载二维码: qrUrl 是 QRCode.toDataURL 生成的 data:image/png;base64,... 直接 <a download> 即可
|
||||
function onDownloadQr() {
|
||||
@@ -586,12 +536,10 @@ function onDownloadQr() {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('scroll', handleScroll, { passive: true })
|
||||
window.addEventListener('keydown', onKeydown)
|
||||
load()
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
window.removeEventListener('keydown', onKeydown)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -147,6 +147,11 @@ async function onUserCmd(cmd) {
|
||||
function handleScroll() { isScrolled.value = window.scrollY > 10 }
|
||||
|
||||
onMounted(() => {
|
||||
// 计划详情需登录 (点击验证登录状态): 未登录跳登录, 登录后回跳 (兜底拦截直链访问)
|
||||
if (!loggedIn.value) {
|
||||
router.replace({ path: '/login', query: { redirect: route.fullPath } })
|
||||
return
|
||||
}
|
||||
window.addEventListener('scroll', handleScroll, { passive: true })
|
||||
load()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user