This commit is contained in:
hrx 2026-08-28 17:53:32 +08:00
parent 69c760421c
commit cc1fc8575d
21 changed files with 3274 additions and 4 deletions

View File

@ -11,7 +11,7 @@ import {getHomePath} from "@/views/setting/tools";
NProgress.configure({showSpinner: false}); // NProgress Configuration
const whiteList = ["product", "/tokenMarket", "/modelDetail", "/modelApiDocument", "/login", "/homePage", "/registrationPage", "/shoppingCart", "/homePageImage","/h5HomePage",'/H5about','/modelProductDetail','/ncmatchHome', '/agreement']; // no redirect whitelist
const whiteList = ["product", "/tokenMarket", "/modelDetail", "/modelApiDocument", "/login", "/homePage", "/registrationPage", "/shoppingCart", "/homePageImage","/h5HomePage",'/H5about','/modelProductDetail','/ncmatchHome', '/agreement', '/ncmatch']; // no redirect whitelist
// 获取用户代理字符串
const userAgent = window.navigator.userAgent;

View File

@ -259,17 +259,36 @@ export const constantRoutes = [
{
hidden: true,
path: '/ncmatchHome',
component: () => import('@/views/homePage/ncmatch/index.vue'),
component: () => import('@/views/Ncmatch/HomeIndex.vue'),
name: 'ncmatchHome',
redirect: '/ncmatchHome/index',
meta: { fullPath: '/ncmatchHome/index', title: '官网首页', noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: 'index',
component: () => import('@/views/homePage/ncmatch/mainPage/index.vue'),
component: () => import('@/views/Ncmatch/index.vue'),
name: 'ncmatchPageIndex',
hidden: true,
meta: { title: '首页', fullPath: '/ncmatch/mainPage/index' }
meta: { title: '首页', fullPath: '/ncmatch/mainPage/index' },
},
{
path: 'opcOverview',
component: () => import('@/views/Ncmatch/opcOverview.vue'),
name: 'ncmatchOpcOverview',
meta: { title: 'OPC社区概况', fullPath: '/ncmatch/opcOverview', noCache: true }
},
{
path: 'societyNews',
component: () => import('@/views/Ncmatch/SocietyNewsBrief.vue'),
name: 'ncmatchSocietyNews',
meta: { title: '政社快讯', fullPath: '/ncmatch/societyNews', noCache: true }
},
{
path: 'tokenMarket',
component: () => import('@/views/product/allProduct/index.vue'),
name: 'homePageTokenMarket',
hidden: true,
meta: { title: 'TOKEN市集', onCache: true }
},
{
path: 'supplyAndDemandSquare',
@ -481,6 +500,28 @@ export const constantRoutes = [
* 需要根据用户角色动态加载的路由
*/
export const asyncRoutes = [
// NCMatch
{
path: '/ncmatch',
component: () => import('@/views/Ncmatch/HomeIndex.vue'),
name: 'ncmatch',
redirect: '/ncmatch/opcOverview',
meta: { title: 'NCMatch', fullPath: '/ncmatch', noCache: true },
children: [
{
path: 'opcOverview',
component: () => import('@/views/Ncmatch/opcOverview.vue'),
name: 'ncmatchOpcOverview',
meta: { title: 'OPC社区概况', fullPath: '/ncmatch/opcOverview', noCache: true }
},
{
path: 'societyNews',
component: () => import('@/views/Ncmatch/SocietyNewsBrief.vue'),
name: 'ncmatchSocietyNews',
meta: { title: '政社快讯', fullPath: '/ncmatch/societyNews', noCache: true }
}
]
},
// 财务——财务概览
{
path: '/financialOverview',

View File

@ -0,0 +1,594 @@
<template>
<el-dialog
:visible.sync="dialogVisible"
:fullscreen="true"
custom-class="mobile-consult-fullscreen-dialog"
:append-to-body="true"
:close-on-click-modal="false"
:close-on-press-escape="!submitSuccess"
:show-close="false"
@close="handleClose"
>
<div v-if="submitSuccess" class="submit-success-page">
<div class="success-icon">
<i class="el-icon-check"></i>
</div>
<h2 class="success-title">已完成</h2>
<p class="success-desc">您的咨询信息已提交我们会尽快与您联系</p>
</div>
<div v-else class="mobile-consult-wrap">
<h2 class="mobile-consult-title">{{ title }}</h2>
<p class="mobile-consult-subtitle">期待与您开启AI创新未来</p>
<el-form
ref="consultForm"
:model="formData"
:rules="rules"
label-position="top"
class="mobile-consult-form"
>
<el-form-item label="1. 请填写您的姓名" prop="name">
<el-input
v-model.trim="formData.name"
maxlength="20"
placeholder="请输入您的姓名"
/>
</el-form-item>
<el-form-item label="2. 请填写您的联系方式" prop="phone">
<el-input
v-model.trim="formData.phone"
maxlength="11"
placeholder="请输入您的联系电话"
/>
</el-form-item>
<el-form-item label="3. 请填写您的邮箱" prop="email">
<el-input
v-model.trim="formData.email"
maxlength="60"
placeholder="请输入您的邮箱"
/>
</el-form-item>
<el-form-item label="4. 请填写您的公司名称" prop="company">
<el-input
v-model.trim="formData.company"
maxlength="80"
placeholder="请输入您的公司名称"
/>
</el-form-item>
<el-form-item label="5. 请选择您的企业类型(单选)" prop="enterprise_type">
<el-radio-group v-model="formData.enterprise_type" class="option-grid">
<el-radio
v-for="item in enterpriseOptions"
:key="item.id"
:label="String(item.id)"
class="option-item"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="6. 请选择您所在的省份(单选)" prop="region">
<el-select
:key="provinceSelectKey"
v-model="formData.region"
filterable
placeholder="输入省份名称搜索..."
class="full-select"
>
<el-option
v-for="item in provinceOptions"
:key="`region-${item.id}-${item.name}`"
:label="item.name"
:value="String(item.id)"
/>
</el-select>
</el-form-item>
<el-form-item label="7. 您想咨询的方向(可多选)">
<el-checkbox-group v-model="formData.consult_direction_list" class="direction-list">
<el-checkbox
v-for="item in consultDirectionOptions"
:key="item.id"
:label="item.id"
class="direction-item"
>
<span class="direction-main">{{ item.title }}</span>
<span class="direction-sub">{{ item.desc }}</span>
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="8. 如果您有其他问题需要咨询,请留言">
<el-input
v-model.trim="formData.content"
type="textarea"
:autosize="{ minRows: 3, maxRows: 5 }"
maxlength="500"
show-word-limit
placeholder="请输入您想咨询的内容..."
/>
</el-form-item>
</el-form>
<div class="qrcode-area">
<p>长按二维码添加微信获取专属服务</p>
<img src="@/assets/image/newkefu.png" alt="官方客服二维码">
</div>
<el-checkbox v-model="formData.checked" class="privacy-checkbox">
<span>
您填写的信息仅用于本次业务对接沟通公司将严格落实信息安全保护机制不泄露不滥用您的任何个人资料
</span>
</el-checkbox>
<el-button
type="primary"
class="submit-btn"
:loading="loading"
@click="handleSubmit"
>
提交咨询
</el-button>
</div>
</el-dialog>
</template>
<script>
import { reqConsultForm, reqProductConsult } from '@/api/H5'
export default {
name: 'MobileFullScreenConsultDialog',
props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '开元云科技合作咨询'
},
qrCode: {
type: String,
default: ''
},
currentUrl: {
type: String,
default: ''
},
submitApi: {
type: Function,
default: null
}
},
data() {
const validatePhone = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入联系电话'))
} else if (!/^1[3-9]\d{9}$/.test(value)) {
callback(new Error('请输入正确的手机号'))
} else {
callback()
}
}
return {
loading: false,
formData: this.getInitialFormData(),
enterpriseOptions: [],
provinceOptions: [],
provinceSelectKey: 0,
consultDirectionOptions: [],
submitSuccess: false,
rules: {
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
phone: [{ required: true, validator: validatePhone, trigger: 'blur' }],
email: [
{ required: true, message: '请输入邮箱', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
],
company: [{ required: true, message: '请输入公司名称', trigger: 'blur' }],
enterprise_type: [{ required: true, message: '请选择企业类型', trigger: 'change' }],
region: [{ required: true, message: '请选择所在省份', trigger: 'change' }]
},
originalOverflow: ''
}
},
computed: {
dialogVisible: {
get() {
return this.visible
},
set(value) {
this.$emit('update:visible', value)
}
}
},
watch: {
visible: {
immediate: true,
handler(newVal) {
if (newVal) {
this.resetForm()
this.fetchConsultOptions()
this.disableBodyScroll()
} else {
this.enableBodyScroll()
}
}
}
},
methods: {
getInitialFormData() {
return {
custom_type: '1',
name: '',
phone: '',
email: '',
company: '',
enterprise_type: '',
region: '',
consult_direction_list: [],
content: '',
checked: false
}
},
resetForm() {
this.formData = this.getInitialFormData()
this.submitSuccess = false
this.$nextTick(() => {
if (this.$refs.consultForm) this.$refs.consultForm.clearValidate()
})
},
disableBodyScroll() {
this.originalOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
},
enableBodyScroll() {
document.body.style.overflow = this.originalOverflow || ''
},
handleClose() {
this.dialogVisible = false
this.$emit('close')
},
normalizeDictOptions(list, type) {
return list
.filter(item => item && item.dict_type === type)
.sort((a, b) => Number(a.sort_order || 0) - Number(b.sort_order || 0))
.map(item => ({
id: String(item.dict_key),
name: item.dict_value_zh
}))
},
normalizeDirectionOptions(list) {
return list
.filter(item => item && item.dict_type === 'direction')
.sort((a, b) => Number(a.sort_order || 0) - Number(b.sort_order || 0))
.map(item => {
const value = item.dict_value_zh || ''
const match = value.match(/^(.+?)(.+?)$/)
return {
id: String(item.dict_key),
name: value,
title: match ? match[1] : value,
desc: match ? match[2] : ''
}
})
},
extractConsultOptionList(res) {
if (res && Array.isArray(res.data)) return res.data
if (res && res.data && Array.isArray(res.data.data)) return res.data.data
return []
},
async fetchConsultOptions() {
try {
const res = await reqConsultForm()
const list = this.extractConsultOptionList(res)
if (!list.length) return
const enterpriseOptions = this.normalizeDictOptions(list, 'enterprise_type')
const provinceOptions = this.normalizeDictOptions(list, 'region')
const consultDirectionOptions = this.normalizeDirectionOptions(list)
if (enterpriseOptions.length) this.enterpriseOptions = enterpriseOptions
if (provinceOptions.length) {
this.provinceOptions = provinceOptions
this.provinceSelectKey += 1
if (this.formData.region && !provinceOptions.some(item => item.id === String(this.formData.region))) {
this.formData.region = ''
}
}
if (consultDirectionOptions.length) this.consultDirectionOptions = consultDirectionOptions
} catch (error) {
//
}
},
buildSubmitPayload() {
return {
custom_type: this.formData.custom_type,
name: this.formData.name,
phone: this.formData.phone,
email: this.formData.email,
company: this.formData.company,
enterprise_type: this.formData.enterprise_type,
region: this.formData.region,
consult_direction: this.formData.consult_direction_list.join(','),
content: this.formData.content,
source: '公众号',
url_link: this.currentUrl || window.location.href
}
},
async handleSubmit() {
if (!this.formData.checked) {
this.$message.warning('请先勾选信息安全与隐私保护说明')
return
}
this.$refs.consultForm.validate(async valid => {
if (!valid) return
this.loading = true
try {
const payload = this.buildSubmitPayload()
const response = this.submitApi ? await this.submitApi(payload) : await reqProductConsult(payload)
if (response && (response.status === true || response.status === 'true')) {
this.$emit('success', response)
this.submitSuccess = true
} else {
this.$message.error((response && response.msg) || '提交失败,请稍后再试')
}
} catch (error) {
this.$message.error('提交失败,请稍后再试')
} finally {
this.loading = false
}
})
}
},
beforeDestroy() {
this.enableBodyScroll()
}
}
</script>
<style scoped lang="less">
::v-deep .mobile-consult-fullscreen-dialog {
margin: 0 !important;
width: 100% !important;
max-width: 100% !important;
height: 100vh;
border-radius: 0;
.el-dialog__header {
padding: 14px 14px 8px;
border-bottom: 0;
}
.el-dialog__title {
font-size: 18px;
font-weight: 700;
color: #111827;
}
.el-dialog__body {
height: calc(100vh - 60px);
padding: 12px 14px 18px;
overflow-y: auto;
}
}
.mobile-consult-wrap {
padding-bottom: 8px;
}
.submit-success-page {
min-height: calc(100vh - 110px);
padding: 80px 24px 24px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
box-sizing: border-box;
}
.success-icon {
width: 88px;
height: 88px;
border-radius: 50%;
background: #22c55e;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 14px 28px rgba(34, 197, 94, 0.22);
i {
font-size: 46px;
font-weight: 700;
}
}
.success-title {
margin: 24px 0 10px;
color: #111827;
font-size: 24px;
font-weight: 700;
}
.mobile-consult-title {
margin: 0;
font-size: 24px;
line-height: 1.25;
color: #111827;
}
.mobile-consult-subtitle {
margin: 6px 0 16px;
font-size: 12px;
color: #9ca3af;
}
.mobile-consult-form {
::v-deep .el-form-item {
margin-bottom: 18px;
}
::v-deep .el-form-item__content,
::v-deep .el-input,
::v-deep .el-select,
::v-deep .el-textarea {
width: 100%;
}
::v-deep .el-form-item__label {
padding-bottom: 6px;
font-size: 13px;
line-height: 1.3;
color: #1f2937;
font-weight: 600;
}
::v-deep .el-form-item__error {
position: static;
padding-top: 6px;
line-height: 1.25;
font-size: 12px;
}
::v-deep .el-input__inner {
height: 40px;
border-radius: 10px;
border: 1px solid #e5e7eb;
}
::v-deep .el-form-item.is-error .el-input__inner,
::v-deep .el-form-item.is-error .el-textarea__inner {
border-color: #f56c6c;
}
::v-deep .el-textarea__inner {
border-radius: 10px;
border: 1px solid #e5e7eb;
}
}
.option-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
width: 100%;
}
.option-item {
margin-right: 0 !important;
padding: 9px 8px;
border: 1px solid #e5e7eb;
border-radius: 10px;
white-space: nowrap;
box-sizing: border-box;
display: flex;
align-items: center;
width: 100%;
::v-deep .el-radio__label {
padding-left: 6px;
font-size: 12px;
color: #374151;
}
}
.full-select {
width: 100%;
}
.direction-list {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
}
.direction-item {
margin-right: 0 !important;
border: 1px solid #e5e7eb;
border-radius: 10px;
padding: 10px;
display: flex;
align-items: flex-start;
width: 100%;
box-sizing: border-box;
::v-deep .el-checkbox__label {
display: inline-flex;
flex-direction: column;
gap: 2px;
padding-left: 8px;
}
}
.direction-main {
font-size: 14px;
color: #111827;
line-height: 1.2;
}
.direction-sub {
font-size: 12px;
color: #9ca3af;
line-height: 1.2;
}
.qrcode-area {
margin: 16px 0;
text-align: center;
padding: 18px 0;
background: #f7f8fa;
border-radius: 12px;
p {
margin: 0 0 10px;
font-size: 12px;
color: #6b7280;
}
img {
width: 98px;
height: 98px;
border-radius: 8px;
border: 1px dashed #3b82f6;
}
}
.privacy-checkbox {
margin: 0 0 14px;
display: flex;
align-items: flex-start;
::v-deep .el-checkbox__input {
margin-top: 3px;
}
::v-deep .el-checkbox__label {
padding-left: 8px;
color: #6b7280;
font-size: 12px;
line-height: 1.6;
white-space: normal;
}
}
.submit-btn {
width: 100%;
height: 44px;
border: none;
border-radius: 999px;
background: #111827;
color: #fff;
font-size: 16px;
font-weight: 600;
}
</style>

View File

@ -0,0 +1,42 @@
<template>
<div class="consult-full-preview">
<mobile-full-screen-consult-dialog
:visible.sync="dialogVisible"
:current-url="currentUrl"
/>
</div>
</template>
<script>
import MobileFullScreenConsultDialog from '../components/H5_dialog/MobileFullScreenConsultDialog.vue'
export default {
name: 'ConsultDialogFullPreview',
components: { MobileFullScreenConsultDialog },
data() {
return {
dialogVisible: true
}
},
computed: {
currentUrl() {
return window.location.href
}
}
}
</script>
<style scoped lang="less">
.consult-full-preview {
min-height: 100%;
}
.preview-actions {
padding: 12px;
display: flex;
justify-content: center;
}
</style>

View File

@ -0,0 +1,40 @@
<template>
<div class="container">
<!-- 头部 -->
<div class="header">
<topNav />
</div>
<!-- 内容 -->
<div class="content">
<router-view />
</div>
</div>
</template>
<script>
import topNav from './topNav.vue';
export default {
name: 'ComponentName',
components: { topNav },
data() {
return {
};
},
computed: {
},
methods: {
}
};
</script>
<style scoped>
.container {
width: 100%;
height: 100%;
overflow-y: auto;
}
</style>

View File

@ -0,0 +1,360 @@
<template>
<el-dialog
:visible.sync="dialogVisible"
:append-to-body="true"
:close-on-click-modal="false"
:show-close="false"
custom-class="opc-application-dialog"
@close="handleClose"
>
<div class="dialog-layout">
<!-- 头部 -->
<header class="dialog-header">
<div class="header-info">
<h2 class="header-title">紫东数智OPC社区入驻申请表</h2>
<p class="header-desc">请如实填写以下信息我们将尽快与您联系</p>
</div>
<button type="button" class="close-btn" @click="handleClose">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 6L6 18M6 6l12 12"/>
</svg>
</button>
</header>
<!-- 主体 -->
<main class="dialog-body">
<el-form ref="form" :model="formData" :rules="rules" label-position="top" class="opc-form">
<!-- 第一部分企业基本信息 -->
<section class="form-section">
<h3 class="section-title">
<span class="section-num">1</span>
企业基本信息
</h3>
<div class="grid-2">
<el-form-item label="企业名称" prop="company_name">
<el-input
v-model="formData.company_name"
placeholder="请输入企业名称"
maxlength="100"
/>
</el-form-item>
<el-form-item label="统一社会信用代码">
<el-input
v-model="formData.credit_code"
placeholder="请输入统一社会信用代码"
maxlength="18"
/>
</el-form-item>
<el-form-item label="成立时间">
<el-date-picker
v-model="formData.est_date"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
class="full-width"
/>
</el-form-item>
<el-form-item label="从业人数(人)">
<el-input
v-model.number="formData.employee_count"
type="number"
placeholder="请输入从业人数"
min="1"
/>
<p class="field-tip">以最近一个月实际缴纳社保为准</p>
</el-form-item>
<el-form-item label="企业性质" class="col-span-2">
<div class="checkbox-group">
<label
v-for="item in companyTypeOptions"
:key="item"
class="checkbox-label"
:class="{ active: formData.company_type.includes(item) }"
>
<input
type="checkbox"
:value="item"
v-model="formData.company_type"
/>
<span>{{ item }}</span>
</label>
</div>
</el-form-item>
<el-form-item label="所属细分行业" class="col-span-2">
<el-input
v-model="formData.industry"
placeholder="请输入所属细分行业"
maxlength="100"
/>
</el-form-item>
<el-form-item label="注册地址">
<el-input
v-model="formData.reg_address"
placeholder="请输入注册地址"
maxlength="200"
/>
</el-form-item>
<el-form-item label="办公地址">
<el-input
v-model="formData.office_address"
placeholder="请输入办公地址"
maxlength="200"
/>
</el-form-item>
<el-form-item label="企业简介不超过1000字" class="col-span-2">
<el-input
v-model="formData.company_intro"
type="textarea"
:rows="4"
maxlength="1000"
show-word-limit
placeholder="请简要介绍企业主营业务、发展历程、核心优势等"
/>
</el-form-item>
</div>
</section>
<!-- 第二部分创始人信息 -->
<section class="form-section">
<h3 class="section-title">
<span class="section-num">2</span>
创始人信息
</h3>
<div class="grid-2">
<el-form-item label="单一创始人姓名" prop="founder_name">
<el-input
v-model="formData.founder_name"
placeholder="请输入创始人姓名"
maxlength="50"
/>
</el-form-item>
<el-form-item label="经股权穿透后合计持股比例(%">
<el-input
v-model.number="formData.shareholding_ratio"
type="number"
placeholder="请输入持股比例"
min="0"
max="100"
/>
</el-form-item>
<el-form-item label="联系电话" prop="founder_phone">
<el-input
v-model="formData.founder_phone"
placeholder="请输入联系电话"
maxlength="20"
/>
</el-form-item>
<el-form-item label="创始人在企业的技术创新、战略决策和主要业务执行中发挥主导作用情况" class="col-span-2">
<el-input
v-model="formData.founder_role"
type="textarea"
:rows="3"
placeholder="请说明创始人在企业技术创新、战略决策和主要业务执行中发挥的主导作用"
maxlength="500"
/>
</el-form-item>
<el-form-item label="上传相关证明文件(可选)" class="col-span-2">
<div class="file-upload-area">
<label class="upload-btn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13"/>
</svg>
<span>选择文件</span>
<input
type="file"
class="hidden"
accept=".pdf,.doc,.docx,.jpg,.jpeg,.png"
@change="handleFileChange($event, 'founder_file_list')"
/>
</label>
<div class="file-list">
<div
v-for="(file, index) in formData.founder_file_list"
:key="index"
class="file-item"
>
<span class="file-name">{{ file.name }}</span>
<button type="button" class="file-remove" @click="removeFile(index, 'founder_file_list')">×</button>
</div>
</div>
</div>
</el-form-item>
<el-form-item label="核心技术情况" class="col-span-2">
<el-input
v-model="formData.core_tech"
type="textarea"
:rows="3"
placeholder="以人工智能技术作为核心生产工具与关键驱动力利用大模型、智能体、AI开发工具等完成核心产品开发"
maxlength="500"
/>
</el-form-item>
</div>
</section>
<!-- 第三部分联系人信息 -->
<section class="form-section">
<h3 class="section-title">
<span class="section-num">3</span>
联系人信息
</h3>
<div class="grid-3">
<el-form-item label="联系人姓名" prop="contact_name">
<el-input
v-model="formData.contact_name"
placeholder="请输入联系人姓名"
maxlength="50"
/>
</el-form-item>
<el-form-item label="职务">
<el-input
v-model="formData.job_title"
placeholder="请输入职务"
maxlength="50"
/>
</el-form-item>
<el-form-item label="联系电话" prop="contact_phone">
<el-input
v-model="formData.contact_phone"
placeholder="请输入联系电话"
maxlength="20"
/>
</el-form-item>
</div>
</section>
</el-form>
</main>
<!-- 底部 -->
<footer class="dialog-footer">
<el-button type="default" @click="saveDraft">保存草稿</el-button>
<el-button type="default" @click="handleClose">取消</el-button>
<el-button type="primary" :loading="submitting" @click="submitForm">提交申请</el-button>
</footer>
</div>
</el-dialog>
</template>
<script>
export default {
name: 'OpcApplicationForm',
props: {
visible: {
type: Boolean,
default: false
}
},
data() {
const validatePhone = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入联系电话'))
} else if (!/^1[3-9]\d{9}$/.test(value)) {
callback(new Error('请输入正确的手机号'))
} else {
callback()
}
}
return {
submitting: false,
companyTypeOptions: ['国有企业', '国有控股企业', '私营企业', '外资企业', '合资企业', '个体工商户', '其他'],
formData: {
company_name: '',
credit_code: '',
est_date: '',
employee_count: null,
company_type: [],
industry: '',
reg_address: '',
office_address: '',
company_intro: '',
founder_name: '',
shareholding_ratio: null,
founder_phone: '',
founder_role: '',
founder_file_list: [],
core_tech: '',
contact_name: '',
job_title: '',
contact_phone: ''
},
rules: {
company_name: [{ required: true, message: '请输入企业名称', trigger: 'blur' }],
founder_name: [{ required: true, message: '请输入创始人姓名', trigger: 'blur' }],
founder_phone: [{ required: true, validator: validatePhone, trigger: 'blur' }],
contact_name: [{ required: true, message: '请输入联系人姓名', trigger: 'blur' }],
contact_phone: [{ required: true, validator: validatePhone, trigger: 'blur' }]
}
}
},
computed: {
dialogVisible: {
get() {
return this.visible
},
set(val) {
this.$emit('update:visible', val)
}
}
},
watch: {
visible(val) {
if (val) {
this.loadDraft()
}
}
},
methods: {
handleClose() {
this.dialogVisible = false
this.$emit('close')
},
handleFileChange(event, field) {
const files = Array.from(event.target.files)
if (files.length) {
this.formData[field] = [...this.formData[field], ...files]
}
event.target.value = ''
},
removeFile(index, field) {
this.formData[field].splice(index, 1)
},
saveDraft() {
localStorage.setItem('opc_application_draft', JSON.stringify(this.formData))
this.$message.success('草稿已保存')
},
loadDraft() {
try {
const draft = localStorage.getItem('opc_application_draft')
if (draft) {
const data = JSON.parse(draft)
this.formData = { ...this.formData, ...data }
}
} catch (e) {
// ignore
}
},
submitForm() {
this.$refs.form.validate(async valid => {
if (!valid) return
this.submitting = true
try {
// TODO:
console.log('提交数据:', this.formData)
this.$message.success('提交成功')
localStorage.removeItem('opc_application_draft')
this.handleClose()
} catch (e) {
this.$message.error('提交失败,请重试')
} finally {
this.submitting = false
}
})
}
}
}
</script>
<style scoped lang="less">
@import url('./less/OpcJoinDialog.less');
</style>

View File

@ -0,0 +1,266 @@
<template>
<div class="society-news-page">
<div class="news-container">
<div class="news-header">
<h1 class="news-title">政社快讯</h1>
<p class="news-subtitle">第一时间追踪 OPC 政策风向与社区动态助力创业者精准把握发展机遇</p>
</div>
<div class="filter-row">
<button v-for="tab in filterTabs" :key="tab.key" type="button" class="filter-btn"
:class="{ active: activeFilter === tab.key }" @click="filterActivities(tab.key)">
{{ tab.label }}
</button>
</div>
<div class="activity-list" id="activity-list">
<div v-for="(item, index) in filteredActivities" :key="item.id" class="activity-card glass-card"
:data-tag="item.tag" @click="openDetail(item)">
<div class="activity-row">
<div class="activity-date">
<div class="date-block" :class="['date-block-' + item.theme]">
<span class="date-day">{{ item.day }}</span>
<span class="date-month">{{ item.month }}</span>
</div>
</div>
<div class="activity-main">
<div class="activity-meta">
<span class="tag" :class="['tag-' + item.theme]">{{ item.tag }}</span>
<span class="meta-source">{{ item.source }}</span>
</div>
<h3 class="activity-title">{{ item.title }}</h3>
<p class="activity-desc">{{ item.desc }}</p>
</div>
<div class="activity-arrow">
<svg class="arrow-icon" width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
</svg>
</div>
</div>
</div>
</div>
</div>
<!-- 详情弹窗 -->
<transition name="detail-fade">
<div v-if="detailVisible && currentDetail" class="detail-overlay" @click.self="closeDetail">
<div class="detail-panel">
<!-- 弹窗头部 -->
<div class="detail-sticky-header">
<div class="detail-header-left">
<span class="detail-tag" :class="['tag-' + currentDetail.theme]">{{ currentDetail.tag }}</span>
<span class="detail-publisher">{{ currentDetail.source }}</span>
</div>
<button class="detail-close" @click="closeDetail" aria-label="关闭">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<!-- 弹窗内容 -->
<div class="detail-body">
<h2 class="detail-title">{{ currentDetail.title }}</h2>
<div class="detail-meta">
<span v-if="currentDetail.location" class="detail-meta-item">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/>
<path d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
{{ currentDetail.location }}
</span>
<span v-if="currentDetail.date" class="detail-meta-item">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
{{ currentDetail.date }}
</span>
<a v-if="currentDetail.url" :href="currentDetail.url" target="_blank" rel="noopener noreferrer"
class="detail-meta-item detail-meta-link">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
</svg>
原文链接
</a>
</div>
<div class="detail-content" v-html="currentDetail.content"></div>
</div>
<!-- 弹窗底部 -->
<div class="detail-sticky-footer">
<button class="btn-close" @click="closeDetail">关闭</button>
</div>
</div>
</div>
</transition>
</div>
</template>
<script>
export default {
name: 'SocietyNewsBrief',
data() {
return {
detailVisible: false,
currentDetail: null,
activeFilter: 'all',
filterTabs: [
{ key: 'all', label: '全部快讯' },
{ key: '政策动态', label: '政策动态' },
{ key: '社区动态', label: '社区动态' }
],
activities: [
{
id: 'policy1',
tag: '政策动态',
theme: 'blue',
day: '18',
month: '2026.06',
source: '北京市经济和信息化局',
location: '北京市',
date: '2026年6月18日',
url: 'https://jxj.beijing.gov.cn/zwgk/2024zcwj/202606/t20260618_4706233.html',
title: '北京市正式印发《支持人工智能OPC创新发展行动方案》试行',
desc: '北京市经济和信息化局正式印发《支持人工智能OPC创新发展行动方案》试行从培育孵化、算力支撑、场景开放、融资渠道、人才引进五大维度系统构建OPC创业支持体系。',
content: `
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
为落实国家"人工智能+"战略构建活跃开放的人工智能创新创业生态支持青年人才创新创业助力超级经济个体OPC快速成长近日北京市经济和信息化局正式印发<strong style="color:#111827;">支持人工智能OPC创新发展行动方案试行</strong>方案围绕支持创办OPC企业建设OPC社区发放"全栈资源包"加强融资增信赋能强化赛事培优赋能举办全球OPC共创节深化合规引导赋能支持高校设立OPC微专业八大重点领域出台8条支持举措
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">北京市经济和信息化局关于印发支持人工智能OPC创新发展行动方案试行的通知</strong>
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">各区中小企业主管部门各有关单位</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">现将支持人工智能OPC创新发展行动方案试行印发给你们请结合实际认真贯彻落实</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">北京市经济和信息化局</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">2026年6月17日</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">支持人工智能OPC创新发展行动方案试行</strong>
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
为深入贯彻落实国家"人工智能+"战略部署加快构建活跃开放的人工智能创新创业生态支持青年人才创新创业培育一批AI驱动小而精专而强的创新主体助力更多超级经济个体OPC快速成长壮大本市中小企业科技创新力量制定本行动方案
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">支持创办智能体开发</strong>AIGC产品创制大模型微调AI行业落地应用等领域的OPC企业依托北京市中小企业公共服务平台打造"OPC开办服务专区"提供开办指引社区入驻政策辅导和订单对接等全链条服务联合高校产业园区龙头企业开展OPC创业"校园行""园区行""企业行"通过政策宣讲案例分享技术培训等鼓励各类科技人才创办OPC
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">出台OPC成长社区建设指引</strong>支持社区自建词元Token服务平台或链接智算服务机构数字员工服务平台为OPC企业提供AI工具集高质量数据集优惠算力低成本办公空间创业服务包等配套服务对于服务成效突出的OPC社区最高给予200万元资金支持鼓励新一代信息技术企业依托存量技术人才与产业生态资源探索建设企业内部OPC社区
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">通过OPC成长社区为入驻OPC企业发放"全栈资源包"</strong>包含Token券模型券算力券数据券等入驻企业可免费享受3个月的模型使用智能算力使用数据代采赠送等服务单个企业合计最高不超过10万元OPC成长社区可按季度申请兑付每个社区每年最高不超过1000万元对通过北京人工智能数据沙盒服务平台开展数据治理和模型应用的OPC企业可享受50%服务费减免
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">依托北京市企业融资综合信用服务平台设立"OPC金融服务专区"</strong>提供担保增信融资撮合政策对接等服务联合金融机构开发适配OPC企业的专属信贷产品"OPC贷"综合运用Token消耗数据赛事获奖情况创始人征信报告自主知识产权与自研技术成果等内容为企业提供增信支持
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">联合一批投资机构举办"创赢未来"OPC专场路演活动</strong>由OPC社区推荐OPC企业参赛针对参赛企业提供"创训营"综合赋能服务对路演通过的OPC项目最高给予1000万元资金支持依托"创客北京""京彩大创"设立OPC专项赛道对于优质项目最高给予50万元资金支持
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">举办全球OPC共创节</strong>通过AI黑客松AI工作坊项目路演大咖分享成果发布等系列活动为OPC企业提供项目展示的国际舞台和资源对接平台打造连接开发者创作者企业与大众的创新生态支持符合条件OPC企业参加中小企业服务券服务商遴选依托服务券政策推动其产品在中小企业中落地应用
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">编制OPC合规指引</strong>梳理财税管理知识产权算法备案跨境出海等领域的风险要点明确合规要求和操作规范联合专业服务机构开展"合规服务进社区"活动通过合规咨询实操指导推动合规服务深度对接提升OPC企业合规经营意识与风险防控能力
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">支持高校依托学科特色设立OPC微专业</strong>聚焦"人工智能大模型工程应用OPC创业实践"构建"AI通识政策解读场景实战"课程体系推行校企"双师制"教学推动形成"学科建设—产业需求—人才培养"闭环育人体系打造校企政合作共建的实训基地鼓励OPC社区开办"夜校""加速营"等OPC创业培训
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0;">
本方案自印发之日起试行由北京市经济和信息化局负责解释
</p>
`
},
{
id: 'policy2',
tag: '政策动态',
theme: 'green',
day: '10',
month: '2026.07',
source: '石景山区人民政府',
location: '石景山区',
date: '2026年7月10日',
url: '',
title: '石景山区关于支持OPC创业生态发展的若干措施',
desc: '石景山区出台专项措施支持OPC创业生态发展涵盖入驻补贴、算力支持、融资对接、人才保障等多项扶持政策打造OPC创业首选地。',
content: `
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
石景山区人民政府正式出台关于支持OPC创业生态发展的若干措施围绕OPC企业入驻算力支撑融资渠道人才保障等关键环节推出多项专项扶持政策旨在将石景山区打造为北京OPC创业首选地
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">办公空间支持</strong>新入驻 OPC 企业首年办公租金全免第二年减免50%第三年减免30%单家最高可节省租金支出约500万元符合条件的企业可优先入驻石景山区人工智能产业园享受"一事一议"专项支持
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">算力补贴政策</strong>联合区内智算中心为OPC企业提供专项算力包单家最高补贴200万元/企业可享受不高于市场价的80%算力采购折扣同时可申请算力"先用后付"试点服务
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">融资对接机制</strong>设立 OPC 创投联盟搭建"基金 + 直投"双轨融资通道区级产业发展基金优先跟投优质OPC项目单笔跟投最高5000万元定期举办OPC项目路演及资本对接会
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">人才引进保障</strong>为OPC企业引进的高层次人才提供最高100万元安家补贴在住房子女入学医疗等方面给予优先保障设立"石景山OPC人才服务专线"提供一对一政策咨询服务
</p>
<p style="color:#374151;font-size:0.875rem;margin-bottom:0;">
<strong style="color:#111827;">政务绿色通道</strong>开通 OPC 项目"绿色通道"实行专班专员全程服务企业注册资质审批政策申报等事项平均提速50%以上
</p>
`
},
{
id: 'community1',
tag: '社区动态',
theme: 'violet',
day: '14',
month: '2026.07',
source: '紫东数智OPC社区',
location: '北京市',
date: '2026年7月14日',
url: '',
title: '紫东数智OPC社区正式揭牌',
desc: '紫东数智OPC社区正式揭牌成立标志着北京OPC创业生态建设迈入新阶段。社区由中关村数智人工智能产业联盟与紫东太初联合打造致力于构建集科研创新、项目孵化、创业加速与产业转化于一体的新型生态社区。',
content: `
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
紫东数智OPC社区正式揭牌成立标志着北京OPC创业生态建设迈入新阶段社区由中关村数智人工智能产业联盟与紫东太初联合打造致力于构建集科研创新项目孵化创业加速与产业转化于一体的新型生态社区
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">孵化空间</strong>社区首期启用2万平方米孵化空间覆盖50+创业团队配备独立办公室共享工位及会议室满足OPC企业不同阶段的空间需求
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">产学研合作</strong>联合清华北航北理工等高校共建OPC联合实验室围绕大模型应用智能体开发AI行业落地等方向开展联合技术攻关推动科研成果转化
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">品牌活动</strong>每月举办AI沙龙项目路演产业对接等品牌活动邀请学术界产业界投资界嘉宾分享前沿洞见为OPC创业者搭建高质量交流平台
</p>
<p style="color:#374151;font-size:0.875rem;line-height:1.9;margin-bottom:0.75rem;">
<strong style="color:#111827;">公共服务</strong>配套算力数据模型"一站式"公共服务平台提供优惠算力调用高质量数据集获取主流大模型API接入等服务降低OPC企业创新门槛
</p>
<p style="color:#374151;font-size:0.875rem;margin-bottom:0;">
<strong style="color:#111827;">发展目标</strong>2026年内计划孵化30个标杆性OPC创业项目培育5家以上估值超亿元的OPC头部企业形成具有全国影响力的OPC创新高地
</p>
`
}
]
}
},
computed: {
filteredActivities() {
if (this.activeFilter === 'all') return this.activities
return this.activities.filter(item => item.tag === this.activeFilter)
}
},
methods: {
filterActivities(key) {
this.activeFilter = key
},
openDetail(item) {
this.currentDetail = item
this.detailVisible = true
},
closeDetail() {
this.detailVisible = false
},
}
}
</script>
<style scoped lang="less">
@import url('./less/SocietyNewsBrief.less');
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 907 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 KiB

View File

@ -0,0 +1,135 @@
<template>
<div class="container">
<!-- slogan -->
<div class="slogan">
<div class="title">
<span class="gradient-text">数智开物</span>
<span class="clip-text">opc公共服务平台</span>
<span class="desp">紫东数智OPC社区 × 数智开物OPC公共服务平台超级个体从这里启航</span>
</div>
<div class="MetricsDisplay">
<div class="item">
<div class="item-title">
3500<span></span>
</div>
<div class="item-details">
社区建筑面积
</div>
</div>
<div class="item">
<div class="item-title">
3500<span></span>
</div>
<div class="item-details">
社区建筑面积
</div>
</div>
<div class="item">
<div class="item-title">
3500<span></span>
</div>
<div class="item-details">
社区建筑面积
</div>
</div>
<div class="item">
<div class="item-title">
3500<span></span>
</div>
<div class="item-details">
社区建筑面积
</div>
</div>
</div>
</div>
<!-- 优惠政策 -->
<div class="policies">
</div>
</div>
</template>
<script>
export default {
name: 'ComponentName',
data() {
return {
};
},
computed: {
},
methods: {
}
};
</script>
<style scoped lang="less">
.container {
width: 100%;
height: 100%;
}
.slogan {
width: 100%;
height: 100%;
padding: 176px 0;
.title {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding-bottom: 62px;
.gradient-text {
font-size: 72px;
font-weight: 800;
background: linear-gradient(135deg, #1e40af 0%, #2563eb 40%, #3b82f6 70%, #0ea5e9 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.clip-text{
font-size: 72px;
font-weight: 800;
background: linear-gradient(to right, rgb(147, 51, 234), rgb(37, 99, 235), rgb(6, 182, 212));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 32px;
}
.desp{
font-size: 20px;
color:rgb(75, 85, 99)
}
}
.MetricsDisplay {
display: flex;
justify-content: center;
gap: 80px;
.item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.item-title{
font-size: 36px;
font-weight: 700;
span{
color: #2563EB;
}
}
.item-details{
font-size: 14px;
color: #6B7280;
}
}
}
}
</style>

View File

@ -0,0 +1,362 @@
::v-deep .opc-application-dialog {
margin: 0 auto !important;
width: 100% !important;
max-width: 900px;
// max-height: 85vh;
border-radius: 16px !important;
background: #ffffff;
border: 1px solid #dbeafe;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
overflow: hidden;
display: flex;
flex-direction: column;
.el-dialog__header {
display: none;
}
.el-dialog__body {
flex: 1;
padding: 0;
overflow: hidden;
min-height: 0;
}
}
::v-deep .el-dialog__wrapper {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
overflow: hidden;
}
.dialog-layout {
display: flex;
flex-direction: column;
height: 100%;
background: #ffffff;
overflow: hidden;
}
.dialog-header {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px 32px;
background: #ffffff;
border-bottom: 1px solid #f0f0f0;
.header-info {
.header-title {
margin: 0 0 4px;
font-size: 20px;
font-weight: 700;
color: #111827;
}
.header-desc {
margin: 0;
font-size: 13px;
color: #6b7280;
}
}
.close-btn {
width: 32px;
height: 32px;
border: none;
border-radius: 8px;
background: #f3f4f6;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: #6b7280;
transition: all 0.2s;
&:hover {
background: #e5e7eb;
color: #374151;
}
}
}
.dialog-body {
flex: 1;
overflow-y: auto;
padding: 24px 32px;
}
.dialog-footer {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
padding: 16px 32px;
background: #f9fafb;
border-top: 1px solid #f0f0f0;
.el-button--primary {
background: linear-gradient(135deg, #2563eb, #06b6d4);
border: none;
border-radius: 8px;
padding: 0 24px;
height: 40px;
font-weight: 500;
font-size: 14px;
transition: opacity 0.2s;
&:hover {
opacity: 0.9;
}
}
.el-button {
border-radius: 8px;
font-size: 14px;
height: 40px;
}
}
.form-section {
margin-bottom: 32px;
.section-title {
display: flex;
align-items: center;
gap: 8px;
margin: 0 0 16px;
font-size: 15px;
font-weight: 600;
color: #111827;
.section-num {
width: 24px;
height: 24px;
border-radius: 50%;
background: #dbeafe;
color: #2563eb;
font-size: 12px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
}
}
}
.grid-2 {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
.col-span-2 {
grid-column: 1 / -1;
}
}
.grid-3 {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
.full-width {
width: 100%;
}
.field-tip {
margin: 4px 0 0;
font-size: 12px;
color: #9ca3af;
}
.checkbox-group {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
background: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 8px;
cursor: pointer;
transition: all 0.15s;
font-size: 14px;
color: #374151;
input {
display: none;
}
&:hover {
border-color: #2563eb;
color: #2563eb;
}
&.active {
background: #eff6ff;
border-color: #2563eb;
color: #2563eb;
font-weight: 500;
}
}
.file-upload-area {
display: flex;
flex-direction: column;
gap: 8px;
}
.upload-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
border: 1px dashed #2563eb;
border-radius: 8px;
color: #2563eb;
font-size: 14px;
cursor: pointer;
width: fit-content;
transition: all 0.15s;
&:hover {
background: #eff6ff;
}
input {
display: none;
}
}
.file-list {
display: flex;
flex-direction: column;
gap: 4px;
}
.file-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 12px;
background: #f9fafb;
border-radius: 6px;
font-size: 13px;
color: #374151;
.file-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.file-remove {
border: none;
background: none;
color: #9ca3af;
font-size: 16px;
cursor: pointer;
padding: 0 4px;
line-height: 1;
&:hover {
color: #ef4444;
}
}
}
::v-deep .opc-form {
.el-form-item {
margin-bottom: 0;
}
.el-form-item__label {
padding-bottom: 6px;
font-size: 14px;
font-weight: 500;
color: #374151;
line-height: 1.4;
}
.el-form-item__error {
position: static;
padding-top: 4px;
font-size: 12px;
}
.el-input__inner,
.el-textarea__inner {
border-radius: 8px;
border-color: #e5e7eb;
background: #f9fafb;
font-size: 14px;
color: #111827;
&::placeholder {
color: #9ca3af;
}
&:focus {
border-color: #2563eb;
background: #ffffff;
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
}
.el-input {
.el-input__inner {
height: 40px;
}
}
.el-textarea__inner {
resize: none;
}
.el-checkbox__input.is-checked .el-checkbox__inner {
background-color: #2563eb;
border-color: #2563eb;
}
.el-checkbox__label {
color: #374151;
font-size: 14px;
}
}
@media (max-width: 768px) {
.grid-2,
.grid-3 {
grid-template-columns: 1fr;
}
.col-span-2 {
grid-column: 1;
}
.dialog-header {
padding: 16px 20px;
}
.dialog-body {
padding: 20px 16px;
}
.dialog-footer {
padding: 12px 16px;
}
::v-deep .opc-application-dialog {
margin: 16px auto !important;
max-height: calc(100vh - 32px);
}
}

View File

@ -0,0 +1,540 @@
.society-news-page {
position: relative;
z-index: 10;
padding: 56px 0 80px;
height: 100%;
//height: calc(100vh - 64px);
background: linear-gradient(135deg, #f0f4ff 0%, #ffffff 50%, #f8fafc 100%);
}
.news-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 24px;
width: 100%;
box-sizing: border-box;
}
.news-header {
text-align: center;
margin-bottom: 48px;
}
.news-title {
margin: 0 0 16px;
font-size: 36px;
font-weight: 700;
background: linear-gradient(135deg, #3b82f6 0%, #06b6d4 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.news-subtitle {
margin: 0 auto;
max-width: 576px;
font-size: 16px;
line-height: 1.625;
color: #6b7280;
}
.filter-row {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 48px;
}
.filter-btn {
padding: 8px 20px;
border-radius: 9999px;
font-size: 14px;
font-weight: 500;
background: #ffffff;
color: #6b7280;
border: 1px solid #e5e7eb;
cursor: pointer;
transition: all 0.2s;
&:hover {
background: #eff6ff;
color: #2563eb;
border-color: #93c5fd;
}
&.active {
background: linear-gradient(135deg, #2563eb, #3b82f6);
color: #ffffff;
border-color: #2563eb;
}
}
.activity-list {
display: flex;
justify-content: center;
align-items: stretch;
flex-direction: column;
gap: 16px;
width: 100%;
max-width: 960px;
margin: 0 auto;
}
.activity-card {
position: relative;
border-radius: 16px;
padding: 24px;
cursor: pointer;
animation: fadeIn 0.4s ease-out both;
&:hover {
transform: translateY(-2px);
border-color: rgba(59, 130, 246, 0.3);
box-shadow: 0 8px 32px rgba(59, 130, 246, 0.12);
.activity-title {
color: #2563eb;
}
.arrow-icon {
color: #2563eb;
transform: translateX(4px);
}
}
}
.activity-row {
display: flex;
gap: 24px;
flex-direction: row;
align-items: flex-start;
}
.activity-date {
flex-shrink: 0;
display: flex;
align-items: center;
gap: 8px;
}
.date-block {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 64px;
height: 64px;
border-radius: 12px;
border: 1px solid;
background-clip: padding-box;
.date-day {
font-size: 20px;
font-weight: 700;
line-height: 1;
}
.date-month {
font-size: 12px;
color: #6b7280;
margin-top: 2px;
}
}
.date-block-blue {
background-image: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(6, 182, 212, 0.08));
border-color: #bfdbfe;
.date-day {
color: #2563eb;
}
}
.date-block-green {
background-image: linear-gradient(135deg, rgba(16, 185, 129, 0.1), rgba(20, 184, 166, 0.08));
border-color: #a7f3d0;
.date-day {
color: #059669;
}
}
.date-block-violet {
background-image: linear-gradient(135deg, rgba(139, 92, 246, 0.1), rgba(168, 85, 247, 0.08));
border-color: #ddd6fe;
.date-day {
color: #7c3aed;
}
}
.activity-main {
flex: 1;
min-width: 0;
}
.activity-meta {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 12px;
}
.tag {
display: inline-block;
padding: 2px 10px;
border-radius: 9999px;
font-size: 12px;
font-weight: 500;
line-height: 1.5;
}
.tag-blue {
background: rgba(59, 130, 246, 0.08);
color: #2563eb;
border: 1px solid rgba(59, 130, 246, 0.2);
}
.tag-green {
background: rgba(16, 185, 129, 0.08);
color: #059669;
border: 1px solid rgba(16, 185, 129, 0.2);
}
.tag-violet {
background: rgba(139, 92, 246, 0.08);
color: #7c3aed;
border: 1px solid rgba(139, 92, 246, 0.2);
}
.meta-source {
font-size: 14px;
color: #6b7280;
}
.activity-title {
margin: 0 0 12px;
font-size: 18px;
font-weight: 600;
line-height: 1.5;
color: #111827;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
transition: color 0.2s;
}
.activity-desc {
margin: 0;
font-size: 14px;
line-height: 1.625;
color: #4b5563;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.activity-arrow {
flex-shrink: 0;
display: none;
align-items: center;
}
@media (min-width: 1024px) {
.activity-arrow {
display: flex;
}
}
.arrow-icon {
width: 20px;
height: 20px;
color: #93c5fd;
transition: all 0.2s;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* glass-card 玻璃拟态背景 */
.glass-card {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(59, 130, 246, 0.15);
box-shadow: 0 4px 16px rgba(59, 130, 246, 0.04);
transition: all 0.3s;
}
@media (max-width: 768px) {
.news-title {
font-size: 28px;
}
.activity-card {
padding: 20px 16px;
}
.activity-meta {
gap: 8px;
}
.activity-title {
font-size: 16px;
}
}
/* ==================== Detail Modal ==================== */
.detail-overlay {
position: fixed;
inset: 0;
z-index: 999;
display: flex;
align-items: flex-start;
justify-content: center;
padding: 40px 24px;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
overflow-y: auto;
}
.detail-panel {
position: relative;
width: 100%;
max-width: 720px;
background: #ffffff;
border-radius: 16px;
border: 1px solid #dbeafe;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
overflow: hidden;
flex-shrink: 0;
}
.detail-sticky-header {
position: sticky;
top: 0;
z-index: 10;
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 24px;
background: linear-gradient(to bottom, #ffffff 80%, rgba(255,255,255,0.95));
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border-bottom: 1px solid #dbeafe;
.detail-header-left {
display: flex;
align-items: center;
gap: 12px;
}
.detail-tag {
display: inline-block;
padding: 3px 12px;
border-radius: 9999px;
font-size: 12px;
font-weight: 500;
line-height: 1.5;
&.tag-blue {
background: rgba(59, 130, 246, 0.1);
color: #2563eb;
border: 1px solid rgba(59, 130, 246, 0.2);
}
&.tag-green {
background: rgba(16, 185, 129, 0.1);
color: #059669;
border: 1px solid rgba(16, 185, 129, 0.2);
}
&.tag-violet {
background: rgba(139, 92, 246, 0.1);
color: #7c3aed;
border: 1px solid rgba(139, 92, 246, 0.2);
}
}
.detail-publisher {
font-size: 13px;
color: #6b7280;
}
}
.detail-close {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border: 1px solid #e5e7eb;
border-radius: 8px;
background: #f3f4f6;
cursor: pointer;
color: #6b7280;
transition: all 0.2s;
&:hover {
background: #e5e7eb;
color: #374151;
}
}
.detail-body {
padding: 24px 32px;
}
.detail-title {
margin: 0 0 16px;
font-size: 20px;
font-weight: 700;
line-height: 1.45;
color: #111827;
}
.detail-meta {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 16px;
margin-bottom: 24px;
padding-bottom: 24px;
border-bottom: 1px solid #f0f0f0;
.detail-meta-item {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 13px;
color: #6b7280;
}
.detail-meta-link {
color: #3b82f6;
text-decoration: none;
transition: color 0.2s;
&:hover {
color: #2563eb;
text-decoration: underline;
}
}
svg {
flex-shrink: 0;
opacity: 0.6;
}
}
.detail-content {
font-size: 14px;
line-height: 1.9;
color: #374151;
p {
margin: 0 0 12px;
}
strong {
color: #111827;
}
}
.detail-sticky-footer {
position: sticky;
bottom: 0;
z-index: 10;
display: flex;
align-items: center;
justify-content: flex-end;
padding: 16px 24px;
background: linear-gradient(to top, #ffffff 80%, rgba(255,255,255,0.95));
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border-top: 1px solid #dbeafe;
}
.btn-close {
height: 40px;
padding: 0 24px;
border: none;
border-radius: 12px;
font-size: 14px;
font-weight: 500;
color: #ffffff;
background: linear-gradient(135deg, #2563eb, #3b82f6);
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
cursor: pointer;
transition: opacity 0.2s;
&:hover {
opacity: 0.9;
}
}
.detail-fade-enter-active,
.detail-fade-leave-active {
transition: opacity 0.25s ease;
}
.detail-fade-enter-active .detail-panel,
.detail-fade-leave-active .detail-panel {
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.detail-fade-enter,
.detail-fade-leave-to {
opacity: 0;
}
.detail-fade-enter .detail-panel,
.detail-fade-leave-to .detail-panel {
transform: translateY(20px) scale(0.96);
opacity: 0;
}
@media (max-width: 640px) {
.detail-panel {
max-width: 100%;
border-radius: 16px;
}
.detail-cover {
padding: 24px 20px 20px;
}
.detail-body {
padding: 20px;
}
.detail-title {
font-size: 18px;
}
}

View File

@ -0,0 +1,520 @@
.opc-overview-page {
min-height: 100%;
position: relative;
overflow-x: hidden;
background: linear-gradient(135deg, #f0f4ff 0%, #ffffff 50%, #f8fafc 100%);
}
/* 背景装饰球 */
.bg-orb {
position: fixed;
border-radius: 50%;
pointer-events: none;
z-index: 0;
&.orb-blue {
width: 600px;
height: 600px;
top: -100px;
right: -100px;
background: radial-gradient(circle, rgba(59, 130, 246, 0.12) 0%, transparent 70%);
filter: blur(60px);
}
&.orb-cyan {
width: 500px;
height: 500px;
bottom: 100px;
left: -150px;
background: radial-gradient(circle, rgba(6, 182, 212, 0.1) 0%, transparent 70%);
filter: blur(60px);
}
}
/* Hero Section */
.hero-section {
position: relative;
padding-top: 40px;
padding-bottom: 64px;
overflow: hidden;
}
.hero-glow {
position: absolute;
border-radius: 50%;
pointer-events: none;
&.glow-blue {
width: 480px;
height: 480px;
top: 0;
right: 10%;
background: radial-gradient(circle, rgba(59, 130, 246, 0.15) 0%, transparent 70%);
filter: blur(40px);
}
&.glow-cyan {
width: 400px;
height: 400px;
bottom: 0;
left: 5%;
background: radial-gradient(circle, rgba(6, 182, 212, 0.12) 0%, transparent 70%);
filter: blur(40px);
}
}
.hero-content {
position: relative;
max-width: 1200px;
margin: 0 auto;
padding: 0 24px;
z-index: 1;
}
.hero-text-wrap {
text-align: center;
margin-bottom: 48px;
}
.hero-title {
margin: 0 0 16px;
font-size: 48px;
font-weight: 700;
background: linear-gradient(135deg, #3b82f6 0%, #06b6d4 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-subtitle {
margin: 0;
font-size: 20px;
color: #9ca3af;
}
.hero-images {
display: grid;
gap: 24px;
grid-template-columns: repeat(1, 1fr);
}
@media (min-width: 768px) {
.hero-images {
grid-template-columns: repeat(2, 1fr);
}
}
.image-card {
position: relative;
border-radius: 16px;
overflow: hidden;
border: 1px solid rgba(59, 130, 246, 0.15);
transition: all 0.3s;
&:hover {
border-color: rgba(59, 130, 246, 0.3);
box-shadow: 0 8px 32px rgba(59, 130, 246, 0.08);
.image-wrapper img {
transform: scale(1.05);
}
}
}
.image-wrapper {
width: 100%;
height: 320px;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s;
}
}
.image-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 16px;
background: linear-gradient(to top, rgba(0, 0, 0, 0.6), transparent);
h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: #fff;
}
}
/* 入驻流程 */
.process-section {
position: relative;
padding: 96px 24px;
overflow: hidden;
}
.section-bg {
position: absolute;
inset: 0;
background: linear-gradient(to bottom, transparent, rgba(59, 130, 246, 0.03), transparent);
}
.section-glow {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 800px;
height: 800px;
background: radial-gradient(circle, rgba(59, 130, 246, 0.08) 0%, transparent 70%);
border-radius: 50%;
filter: blur(60px);
pointer-events: none;
}
.section-content {
position: relative;
max-width: 1200px;
margin: 0 auto;
z-index: 1;
}
.process-header {
text-align: center;
margin-bottom: 48px;
}
.section-title {
margin: 0 0 16px;
font-size: 36px;
font-weight: 700;
background: linear-gradient(135deg, #3b82f6 0%, #06b6d4 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.section-desc {
margin: 0;
font-size: 18px;
color: #9ca3af;
text-align: center;
}
.process-connector {
display: none;
}
@media (min-width: 1024px) {
.process-connector {
display: block;
position: absolute;
top: 97px;
left: 25%;
right: 25%;
height: 2px;
// background: linear-gradient(to right, rgba(59, 130, 246, 0.3), rgba(6, 182, 212, 0.4), rgba(59, 130, 246, 0.3));
pointer-events: none;
}
}
.process-grid {
display: grid;
grid-template-columns: 1fr;
gap: 32px;
}
@media (min-width: 1024px) {
.process-grid {
grid-template-columns: repeat(2, 1fr);
gap: 48px;
}
}
.process-card {
position: relative;
border-radius: 24px;
overflow: hidden;
transition: all 0.5s;
animation: fadeInUp 0.6s ease-out both;
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: 24px;
padding: 1px;
background: linear-gradient(135deg, rgba(59, 130, 246, 0.3), rgba(6, 182, 212, 0.2), rgba(59, 130, 246, 0.3));
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
pointer-events: none;
transition: opacity 0.5s;
opacity: 0.5;
}
&:hover::before {
opacity: 1;
}
&:nth-child(2) {
animation-delay: 0.2s;
}
&:hover {
box-shadow: 0 8px 32px rgba(59, 130, 246, 0.12);
.step-badge-01 {
background: linear-gradient(135deg, #3b82f6, #06b6d4);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
}
.step-badge-02 {
background: linear-gradient(135deg, #06b6d4, #3b82f6);
box-shadow: 0 4px 12px rgba(6, 182, 212, 0.3);
}
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.step-badge {
position: absolute;
top: 16px;
right: 16px;
width: 48px;
height: 48px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
transition: all 0.3s;
span {
font-size: 16px;
font-weight: 700;
color: #1e293b;
}
&.step-badge-01 {
background: linear-gradient(135deg, #3b82f6, #06b6d4);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);
span {
color: #fff;
}
}
&.step-badge-02 {
background: linear-gradient(135deg, #06b6d4, #3b82f6);
box-shadow: 0 4px 12px rgba(6, 182, 212, 0.2);
span {
color: #fff;
}
}
}
.card-media {
padding: 24px 24px 0;
.media-inner {
border-radius: 16px;
overflow: hidden;
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(6, 182, 212, 0.05));
padding: 6px;
img {
width: 100%;
height: 208px;
object-fit: cover;
border-radius: 12px;
transition: transform 0.5s;
&:hover {
transform: scale(1.03);
}
}
}
}
.card-body {
padding: 20px 24px 28px;
}
.card-icon-row {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
h3 {
margin: 0;
font-size: 18px;
font-weight: 700;
color: #1e293b;
}
}
.card-icon {
width: 40px;
height: 40px;
border-radius: 10px;
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(6, 182, 212, 0.1));
border: 1px solid rgba(59, 130, 246, 0.2);
display: flex;
align-items: center;
justify-content: center;
color: #2563eb;
svg {
flex-shrink: 0;
}
}
.card-body > p {
margin: 0 0 16px;
font-size: 14px;
color: #9ca3af;
line-height: 1.6;
}
.card-link-row {
padding-top: 16px;
border-top: 1px solid #f1f5f9;
}
.card-link,
.card-link-btn {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 14px;
color: #3b82f6;
text-decoration: none;
transition: color 0.2s;
background: none;
border: none;
padding: 0;
cursor: pointer;
&:hover {
color: #06b6d4;
}
svg {
flex-shrink: 0;
}
}
/* CTA */
.cta-section {
position: relative;
padding: 64px 24px;
text-align: center;
}
.cta-bg {
position: absolute;
inset: 0;
background: linear-gradient(135deg, rgba(59, 130, 246, 0.05), rgba(139, 92, 246, 0.05));
border-radius: 0;
}
.cta-content {
position: relative;
max-width: 640px;
margin: 0 auto;
z-index: 1;
}
.cta-title {
margin: 0 0 20px;
font-size: 30px;
font-weight: 700;
background: linear-gradient(135deg, #3b82f6 0%, #06b6d4 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.cta-desc {
margin: 0 0 32px;
font-size: 16px;
color: #9ca3af;
}
.cta-btn {
display: inline-block;
padding: 16px 40px;
border: none;
border-radius: 12px;
font-size: 16px;
font-weight: 500;
color: #fff;
background: linear-gradient(135deg, #3b82f6, #06b6d4);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.25);
cursor: pointer;
transition: all 0.2s;
&:hover {
opacity: 0.9;
transform: translateY(-2px);
}
}
/* 响应式 */
@media (max-width: 768px) {
.nav-links {
display: none;
}
.hero-title {
font-size: 32px;
}
.hero-subtitle {
font-size: 16px;
}
.hero-images {
grid-template-columns: 1fr;
}
.image-wrapper {
height: 220px;
}
.section-title {
font-size: 28px;
}
.process-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 480px) {
.hero-title {
font-size: 28px;
}
.card-icon-row h3 {
font-size: 16px;
}
}

View File

@ -0,0 +1,161 @@
<template>
<div class="opc-overview-page">
<!-- 背景装饰 -->
<div class="bg-orb orb-blue"></div>
<div class="bg-orb orb-cyan"></div>
<!-- Hero Section -->
<section class="hero-section">
<div class="hero-glow glow-blue"></div>
<div class="hero-glow glow-cyan"></div>
<div class="hero-content">
<div class="hero-text-wrap">
<h1 class="hero-title">OPC社区概况</h1>
<p class="hero-subtitle">紫东数智OPC社区 · 赋能创业人才 · 助力项目落地</p>
</div>
<!-- 园区图片 -->
<div class="hero-images">
<div class="image-card">
<div class="image-wrapper">
<img src="./images/opc-community-sign.jpeg" alt="紫东数智OPC社区">
</div>
<div class="image-overlay">
<h3>紫东数智OPC社区</h3>
</div>
</div>
<div class="image-card">
<div class="image-wrapper">
<img src="./images/office-space.jpeg" alt="共享办公空间">
</div>
<div class="image-overlay">
<h3>共享空间</h3>
</div>
</div>
</div>
</div>
</section>
<!-- 入驻流程 -->
<section class="process-section">
<div class="section-bg"></div>
<div class="section-glow"></div>
<div class="section-content">
<div class="process-header">
<h2 class="section-title">入驻流程</h2>
<p class="section-desc">完成入驻申请开启创业之旅</p>
</div>
<!-- 连接线 -->
<div class="process-connector"></div>
<div class="process-grid">
<!-- 步骤1 -->
<div class="process-card">
<div class="step-badge step-badge-01">
<span>01</span>
</div>
<div class="card-media">
<div class="media-inner">
<img src="./images/step1-register.jpg" alt="注册登录">
</div>
</div>
<div class="card-body">
<div class="card-icon-row">
<div class="card-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/>
</svg>
</div>
<h3>注册登录</h3>
</div>
<p>前往北京市石景山区工商注册平台完成企业注册登录</p>
<div class="card-link-row">
<a href="https://scjgj.beijing.gov.cn/" target="_blank" class="card-link">
<span>访问工商注册平台</span>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
</svg>
</a>
</div>
</div>
</div>
<!-- 步骤2 -->
<div class="process-card">
<div class="step-badge step-badge-02">
<span>02</span>
</div>
<div class="card-media">
<div class="media-inner">
<img src="./images/step2-submit.jpg" alt="入驻申请">
</div>
</div>
<div class="card-body">
<div class="card-icon-row">
<div class="card-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
</div>
<h3>入驻申请</h3>
</div>
<p>填写紫东数智OPC社区入驻申请表并提交申请</p>
<div class="card-link-row">
<button type="button" class="card-link-btn" @click="openFormModal">
<span>填写入驻申请表</span>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 申请入驻 CTA -->
<section class="cta-section">
<div class="cta-bg"></div>
<div class="cta-content">
<h2 class="cta-title">申请入驻</h2>
<p class="cta-desc">符合入驻标准的创业团队欢迎申请入驻紫东数智OPC社区</p>
<button type="button" class="cta-btn" @click="openFormModal">
立即申请入驻
</button>
</div>
</section>
<!-- 入驻申请弹窗 -->
<OpcJoinDialog :visible.sync="showFormModal" @close="showFormModal = false" />
</div>
</template>
<script>
import OpcJoinDialog from './OpcJoinDialog.vue'
export default {
name: 'OpcOverview',
components: { OpcJoinDialog },
data() {
return {
showFormModal: false
}
},
methods: {
openFormModal() {
this.showFormModal = true
}
}
}
</script>
<style scoped lang="less">
@import url('./less/opcOverview.less');
</style>

View File

View File

@ -0,0 +1,209 @@
<template>
<header class="ncmatch-header">
<nav class="ncmatch-nav">
<!-- 左侧Logo -->
<a href="javascript:void(0)" class="logo-link" @click="go('/ncmatchHome/index')">
<img src="./images/logo.png" alt="OPC数智平台" class="logo-img" />
</a>
<!-- 中间导航 -->
<div class="nav-links">
<a href="javascript:void(0)" @click="go('/ncmatchHome/index')" class="nav-link"
:class="{ active: isActive('/ncmatchHome/index') }">首页
</a>
<a href="javascript:void(0)" @click="go('/ncmatchHome/societyNews')" class="nav-link"
:class="{ active: isActive('/ncmatchHome/societyNews') }">政社快讯
</a>
<a href="javascript:void(0)" @click="go('/ncmatchHome/tokenMarket')" class="nav-link"
:class="{ active: isActive('/ncmatchHome/tokenMarket') }">token市集
</a>
<a href="javascript:void(0)" @click="go('/ncmatchHome/tokenMarket')" class="nav-link"
:class="{ active: isActive('/ncmatchHome/tokenMarket') }">云枢基座
</a>
<a href="javascript:void(0)" @click="go('/ncmatchHome/supplyAndDemandSquare')" class="nav-link"
:class="{ active: isActive('/ncmatchHome/supplyAndDemandSquare') }">供需广场
</a>
<a href="javascript:void(0)" @click="go('/ncmatchHome/opcOverview')" class="nav-link"
:class="{ active: isActive('/ncmatchHome/opcOverview') }">OPC社区概况
</a>
<!-- 梦工厂 -->
<a href="javascript:void(0)" @click="go('/ncmatchHome/tokenMarket')" class="nav-link"
:class="{ active: isActive('/ncmatchHome/tokenMarket') }">梦工厂
</a>
</div>
<!-- 右侧功能按钮 -->
<div class="nav-actions">
<button class="btn-login" @click="onLogin">登录</button>
<div class="dropdown-wrap" id="platform-menu-wrapper">
</div>
</div>
</nav>
</header>
</template>
<script>
export default {
name: 'TopNav',
data() {
return {
logoSrc: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="120" height="32"><rect width="120" height="32" fill="%23e5e7eb"/><text x="50%" y="55%" font-size="14" text-anchor="middle" fill="%236b7280" font-family="sans-serif">LOGO</text></svg>',
dropdownOpen: false,
};
},
mounted() {
document.addEventListener('click', this.handleOutsideClick);
},
beforeDestroy() {
document.removeEventListener('click', this.handleOutsideClick);
},
methods: {
isActive(path) {
return this.$route && this.$route.path === path;
},
go(path) {
if (this.$route.path === path) return;
this.$router.push(path).catch(() => { });
},
onLogin() {
window.dispatchEvent(new Event('kboss-open-login-dialog'));
},
handleOutsideClick(e) {
const wrap = document.getElementById('platform-menu-wrapper');
if (wrap && !wrap.contains(e.target)) {
const drop = document.getElementById('platform-dropdown');
if (drop) drop.classList.add('hidden');
this.dropdownOpen = false;
}
},
},
};
</script>
<style scoped>
/* ==================== Header ==================== */
.ncmatch-header {
/* position: fixed; */
/* top: 0; */
/* left: 0; */
/* right: 0; */
/* z-index: 50; */
background: rgba(255, 255, 255, 0.92);
backdrop-filter: saturate(180%) blur(14px);
-webkit-backdrop-filter: saturate(180%) blur(14px);
border-bottom: 1px solid rgba(59, 130, 246, 0.1);
}
/* ==================== Nav Layout ==================== */
.ncmatch-nav {
max-width: 1400px;
margin: 0 auto;
padding: 0 24px;
height: 80px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
}
/* ==================== Logo ==================== */
.logo-link {
display: flex;
align-items: center;
flex-shrink: 0;
text-decoration: none;
cursor: pointer;
}
.logo-img {
height: 60px;
width: auto;
display: block;
}
/* ==================== Nav Links ==================== */
.nav-links {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
justify-content: center;
}
.nav-link {
display: inline-block;
padding: 6px 14px;
font-size: 16px;
font-weight: 500;
color: #374151;
text-decoration: none;
border-radius: 6px;
transition: color 0.2s, background-color 0.2s;
white-space: nowrap;
}
.nav-link:hover {
color: #2563eb;
background-color: rgba(37, 99, 235, 0.06);
}
.nav-link.active {
color: #2563eb;
font-weight: 600;
background-color: rgba(37, 99, 235, 0.08);
}
/* ==================== Right Actions ==================== */
.nav-actions {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.btn-login {
height: 48px;
width: 120px;
padding: 0;
border: 0;
border-radius: 99px;
color: #fff;
background-color: #000;
cursor: pointer;
font-size: 16px !important;
font-weight: 700;
}
.btn-login:hover {
opacity: 0.9;
}
.btn-menu {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
background-color: #ffffff;
border: 1px solid #bfdbfe;
border-radius: 8px;
cursor: pointer;
color: #4b5563;
transition: background-color 0.2s;
flex-shrink: 0;
}
.btn-menu:hover {
background-color: #eff6ff;
}
/* ==================== Dropdown ==================== */
/* ==================== Hidden Utility ==================== */
.hidden {
display: none !important;
}
</style>