feat: 会议提交剩余时间+解冻 + 会务/劳务材料批量下载 + 多角色人员/账号模块完善

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
郭庆泰
2026-08-25 06:53:18 +08:00
co-authored by Claude
parent 0c882ae846
commit 591a43fe61
126 changed files with 3789 additions and 1334 deletions
+13 -11
View File
@@ -49,11 +49,9 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue', 'change'])
const areaMap = new Map() // value -> {label, parent}
const labelToValue = new Map()
;(function buildMap(list, parent) {
for (const item of list) {
areaMap.set(item.value, { label: item.label, parent })
labelToValue.set(item.label, item.value)
if (item.children) buildMap(item.children, item.value)
}
})(areaData, null)
@@ -64,17 +62,21 @@ function toLabels(values) {
return values.map(v => areaMap.get(v)?.label || '')
}
// 把 label 数组翻译成 value 数组 (用于字符串回填)
function toValues(labels) {
if (!Array.isArray(labels)) return []
return labels.map(l => labelToValue.get(l) || '')
}
// 字符串 "北京市/北京市/东城区" → value 数组
// 字符串 "北京市/市辖区/东城区" → value 数组
// 按层级逐级匹配 label (而非扁平 label→value), 避免 label 重名冲突 (如"市辖区"在北京/乌鲁木齐等 185+ 处重名)
function stringToValues(str) {
if (typeof str !== 'string' || !str) return []
const labels = str.split(props.joinSep)
return toValues(labels)
const labels = str.split(props.joinSep).filter(Boolean)
const result = []
let level = areaData
const cap = Math.min(labels.length, props.maxLevel)
for (let i = 0; i < cap; i++) {
const found = level.find(item => item.label === labels[i])
if (!found) break
result.push(found.value)
level = found.children || []
}
return result
}
// 按 maxLevel 截断级数 (2=省/市, 3=省/市/区)
+13 -2
View File
@@ -4,7 +4,7 @@
<!-- 图片 -->
<img v-if="kind === 'image'" :src="fileUrl" class="preview-img" :alt="title" />
<!-- PDFiframe 预览浏览器内置 viewer -->
<iframe v-else-if="kind === 'pdf'" :src="fileUrl" class="preview-iframe" />
<iframe v-else-if="kind === 'pdf'" :src="proxyUrl(fileUrl)" class="preview-iframe" />
<!-- 其它doc/xls/zip 给下载链接 -->
<div v-else class="preview-fallback">
<el-icon class="fallback-icon"><Document /></el-icon>
@@ -47,9 +47,20 @@ const kind = computed(() => {
return 'other'
})
// OSS bucket 设置了 Content-Disposition: attachment, iframe 直接访问会被强制下载 (空白+下载)
// PDF 走 /common/oss/proxy 后端代理重写为 inline (与 publicity/{id} / doctor/Meetings / manager/Plans 同技术)
// #toolbar=0 隐藏 Chrome PDF viewer 工具栏, #zoom=page-width 强制 PDF 撑满 iframe 宽度
function proxyUrl(url) {
if (!url) return url
if (url.includes('hwtossbamlorgcn.oss-cn-beijing.aliyuncs.com')) {
return import.meta.env.VITE_APP_BASE_API + '/common/oss/proxy?url=' + encodeURIComponent(url) + '#toolbar=0&zoom=page-width'
}
return url
}
function openNew() {
if (!fileUrl.value) return
window.open(fileUrl.value, '_blank', 'noopener')
window.open(proxyUrl(fileUrl.value), '_blank', 'noopener')
}
async function copyUrl() {
if (!fileUrl.value) return