2025-07-10 13:47:58 +08:00

1453 lines
33 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { ref, onMounted, computed, reactive } from 'vue'
import { useRoute } from 'vue-router'
import {
getPageDetail,
updateViewCount,
addFeedback,
listFeedback,
queryFeedbackDetail,
} from '../services/api'
import TheNavbar from '../components/TheNavbar.vue'
const route = useRoute()
const loading = ref(true)
const error = ref(false)
const page = ref<any>(null)
const feedbackForm = reactive({
issueDescription: '',
phoneNumber: '',
})
const feedbackSubmitSuccess = ref(false)
const feedbackNoReturned = ref('')
// 处理内容,将#标题#格式的文本转换为可折叠卡片
const processedContent = computed(() => {
if (!page.value || !page.value.content) return ''
// 正则表达式匹配 #标题# 格式
const regex = /#([^#]+)#([\s\S]*?)(?=#[^#]+#|$)/g
let index = 0
// 替换为可折叠卡片HTML不使用内联onclick
const result = page.value.content.replace(
regex,
(match: string, title: string, content: string) => {
// 去除前后空白
title = title.trim()
content = content.trim()
const currentIndex = index++
return `
<div class="law-card" data-card-index="${currentIndex}">
<div class="law-card-header" data-card-index="${currentIndex}">
<span class="law-card-title">${title}</span>
<span class="law-card-toggle">▼</span>
</div>
<div class="law-card-content">
${content}
</div>
</div>
`
},
)
return result
})
// 展开或折叠卡片的处理函数
const expandedCards = ref<Set<number>>(new Set())
const toggleCard = (index: number) => {
const card = document.querySelector(`.law-card[data-card-index="${index}"]`)
if (card) {
if (expandedCards.value.has(index)) {
expandedCards.value.delete(index)
card.classList.remove('expanded')
} else {
expandedCards.value.add(index)
card.classList.add('expanded')
}
}
}
// 计算附件列表
const attachmentList = computed(() => {
if (!page.value || !page.value.multiAttachments) return []
try {
const parsed = JSON.parse(page.value.multiAttachments)
// 过滤无效的附件项
return Array.isArray(parsed) ? parsed.filter((item) => item && (item.url || item.name)) : []
} catch (e) {
console.error('解析附件列表失败:', e)
return []
}
})
onMounted(async () => {
try {
const formatId = route.query.Id as string
if (!formatId) {
console.error('缺少必要的formatId参数')
error.value = true
loading.value = false
return
}
console.log('开始获取表单详情formatId:', formatId)
// 获取表单数据
const result = await getPageDetail('form', formatId)
console.log('表单详情请求结果:', result)
if (result && result.code === 200 && result.data) {
page.value = result.data
console.log('成功获取表单详情:', page.value)
// 更新浏览量
try {
const viewResult = (await updateViewCount('form', formatId)) as any
console.log('更新浏览量结果:', viewResult)
// 确认服务器端更新成功
if (viewResult && viewResult.code === 200) {
// 如果后端返回了新的浏览量数据
if (viewResult.data && typeof viewResult.data.viewCount === 'number') {
page.value.viewCount = viewResult.data.viewCount
}
// 否则本地递增浏览量
else if (
typeof page.value.viewCount === 'number' ||
typeof page.value.viewCount === 'string'
) {
page.value.viewCount = Number(page.value.viewCount || 0) + 1
} else {
page.value.viewCount = 1 // 初始浏览量
}
console.log('浏览量更新为:', page.value.viewCount)
} else {
console.warn('浏览量更新API返回错误:', viewResult?.msg || '未知错误')
}
} catch (e) {
console.error('更新浏览量失败:', e)
// 出错时也尝试本地增加浏览量
if (typeof page.value.viewCount === 'number' || typeof page.value.viewCount === 'string') {
page.value.viewCount = Number(page.value.viewCount || 0) + 1
console.log('API出错本地更新浏览量为:', page.value.viewCount)
}
}
// 检查返回的数据是否确实是表单类型
if (page.value.pageType && page.value.pageType !== 'form') {
console.error(`错误: 请求的是表单(form),但返回的是${page.value.pageType}类型的内容`)
// 不再尝试重新获取,而是直接显示错误
error.value = true
loading.value = false
return
}
// 处理可能的中文乱码问题
if (page.value.title && /\\u|%/.test(page.value.title)) {
try {
page.value.title = decodeURIComponent(page.value.title)
} catch (e) {
console.error('标题解码失败:', e)
}
}
// 检查内容是否为HTML格式
if (page.value.content) {
console.log('内容类型:', typeof page.value.content)
console.log('内容前50个字符:', page.value.content.substring(0, 50))
// 处理可能的中文乱码问题
if (/\\u|%/.test(page.value.content)) {
try {
page.value.content = decodeURIComponent(page.value.content)
} catch (e) {
console.error('内容解码失败:', e)
}
}
// 确保内容是HTML字符串
if (typeof page.value.content !== 'string') {
try {
page.value.content = JSON.stringify(page.value.content)
} catch (e) {
console.error('内容格式转换失败:', e)
}
}
} else {
console.warn('页面内容为空')
page.value.content = '<p>暂无内容</p>'
}
// 处理附件信息
if (page.value.attachmentUrl) {
console.log('附件URL:', page.value.attachmentUrl)
// 处理可能的附件URL乱码
if (/\\u|%/.test(page.value.attachmentUrl)) {
try {
page.value.attachmentUrl = decodeURIComponent(page.value.attachmentUrl)
} catch (e) {
console.error('附件URL解码失败:', e)
}
}
}
if (page.value.multiAttachments) {
try {
// 处理可能的多附件数据乱码
let attachmentsStr = page.value.multiAttachments
if (/\\u|%/.test(attachmentsStr)) {
try {
attachmentsStr = decodeURIComponent(attachmentsStr)
} catch (e) {
console.error('多附件数据解码失败:', e)
}
}
const attachments = JSON.parse(attachmentsStr)
console.log('多附件数据:', attachments)
// 处理附件数组中的每个附件URL
if (Array.isArray(attachments)) {
attachments.forEach((attachment, index) => {
if (attachment.url && /\\u|%/.test(attachment.url)) {
try {
attachment.url = decodeURIComponent(attachment.url)
} catch (e) {
console.error(`附件${index}URL解码失败:`, e)
}
}
})
// 更新页面的多附件数据
page.value.multiAttachments = JSON.stringify(attachments)
}
} catch (e) {
console.error('解析多附件数据失败:', e)
}
}
// 更新页面标题
document.title = `${page.value.title || '表单详情'} - 表单下载`
} else {
console.error('获取表单详情失败:', result?.msg || '未知错误')
error.value = true
}
} catch (e) {
console.error('获取表单详情异常:', e)
error.value = true
} finally {
loading.value = false
}
})
// 添加一个初始化折叠卡片功能的方法
const initCollapsibleCards = () => {
console.log('初始化折叠卡片...')
// 使用事件委托在父元素上监听点击事件
document.querySelector('.body')?.addEventListener('click', (e) => {
const target = e.target as HTMLElement
const header = target.closest('.law-card-header')
if (header) {
const index = parseInt(header.getAttribute('data-card-index') || '-1')
if (index >= 0) {
toggleCard(index)
console.log('切换卡片状态:', index)
}
}
})
console.log('折叠卡片初始化完成')
}
// 在内容更新后初始化折叠卡片
onMounted(() => {
// 内容加载完成后初始化折叠卡片
setTimeout(() => {
initCollapsibleCards()
}, 1000)
})
// 格式化日期
function formatDate(dateStr: string): string {
if (!dateStr) return '未知'
const date = new Date(dateStr)
return `${date.getFullYear()}-${padZero(date.getMonth() + 1)}-${padZero(date.getDate())}`
}
// 数字补零
function padZero(num: number): string {
return num < 10 ? `0${num}` : `${num}`
}
// 获取附件图标类
function getAttachmentIconClass(filename: string): string {
if (!filename) return 'icon-file'
const ext = filename.split('.').pop()?.toLowerCase() || ''
switch (ext) {
case 'pdf':
return 'icon-pdf'
case 'doc':
case 'docx':
return 'icon-word'
case 'xls':
case 'xlsx':
return 'icon-excel'
case 'ppt':
case 'pptx':
return 'icon-ppt'
case 'zip':
case 'rar':
return 'icon-archive'
case 'png':
case 'jpg':
case 'jpeg':
case 'gif':
return 'icon-image'
default:
return 'icon-file'
}
}
// 从URL中获取文件名
function getFileName(url: string): string {
if (!url) return '未知文件'
return url.split('/').pop() || '未知文件'
}
// 获取附件完整URL - 修复版本
function getAttachmentUrl(url: string): string {
if (!url) return '#'
// 如果已经是完整URL则直接返回
if (url.startsWith('http://') || url.startsWith('https://')) {
return url
}
// 处理可能的格式问题
let processedUrl = url
// 替换反斜杠为正斜杠
processedUrl = processedUrl.replace(/\\/g, '/')
// 确保不存在连续的双斜杠
while (processedUrl.includes('//')) {
processedUrl = processedUrl.replace('//', '/')
}
// 去除开头的斜杠,因为后续我们会添加
if (processedUrl.startsWith('/')) {
processedUrl = processedUrl.substring(1)
}
// 检查是否已经包含profile前缀
if (processedUrl.startsWith('profile/')) {
return `${import.meta.env.VITE_APP_BASE_API}/${processedUrl}`
}
// 检查是否为年月日格式的路径
const datePattern = /^\d{4}\/\d{2}\/\d{2}\//
if (datePattern.test(processedUrl)) {
// 如果不包含files/master前缀但符合日期格式则添加
if (!processedUrl.startsWith('files/master/')) {
processedUrl = `files/master/${processedUrl}`
}
}
// 返回完整的URL
return `${import.meta.env.VITE_APP_BASE_API}/profile/${processedUrl}`
}
// 添加一个检测当前环境是否为微信浏览器的函数
const isWeixinBrowser = () => {
const ua = navigator.userAgent.toLowerCase()
return /micromessenger/i.test(ua)
}
// 添加一个直接文件下载处理函数,处理点击下载事件
function handleDownload(url: string, filename: string) {
const fullUrl = getAttachmentUrl(url)
// 检测是否在微信浏览器中
if (isWeixinBrowser()) {
// 显示微信浏览器中的提示模态框
showWeixinTips(fullUrl, filename)
return
}
// 显示下载中状态
const downloadStatus = ref('')
downloadStatus.value = '下载中...'
// 使用fetch API下载文件
fetch(fullUrl)
.then((response) => {
if (!response.ok) {
throw new Error(`下载失败: ${response.status}`)
}
return response.blob()
})
.then((blob) => {
// 创建临时下载链接
const downloadUrl = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = downloadUrl
a.download = filename || getFileName(url)
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(downloadUrl)
document.body.removeChild(a)
downloadStatus.value = '下载成功'
})
.catch((error) => {
console.error('下载文件时出错:', error, fullUrl)
downloadStatus.value = '下载失败'
// 如果下载失败,尝试直接打开链接
window.open(fullUrl, '_blank')
})
}
// 微信浏览器中显示提示信息
const showWeixinModal = ref(false)
const currentDownloadUrl = ref('')
const currentFileName = ref('')
function showWeixinTips(url: string, filename: string) {
currentDownloadUrl.value = url
currentFileName.value = filename || getFileName(url)
showWeixinModal.value = true
}
// 复制下载链接到剪贴板
function copyDownloadLink() {
try {
navigator.clipboard.writeText(currentDownloadUrl.value)
alert('下载链接已复制到剪贴板,请在浏览器中粘贴访问')
} catch (err) {
// 如果不支持clipboard API使用传统方法
const input = document.createElement('input')
input.value = currentDownloadUrl.value
document.body.appendChild(input)
input.select()
document.execCommand('copy')
document.body.removeChild(input)
alert('下载链接已复制到剪贴板,请在浏览器中粘贴访问')
}
}
// 在浏览器中打开
function openInBrowser() {
// 使用通用方案,在微信中会提示跳转到浏览器
window.location.href = currentDownloadUrl.value
// 关闭弹窗
setTimeout(() => {
showWeixinModal.value = false
}, 100)
}
const submitFeedback = async () => {
try {
// 前端验证
if (!feedbackForm.issueDescription.trim()) {
alert('问题描述不能为空。')
return
}
if (!feedbackForm.phoneNumber.trim()) {
alert('手机号不能为空。')
return
}
const dataToSend = {
issueDescription: feedbackForm.issueDescription,
phoneNumber: feedbackForm.phoneNumber
}
const res = await addFeedback(dataToSend)
if (res && res.code === 200) {
feedbackSubmitSuccess.value = true
feedbackNoReturned.value = res.data?.feedbackNo || '未知'
// 清空表单
feedbackForm.issueDescription = ''
feedbackForm.phoneNumber = ''
console.log('留言提交成功,查询编号:', feedbackNoReturned.value)
} else {
alert('提交留言失败:' + (res.msg || '未知错误'))
}
} catch (e) {
console.error('提交留言异常:', e)
alert('提交留言异常,请稍后再试')
}
}
const queryFeedback = async () => {
// 实现查询留言回复的逻辑
}
// 删除有类型错误的后台管理相关的函数,因为它们在前台页面不会被使用
</script>
<template>
<div class="form-container">
<TheNavbar />
<div v-if="loading" class="loading">
<div class="spinner"></div>
<p>加载中...</p>
</div>
<div v-else-if="error" class="error">
<div class="container">
<h3>内容加载失败</h3>
<p>无法找到该表单内容或发生网络错误</p>
<router-link to="/" class="btn-home">返回首页</router-link>
</div>
</div>
<div v-else class="content">
<div class="container">
<div class="content-card">
<div class="header">
<div class="form-category">表单下载</div>
<h2>{{ page.title }}</h2>
<div class="meta">
<span><i class="icon-time"></i>{{ formatDate(page.createTime) }}</span>
<span><i class="icon-eye"></i>浏览次数: {{ page.viewCount }}</span>
<span v-if="page.author"><i class="icon-user"></i>作者: {{ page.author }}</span>
</div>
</div>
<div class="body" v-html="processedContent"></div>
<!-- 附件列表 -->
<div class="attachments" v-if="attachmentList.length > 0">
<h3 class="attachments-title">附件下载</h3>
<ul class="attachment-list">
<li v-for="(item, index) in attachmentList" :key="index" class="attachment-item">
<span
class="attachment-icon"
:class="getAttachmentIconClass(item.name || item.url)"
></span>
<span class="attachment-name">{{ item.name || getFileName(item.url) }}</span>
<a
@click.prevent="handleDownload(item.url, item.name || getFileName(item.url))"
class="download-btn"
href="javascript:void(0)"
>下载</a
>
</li>
</ul>
</div>
<div class="footer">
<router-link to="/hasfjform" class="btn-more">查看更多表单</router-link>
<router-link to="/" class="btn-home">返回首页</router-link>
</div>
<!-- 用户留言模块 -->
<div class="feedback-section">
<h3><i class="icon-chat"></i> 表单咨询与留言</h3>
<div v-if="!feedbackSubmitSuccess" class="feedback-form">
<p class="section-description">
如果您有以下问题可以给我们留言我们会在十个工作日内反馈
</p>
<p class="section-description">1.企业经营中遇到的法律问题</p>
<p class="section-description">2.企业遇到行政执法不规范问题</p>
<p class="section-description">3.对我们工作提出的意见和建议</p>
<div class="form-group">
<label for="issueDescription">问题描述: <span class="required">*</span></label>
<textarea
id="issueDescription"
v-model="feedbackForm.issueDescription"
rows="5"
placeholder="请详细描述您的问题字数在10-500字之间"
required
></textarea>
</div>
<div class="form-group">
<label for="phoneNumber">手机号: <span class="required">*</span></label>
<input
type="text"
id="phoneNumber"
v-model="feedbackForm.phoneNumber"
placeholder="请输入手机号"
required
/>
</div>
<button @click="submitFeedback" class="submit-btn">提交留言</button>
</div>
<div v-else class="feedback-success">
<h4>留言提交成功</h4>
<p>
您的留言查询编号是<span class="feedback-no">{{ feedbackNoReturned }}</span>
</p>
<p>请牢记此编号以便查询回复</p>
<button
@click="((feedbackSubmitSuccess = false), (feedbackNoReturned = ''))"
class="submit-btn"
>
继续留言
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 微信浏览器下载提示弹窗 -->
<div v-if="showWeixinModal" class="weixin-modal-overlay">
<div class="weixin-modal">
<div class="weixin-modal-header">
<h3>提示</h3>
<span class="close-btn" @click="showWeixinModal = false">×</span>
</div>
<div class="weixin-modal-content">
<p>由于微信限制无法直接下载文件</p>
<p>请选择以下方式下载</p>
<div class="filename-display">
<strong>文件名:</strong> {{ currentFileName }}
</div>
<div class="weixin-modal-actions">
<button @click="copyDownloadLink" class="copy-link-btn">复制下载链接</button>
<button @click="openInBrowser" class="open-browser-btn">在浏览器中打开</button>
</div>
<div class="weixin-modal-steps">
<p><strong>或按照以下步骤操作</strong></p>
<ol>
<li>点击右上角 <span class="icon"></span> <span class="icon"></span> 按钮</li>
<li>选择"在浏览器中打开"</li>
<li>在浏览器中重新下载文件</li>
</ol>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.form-container {
min-height: 100vh;
background-color: var(--color-background);
display: flex;
flex-direction: column;
width: 100%;
}
.loading {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 50vh;
font-size: 1rem;
color: var(--color-text-light);
}
.spinner {
width: 50px;
height: 50px;
border: 4px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
border-top-color: var(--color-primary);
animation: spin 1s ease-in-out infinite;
margin-bottom: 1rem;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.error {
text-align: center;
padding: 4rem 0;
}
.error h3 {
margin-bottom: 1rem;
color: var(--color-danger);
font-size: 1.5rem;
}
.error p {
margin-bottom: 2rem;
color: var(--color-text-light);
}
.content {
padding: 0;
flex: 1;
background-color: var(--color-background);
width: 100%;
}
.content-card {
background-color: white;
border-radius: 0;
box-shadow: none;
padding: 2.5rem;
overflow: hidden;
max-width: 100%;
margin: 0;
}
.content .header {
text-align: center;
margin-bottom: 2.5rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid var(--color-border-light);
position: relative;
}
.form-category {
display: inline-block;
background-color: var(--color-primary);
color: white;
padding: 0.3rem 1rem;
border-radius: 20px;
font-size: 0.85rem;
margin-bottom: 0.8rem;
}
.content .header h2 {
margin-bottom: 1.5rem;
color: var(--color-text);
font-size: 1.8rem;
font-weight: 600;
}
.content .meta {
color: var(--color-text-light);
font-size: 0.9rem;
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 1.5rem;
}
.content .meta span {
display: flex;
align-items: center;
}
.icon-time::before {
content: '🕒';
margin-right: 5px;
}
.icon-eye::before {
content: '👁️';
margin-right: 5px;
}
.icon-user::before {
content: '👤';
margin-right: 5px;
}
.content .body {
line-height: 1.8;
color: var(--color-text);
font-size: 1.1rem;
}
.content .body img {
max-width: 100%;
height: auto;
display: block;
margin: 1rem auto;
border-radius: 5px;
}
.content .body p {
margin-bottom: 1rem;
}
.content .body h1,
.content .body h2,
.content .body h3,
.content .body h4,
.content .body h5,
.content .body h6 {
margin: 1.5rem 0 1rem;
color: var(--color-text);
}
.content .body table {
width: 100%;
border-collapse: collapse;
margin: 1rem 0;
}
.content .body table th,
.content .body table td {
border: 1px solid #eee;
padding: 0.5rem;
text-align: left;
}
.content .body table th {
background-color: #f8f9fa;
}
/* 添加法律卡片相关样式 */
:deep(.law-card) {
margin: 1.5rem 0;
border: 1px solid #e0e0e0;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
background-color: #f9f9f9;
}
:deep(.law-card-header) {
padding: 1rem 1.5rem;
background-color: #f0f0f0;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
transition: background-color 0.3s;
user-select: none; /* 防止选中文本 */
}
:deep(.law-card-header:hover) {
background-color: #e0e0e0;
}
:deep(.law-card-title) {
font-weight: bold;
color: #333;
font-size: 1.1rem;
}
:deep(.law-card-toggle) {
transition: transform 0.3s;
}
:deep(.law-card-content) {
padding: 0;
max-height: 0;
overflow: hidden;
transition:
max-height 0.5s ease,
padding 0.5s ease;
}
:deep(.law-card.expanded .law-card-content) {
padding: 1.5rem;
max-height: 2000px;
}
:deep(.law-card.expanded .law-card-toggle) {
transform: rotate(180deg);
}
.attachments {
background-color: #f9f9f9;
border-radius: 8px;
margin-top: 2rem;
padding: 1.5rem;
}
.attachments-title {
margin-bottom: 1.2rem;
color: var(--color-text);
font-size: 1.2rem;
font-weight: 600;
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
padding-bottom: 0.8rem;
}
.attachment-list {
list-style: none;
margin: 0;
padding: 0;
}
.attachment-item {
display: flex;
align-items: center;
padding: 0.8rem 0;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
.attachment-item:last-child {
border-bottom: none;
}
.attachment-icon {
width: 24px;
height: 24px;
background-position: center;
background-repeat: no-repeat;
background-size: contain;
margin-right: 12px;
}
.attachment-name {
flex: 1;
color: var(--color-text);
font-size: 0.95rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.download-btn {
background-color: var(--color-primary);
color: white;
border: none;
border-radius: 4px;
padding: 0.4rem 0.8rem;
font-size: 0.85rem;
text-decoration: none;
transition:
background-color 0.3s,
transform 0.3s;
cursor: pointer;
}
.download-btn:hover {
background-color: var(--color-primary-dark);
transform: translateY(-2px);
}
.content .footer {
margin-top: 3rem;
padding-top: 1.5rem;
border-top: 1px dashed #eee;
display: flex;
justify-content: center;
gap: 1rem;
flex-wrap: wrap;
}
.btn-home,
.btn-more {
display: inline-block;
padding: 0.6rem 1.5rem;
background-color: var(--color-primary);
color: white;
text-decoration: none;
border-radius: 4px;
transition:
background-color 0.3s,
transform 0.3s;
font-size: 0.9rem;
}
.btn-home:hover,
.btn-more:hover {
background-color: #0069d9;
transform: translateY(-2px);
}
.btn-more {
background-color: var(--color-success);
}
.btn-more:hover {
background-color: #218838;
}
@media (max-width: 768px) {
.content {
padding: 0;
}
.content-card {
padding: 1.5rem;
}
.content .header h2 {
font-size: 1.5rem;
}
.content .body {
font-size: 0.95rem;
}
.attachments {
padding: 1.2rem;
}
:deep(.law-card-title) {
font-size: 1rem;
}
}
@media (max-width: 576px) {
.content {
padding: 0;
}
.content-card {
padding: 1rem;
border-radius: 0;
}
.content .header {
margin-bottom: 1.5rem;
padding-bottom: 1rem;
}
.content .header h2 {
font-size: 1.3rem;
margin-bottom: 0.8rem;
}
.content .meta {
font-size: 0.8rem;
gap: 1rem;
}
.content .body {
font-size: 0.9rem;
}
.attachments {
padding: 1rem;
}
.attachment-item {
padding: 0.6rem 0;
}
.content .footer {
margin-top: 2rem;
padding-top: 1rem;
}
.btn-home,
.btn-more {
padding: 0.5rem 1.2rem;
font-size: 0.85rem;
}
:deep(.law-card-header) {
padding: 0.8rem 1rem;
}
:deep(.law-card-title) {
font-size: 0.9rem;
}
:deep(.law-card.expanded .law-card-content) {
padding: 1rem;
}
}
@media (min-width: 1200px) {
.content-card {
border-radius: 0;
max-width: 100%;
}
.content .body {
padding: 0;
}
.content {
padding: 0;
}
}
.feedback-section {
background-color: #f9f9f9;
border-radius: 8px;
margin-top: 2rem;
padding: 2rem;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
}
.feedback-section h3 {
font-size: 1.5rem;
color: var(--color-primary);
margin-bottom: 1.5rem;
display: flex;
align-items: center;
}
.feedback-section .icon-chat::before {
content: '💬'; /* Chat bubble icon */
margin-right: 10px;
font-size: 1.8rem;
}
.feedback-section .icon-search::before {
content: '🔍'; /* Search icon */
margin-right: 10px;
font-size: 1.8rem;
}
.feedback-section .section-description {
color: var(--color-text-light);
margin-bottom: 1.5rem;
line-height: 1.6;
}
.feedback-form .form-group,
.feedback-query .form-group {
margin-bottom: 1rem;
}
.feedback-form label,
.feedback-query label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: var(--color-text);
}
.feedback-form input[type='text'],
.feedback-form textarea,
.feedback-form select,
.feedback-query input[type='text'],
.feedback-query select {
width: calc(100% - 20px);
padding: 0.8rem 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 1rem;
box-sizing: border-box;
}
.feedback-form textarea {
resize: vertical;
}
.feedback-form .required,
.feedback-query .required {
color: var(--color-danger);
margin-left: 5px;
}
.feedback-form .submit-btn,
.feedback-query .submit-btn {
display: inline-block;
padding: 0.8rem 2rem;
background-color: var(--color-primary);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.3s ease;
margin-top: 1rem;
}
.feedback-form .submit-btn:hover,
.feedback-query .submit-btn:hover {
background-color: #0069d9;
}
.feedback-query .submit-btn:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
.feedback-success {
text-align: center;
padding: 2rem;
background-color: #e6ffe6;
border: 1px solid #aaffaa;
border-radius: 8px;
color: #336633;
}
.feedback-success h4 {
color: #28a745;
margin-bottom: 1rem;
font-size: 1.3rem;
}
.feedback-success .feedback-no {
font-weight: bold;
color: var(--color-primary);
font-size: 1.5rem;
}
.feedback-success button {
margin-top: 1.5rem;
padding: 0.7rem 1.5rem;
background-color: var(--color-success);
border: none;
border-radius: 4px;
color: white;
cursor: pointer;
transition: background-color 0.3s;
}
.feedback-success button:hover {
background-color: #218838;
}
.query-title {
margin-top: 3rem;
border-top: 1px dashed #eee;
padding-top: 2rem;
}
.query-result {
background-color: #e9f7fe;
border: 1px solid #b3e0ff;
border-radius: 8px;
padding: 1.5rem;
margin-top: 2rem;
}
.query-result h4 {
color: #007bff;
margin-bottom: 1rem;
font-size: 1.3rem;
padding-bottom: 0.8rem;
border-bottom: 1px dashed #cceeff;
}
.query-result .result-item {
margin-bottom: 0.8rem;
color: var(--color-text);
font-size: 0.95rem;
}
.query-result .result-item strong {
color: #333;
}
.query-result .admin-reply-content {
background-color: #ffffff;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 1rem;
margin-top: 0.5rem;
line-height: 1.6;
color: #555;
}
.error-message {
color: var(--color-danger);
margin-top: 1rem;
font-size: 0.9rem;
}
.status-pending {
color: orange;
font-weight: bold;
}
.status-replied {
color: green;
font-weight: bold;
}
.status-closed {
color: gray;
font-weight: bold;
}
@media (max-width: 768px) {
.feedback-section {
padding: 1.5rem;
}
}
@media (max-width: 576px) {
.feedback-section {
padding: 1rem;
}
.feedback-section h3 {
font-size: 1.3rem;
}
.feedback-section .icon-chat::before,
.feedback-section .icon-search::before {
font-size: 1.5rem;
}
.feedback-form input[type='text'],
.feedback-form textarea,
.feedback-form select,
.feedback-query input[type='text'],
.feedback-query select {
padding: 0.6rem 8px;
font-size: 0.9rem;
}
.feedback-form .submit-btn,
.feedback-query .submit-btn {
padding: 0.6rem 1.5rem;
font-size: 0.9rem;
}
.feedback-success h4 {
font-size: 1.1rem;
}
.feedback-success .feedback-no {
font-size: 1.3rem;
}
.query-result h4 {
font-size: 1.1rem;
}
.query-result .result-item {
font-size: 0.85rem;
}
}
/* 微信下载弹窗样式 */
.weixin-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.6);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.weixin-modal {
background-color: white;
border-radius: 8px;
width: 90%;
max-width: 400px;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}
.weixin-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
border-bottom: 1px solid #eee;
}
.weixin-modal-header h3 {
margin: 0;
color: var(--color-primary);
font-size: 1.2rem;
}
.close-btn {
font-size: 1.5rem;
font-weight: bold;
cursor: pointer;
color: #888;
}
.close-btn:hover {
color: #333;
}
.weixin-modal-content {
padding: 20px;
}
.filename-display {
background-color: #f5f5f5;
padding: 10px;
border-radius: 4px;
margin: 15px 0;
word-break: break-all;
}
.weixin-modal-actions {
display: flex;
flex-direction: column;
gap: 10px;
margin: 15px 0;
}
.copy-link-btn, .open-browser-btn {
padding: 12px;
border: none;
border-radius: 4px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s;
}
.copy-link-btn {
background-color: var(--color-primary);
color: white;
}
.open-browser-btn {
background-color: #4CAF50;
color: white;
}
.copy-link-btn:hover {
background-color: #0056b3;
}
.open-browser-btn:hover {
background-color: #3d8b40;
}
.weixin-modal-steps {
margin-top: 20px;
border-top: 1px dashed #ddd;
padding-top: 15px;
}
.weixin-modal-steps ol {
padding-left: 20px;
margin: 10px 0;
}
.weixin-modal-steps li {
margin-bottom: 8px;
}
.icon {
background-color: #f5f5f5;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
/* 在小屏幕上调整弹窗样式 */
@media (max-width: 576px) {
.weixin-modal {
width: 95%;
}
.weixin-modal-header {
padding: 12px 15px;
}
.weixin-modal-content {
padding: 15px;
}
.weixin-modal-actions {
gap: 8px;
}
.copy-link-btn, .open-browser-btn {
padding: 10px;
}
}
</style>