Merge branch 'master' of https://gitea.xinyuxi.com/zhanggaoming/xh-medical-sign-web
This commit is contained in:
@@ -98,7 +98,10 @@ const canViewPolicy = computed(
|
||||
() => isPermissionMockEnabled || hasAnyPermission(policyReadPermissions),
|
||||
)
|
||||
const canEditPolicy = computed(
|
||||
() => isPermissionMockEnabled || hasAnyPermission(policyWritePermissions),
|
||||
() =>
|
||||
!loginStore.isReadOnly &&
|
||||
(isPermissionMockEnabled ||
|
||||
policyWritePermissions.some((code) => loginStore.hasWritePermission(code))),
|
||||
)
|
||||
const canViewTemplateExceptions = computed(() => canViewPolicy.value)
|
||||
const canEditTemplateExceptions = computed(() => canEditPolicy.value)
|
||||
|
||||
@@ -5,8 +5,10 @@ withDefaults(
|
||||
total: number
|
||||
/** 只在演示模式下展示「重置演示数据」:真实环境不该出现这个自测入口 */
|
||||
showReset?: boolean
|
||||
canCreate?: boolean
|
||||
canExport?: boolean
|
||||
}>(),
|
||||
{ showReset: false },
|
||||
{ showReset: false, canCreate: false, canExport: false },
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -40,11 +42,11 @@ function handleInput(event: Event) {
|
||||
aria-label="搜索模板名称、编号或科室"
|
||||
@input="handleInput"
|
||||
/>
|
||||
<button type="button" class="tool-button primary" @click="emit('add')">
|
||||
<button v-if="canCreate" type="button" class="tool-button primary" @click="emit('add')">
|
||||
<span aria-hidden="true">+</span>
|
||||
新增文档
|
||||
</button>
|
||||
<button type="button" class="tool-button secondary" @click="emit('export')">
|
||||
<button v-if="canExport" type="button" class="tool-button secondary" @click="emit('export')">
|
||||
导出模板包
|
||||
</button>
|
||||
<button
|
||||
|
||||
@@ -8,6 +8,9 @@ import { readStatusLabel, readStatusTone } from '../mock'
|
||||
|
||||
const props = defineProps<{
|
||||
template: DocumentTemplate
|
||||
canEdit: boolean
|
||||
canInitiate: boolean
|
||||
canRemove: boolean
|
||||
/** 演示模式下「删除」是真删;真实模式下语义是下架(模板无 DELETE 接口,走归档) */
|
||||
isMock: boolean
|
||||
}>()
|
||||
@@ -26,17 +29,19 @@ const statusLabel = computed(() => readStatusLabel(props.template))
|
||||
/** 配色按后端状态分组取(待审核与已驳回不能共用一个颜色) */
|
||||
const statusTone = computed(() => readStatusTone(props.template))
|
||||
|
||||
/**
|
||||
* 只有已发布(后端 APPROVED / PUBLISHED)的模板可以发起签署。
|
||||
*
|
||||
* 这里不再放行 review:review 同时承载后端的 PENDING_REVIEW(待审核)与
|
||||
* REJECTED(已驳回),两者都还没通过审核。允许它们发起签署,
|
||||
* 等于让未定稿的文书进入签署流程,签出来的文书也无法追溯是哪一版定稿。
|
||||
*/
|
||||
const canInitiate = computed(() => props.template.status === 'published')
|
||||
/** 只有已发布 / 待复审的模板可以发起签署;草稿与已归档不可用 */
|
||||
const canInitiateTemplate = computed(
|
||||
() =>
|
||||
props.canInitiate &&
|
||||
(props.template.status === 'published' || props.template.status === 'review'),
|
||||
)
|
||||
|
||||
const initiateTitle = computed(() =>
|
||||
canInitiate.value ? '用此模板发起签署' : '仅已发布(审核通过)的模板可发起签署',
|
||||
canInitiateTemplate.value
|
||||
? '用此模板发起签署'
|
||||
: props.canInitiate
|
||||
? '仅已发布或待复审的模板可发起签署'
|
||||
: '当前账号没有发起签署任务的权限',
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -111,8 +116,11 @@ const removeTitle = computed(() => (props.isMock ? '删除此文书' : '下架
|
||||
</span>
|
||||
<div class="template-actions">
|
||||
<button type="button" class="card-button" @click="emit('preview', template)">预览</button>
|
||||
<button type="button" class="card-button" @click="emit('edit', template)">编辑</button>
|
||||
<button v-if="canEdit" type="button" class="card-button" @click="emit('edit', template)">
|
||||
编辑
|
||||
</button>
|
||||
<button
|
||||
v-if="canInitiate"
|
||||
type="button"
|
||||
class="card-button"
|
||||
title="查看该文书的版本历史"
|
||||
@@ -124,6 +132,7 @@ const removeTitle = computed(() => (props.isMock ? '删除此文书' : '下架
|
||||
v-if="canInitiate"
|
||||
type="button"
|
||||
class="card-button primary"
|
||||
:disabled="!canInitiateTemplate"
|
||||
:title="initiateTitle"
|
||||
@click="emit('initiate', template)"
|
||||
>
|
||||
@@ -150,6 +159,7 @@ const removeTitle = computed(() => (props.isMock ? '删除此文书' : '下架
|
||||
{{ action.label }}
|
||||
</button>
|
||||
<button
|
||||
v-if="canRemove"
|
||||
type="button"
|
||||
class="card-button danger"
|
||||
:title="removeTitle"
|
||||
|
||||
@@ -25,6 +25,7 @@ import type {
|
||||
import type { SigningTaskRecord, SigningTemplate } from '@/api/workbench/types'
|
||||
import NewSigningTaskDialog from '@/components/signing/NewSigningTaskDialog.vue'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { useLoginStore } from '@/stores/login'
|
||||
import { demoCampuses, demoDepartments } from '@/utils/mock-document-library'
|
||||
import { mockFlags } from '@/utils/mock-flags'
|
||||
import { clearMockState } from '@/utils/mock-storage'
|
||||
@@ -65,8 +66,29 @@ import type {
|
||||
*/
|
||||
const router = useRouter()
|
||||
const appStore = useAppStore()
|
||||
const loginStore = useLoginStore()
|
||||
|
||||
const useMockLibrary = mockFlags.documents
|
||||
const canCreateTemplate = computed(
|
||||
() =>
|
||||
!loginStore.isReadOnly &&
|
||||
(useMockLibrary ||
|
||||
loginStore.hasWritePermission(['sign:template:create', 'sign:template:version:create'])),
|
||||
)
|
||||
const canEditTemplate = computed(
|
||||
() =>
|
||||
!loginStore.isReadOnly &&
|
||||
(useMockLibrary || loginStore.hasWritePermission('sign:template:version:create')),
|
||||
)
|
||||
const canArchiveTemplate = computed(
|
||||
() =>
|
||||
!loginStore.isReadOnly &&
|
||||
(useMockLibrary || loginStore.hasWritePermission('sign:template:archive')),
|
||||
)
|
||||
const canInitiateSigning = computed(
|
||||
() =>
|
||||
!loginStore.isReadOnly && (useMockLibrary || loginStore.hasWritePermission('sign:task:create')),
|
||||
)
|
||||
/** 真实模式下的加载状态:区分"正在加载"与"确实没有模板" */
|
||||
const libraryLoading = ref(false)
|
||||
const libraryError = ref('')
|
||||
@@ -361,6 +383,10 @@ async function handlePreview(template: DocumentTemplate) {
|
||||
}
|
||||
|
||||
function showAddMessage() {
|
||||
if (!canCreateTemplate.value) {
|
||||
ElMessage.warning('当前账号没有创建文书模板的权限')
|
||||
return
|
||||
}
|
||||
// Word 自维护方案:新增文书直接进入 Word 编辑器(粘贴/导入 .docx)。
|
||||
wordEditingTemplate.value = null
|
||||
wordEditorVisible.value = true
|
||||
@@ -382,6 +408,11 @@ function closeWordEditor() {
|
||||
* 保存后还会把模板正文冲掉。
|
||||
*/
|
||||
async function showEditMessage(template: DocumentTemplate) {
|
||||
if (!canEditTemplate.value) {
|
||||
ElMessage.warning('当前账号没有创建模板版本的权限')
|
||||
return
|
||||
}
|
||||
|
||||
if (useMockLibrary || template.contentHtml) {
|
||||
wordEditingTemplate.value = template
|
||||
wordEditorVisible.value = true
|
||||
@@ -489,6 +520,11 @@ function toVersionPayload(form: TemplateEditorForm) {
|
||||
}
|
||||
|
||||
async function saveTemplate(form: TemplateEditorForm) {
|
||||
if ((!form.id && !canCreateTemplate.value) || (form.id && !canEditTemplate.value)) {
|
||||
ElMessage.warning('当前账号没有保存此文书的权限')
|
||||
return
|
||||
}
|
||||
|
||||
if (useMockLibrary) {
|
||||
if (form.id) {
|
||||
updateLocalTemplate(form)
|
||||
@@ -684,6 +720,11 @@ function closeVersions() {
|
||||
* 语义上等于下架,已签署文书仍可追溯。
|
||||
*/
|
||||
async function removeTemplate(template: DocumentTemplate) {
|
||||
if (!canArchiveTemplate.value) {
|
||||
ElMessage.warning('当前账号没有下架文书的权限')
|
||||
return
|
||||
}
|
||||
|
||||
const confirmed = await ElMessageBox.confirm(
|
||||
useMockLibrary
|
||||
? `确定删除文书「${template.name}」?删除后不可恢复。`
|
||||
@@ -765,6 +806,11 @@ async function toSigningTemplate(template: DocumentTemplate): Promise<SigningTem
|
||||
}
|
||||
|
||||
async function openSigningDialog(template: DocumentTemplate) {
|
||||
if (!canInitiateSigning.value) {
|
||||
ElMessage.warning('当前账号没有发起签署任务的权限')
|
||||
return
|
||||
}
|
||||
|
||||
closePreview()
|
||||
|
||||
try {
|
||||
@@ -856,6 +902,8 @@ onMounted(() => {
|
||||
v-model="filters.keyword"
|
||||
:total="filteredTemplates.length"
|
||||
:show-reset="useMockLibrary"
|
||||
:can-create="canCreateTemplate"
|
||||
:can-export="useMockLibrary"
|
||||
@add="showAddMessage"
|
||||
@export="showExportMessage"
|
||||
@reset="resetMockData"
|
||||
@@ -880,6 +928,9 @@ onMounted(() => {
|
||||
v-for="template in filteredTemplates"
|
||||
:key="template.id"
|
||||
:template="template"
|
||||
:can-edit="canEditTemplate"
|
||||
:can-initiate="canInitiateSigning"
|
||||
:can-remove="canArchiveTemplate"
|
||||
:is-mock="useMockLibrary"
|
||||
@preview="handlePreview"
|
||||
@edit="showEditMessage"
|
||||
|
||||
@@ -23,6 +23,7 @@ defineProps<{
|
||||
methodOptions: ReportSelectOption<ReportMethodFilter>[]
|
||||
exportLoading: boolean
|
||||
exportStatus: string
|
||||
canExport: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -149,7 +150,13 @@ function handleKeywordChange(event: Event) {
|
||||
|
||||
<span class="filter-spacer" />
|
||||
<span v-if="exportStatus" class="export-status" role="status">{{ exportStatus }}</span>
|
||||
<button type="button" class="export-button" :disabled="exportLoading" @click="emit('export')">
|
||||
<button
|
||||
v-if="canExport"
|
||||
type="button"
|
||||
class="export-button"
|
||||
:disabled="exportLoading"
|
||||
@click="emit('export')"
|
||||
>
|
||||
{{ exportLoading ? '导出中…' : '导出' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -21,6 +21,7 @@ import type {
|
||||
ReportTaskResponseDto,
|
||||
ReportTasksQueryDto,
|
||||
} from '@/api/management/types'
|
||||
import { useLoginStore } from '@/stores/login'
|
||||
|
||||
import DepartmentDistributionChart from './components/DepartmentDistributionChart.vue'
|
||||
import ReportDetailTable from './components/ReportDetailTable.vue'
|
||||
@@ -77,6 +78,12 @@ const exportDownloadLoading = ref(false)
|
||||
const exportJob = ref<ReportExportJobResponseDto | null>(null)
|
||||
const exportErrorMessage = ref('')
|
||||
const router = useRouter()
|
||||
const loginStore = useLoginStore()
|
||||
const canExportReport = computed(
|
||||
() =>
|
||||
!loginStore.isReadOnly &&
|
||||
(isReportsMockEnabled || loginStore.hasWritePermission('report:signing:export')),
|
||||
)
|
||||
|
||||
let reportRequestId = 0
|
||||
let taskRequestId = 0
|
||||
@@ -850,6 +857,11 @@ function handleRetryTasks() {
|
||||
}
|
||||
|
||||
async function handleExport() {
|
||||
if (!canExportReport.value) {
|
||||
ElMessage.warning('当前账号没有导出签署报表的权限')
|
||||
return
|
||||
}
|
||||
|
||||
if (exportLoading.value) {
|
||||
return
|
||||
}
|
||||
@@ -1056,6 +1068,7 @@ onUnmounted(() => {
|
||||
:method-options="methodOptions"
|
||||
:export-loading="exportLoading"
|
||||
:export-status="exportStatusText"
|
||||
:can-export="canExportReport"
|
||||
@update:period="updatePeriod"
|
||||
@update:campus="updateCampus"
|
||||
@update:department="updateDepartment"
|
||||
|
||||
@@ -80,19 +80,29 @@ const rolePermissionDrawerVisible = ref(false)
|
||||
let userRequestId = 0
|
||||
|
||||
const canEditUsers = computed(
|
||||
() => isUserMockEnabled || loginStore.hasPermission('system:user:update'),
|
||||
() =>
|
||||
!loginStore.isReadOnly &&
|
||||
(isUserMockEnabled || loginStore.hasWritePermission('system:user:update')),
|
||||
)
|
||||
const canCreateUsers = computed(
|
||||
() => isUserMockEnabled || loginStore.hasPermission('system:user:create'),
|
||||
() =>
|
||||
!loginStore.isReadOnly &&
|
||||
(isUserMockEnabled || loginStore.hasWritePermission('system:user:create')),
|
||||
)
|
||||
const canDeactivateUsers = computed(
|
||||
() => isUserMockEnabled || loginStore.hasPermission('system:user:disable'),
|
||||
() =>
|
||||
!loginStore.isReadOnly &&
|
||||
(isUserMockEnabled || loginStore.hasWritePermission('system:user:disable')),
|
||||
)
|
||||
const canResetPasswords = computed(
|
||||
() => isUserMockEnabled || loginStore.hasPermission('system:user:password:reset'),
|
||||
() =>
|
||||
!loginStore.isReadOnly &&
|
||||
(isUserMockEnabled || loginStore.hasWritePermission('system:user:password:reset')),
|
||||
)
|
||||
const canImportUsers = computed(
|
||||
() => isUserMockEnabled || loginStore.hasPermission('system:user:import'),
|
||||
() =>
|
||||
!loginStore.isReadOnly &&
|
||||
(isUserMockEnabled || loginStore.hasWritePermission('system:user:import')),
|
||||
)
|
||||
const canViewRoleApiPermissions = computed(
|
||||
() =>
|
||||
@@ -100,7 +110,7 @@ const canViewRoleApiPermissions = computed(
|
||||
loginStore.hasPermission(['system:permission:query', 'system:role:permission:query']),
|
||||
)
|
||||
const canEditRoleApiPermissions = computed(
|
||||
() => !isUserMockEnabled && loginStore.hasPermission('system:role:permission:update'),
|
||||
() => !isUserMockEnabled && loginStore.hasWritePermission('system:role:permission:update'),
|
||||
)
|
||||
|
||||
const mockCampuses: CampusRecord[] = [
|
||||
|
||||
Reference in New Issue
Block a user