0609走查优化
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled

This commit is contained in:
cjh
2026-06-10 15:32:34 +08:00
parent e6d953f2e0
commit 90b93b12c6
28 changed files with 2151 additions and 557 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { createTextVNode, defineComponent } from 'vue';
import { useDialog, useLoadingBar, useMessage, useNotification } from 'naive-ui';
import type { DialogOptions } from 'naive-ui';
import useContentLoading from '@/hooks/common/loading';
defineOptions({
@@ -9,12 +10,39 @@ defineOptions({
const contentLoading = useContentLoading();
function withCancelButtonStyle(options: DialogOptions): DialogOptions {
if (!options.negativeText) return options;
const negativeButtonProps = (options.negativeButtonProps || {}) as Record<string, any>;
const className = negativeButtonProps.class;
return {
...options,
negativeButtonProps: {
...negativeButtonProps,
type: 'default',
class: Array.isArray(className) ? [...className, 'app-cancel-button'] : [className, 'app-cancel-button'].filter(Boolean)
} as any
};
}
function createAppDialog(dialog: ReturnType<typeof useDialog>) {
const appDialog = Object.create(dialog);
const methods = ['create', 'success', 'warning', 'error', 'info'] as const;
methods.forEach(method => {
appDialog[method] = (options: DialogOptions) => dialog[method](withCancelButtonStyle(options));
});
return appDialog;
}
const ContextHolder = defineComponent({
name: 'ContextHolder',
setup() {
function register() {
window.$loadingBar = useLoadingBar();
window.$dialog = useDialog();
window.$dialog = createAppDialog(useDialog());
window.$message = useMessage();
window.$notification = useNotification();
window.$loading = contentLoading;

View File

@@ -23,7 +23,7 @@ const menuWrapperClass = computed(() => (showLogo.value ? 'flex-1-hidden' : 'h-f
</script>
<template>
<DarkModeContainer class="menu-bg pos-relative size-full flex-col-stretch shadow-sider" :inverted="darkMenu">
<DarkModeContainer class="menu-bg pos-relative size-full flex-col-stretch" :inverted="darkMenu">
<GlobalLogo
v-if="showLogo"
class="sider-brand"
@@ -97,15 +97,15 @@ const menuWrapperClass = computed(() => (showLogo.value ? 'flex-1-hidden' : 'h-f
width: 100%;
min-height: 102px;
margin-top: auto;
border: 1px solid #ded8fa;
border: 1px solid #ffe2bf;
border-radius: 11px;
background: #f0ebff;
background: linear-gradient(180deg, #fff8ef 0%, #fff2e3 100%);
padding: 14px 11px;
strong {
display: block;
margin-bottom: 9px;
color: #664cff;
color: #cc741f;
font-size: 13px;
line-height: 16px;
}

View File

@@ -56,10 +56,10 @@ body[data-route-tone='warm'] .n-dialog .n-button--primary-type:hover {
background: linear-gradient(180deg, #ffb755 0%, #ff8f1f 100%) !important;
}
.route-tone-warm .n-button[style*='--n-color']:not(.n-button--disabled):not(.n-button--error-type),
body[data-route-tone='warm'] .n-modal .n-button[style*='--n-color']:not(.n-button--disabled):not(.n-button--error-type),
body[data-route-tone='warm'] .n-drawer .n-button[style*='--n-color']:not(.n-button--disabled):not(.n-button--error-type),
body[data-route-tone='warm'] .n-popconfirm .n-button[style*='--n-color']:not(.n-button--disabled):not(.n-button--error-type) {
.route-tone-warm .n-button[style*='--n-color']:not(.n-button--disabled):not(.n-button--error-type):not(.app-cancel-button),
body[data-route-tone='warm'] .n-modal .n-button[style*='--n-color']:not(.n-button--disabled):not(.n-button--error-type):not(.app-cancel-button),
body[data-route-tone='warm'] .n-drawer .n-button[style*='--n-color']:not(.n-button--disabled):not(.n-button--error-type):not(.app-cancel-button),
body[data-route-tone='warm'] .n-popconfirm .n-button[style*='--n-color']:not(.n-button--disabled):not(.n-button--error-type):not(.app-cancel-button) {
--n-border: 1px solid #ff9f22 !important;
--n-border-hover: 1px solid #ffad42 !important;
--n-border-pressed: 1px solid #eb7208 !important;
@@ -107,10 +107,10 @@ body[data-route-tone='warm'] .n-popconfirm .n-button--text-type[style*='--n-colo
color: #d86f08 !important;
}
.route-tone-warm .n-button--ghost[style*='--n-color'],
body[data-route-tone='warm'] .n-modal .n-button--ghost[style*='--n-color'],
body[data-route-tone='warm'] .n-drawer .n-button--ghost[style*='--n-color'],
body[data-route-tone='warm'] .n-popconfirm .n-button--ghost[style*='--n-color'] {
.route-tone-warm .n-button--ghost[style*='--n-color']:not(.app-cancel-button),
body[data-route-tone='warm'] .n-modal .n-button--ghost[style*='--n-color']:not(.app-cancel-button),
body[data-route-tone='warm'] .n-drawer .n-button--ghost[style*='--n-color']:not(.app-cancel-button),
body[data-route-tone='warm'] .n-popconfirm .n-button--ghost[style*='--n-color']:not(.app-cancel-button) {
--n-border: 1px solid #ffbf72 !important;
--n-border-hover: 1px solid #ffad42 !important;
--n-border-pressed: 1px solid #ef8f20 !important;
@@ -126,18 +126,18 @@ body[data-route-tone='warm'] .n-popconfirm .n-button--ghost[style*='--n-color']
box-shadow: none;
}
.route-tone-warm .n-button--ghost[style*='--n-color'] .n-button__content,
body[data-route-tone='warm'] .n-modal .n-button--ghost[style*='--n-color'] .n-button__content,
body[data-route-tone='warm'] .n-drawer .n-button--ghost[style*='--n-color'] .n-button__content,
body[data-route-tone='warm'] .n-popconfirm .n-button--ghost[style*='--n-color'] .n-button__content {
.route-tone-warm .n-button--ghost[style*='--n-color']:not(.app-cancel-button) .n-button__content,
body[data-route-tone='warm'] .n-modal .n-button--ghost[style*='--n-color']:not(.app-cancel-button) .n-button__content,
body[data-route-tone='warm'] .n-drawer .n-button--ghost[style*='--n-color']:not(.app-cancel-button) .n-button__content,
body[data-route-tone='warm'] .n-popconfirm .n-button--ghost[style*='--n-color']:not(.app-cancel-button) .n-button__content {
color: #c86f1a !important;
}
.route-tone-warm .n-button--default-type,
body[data-route-tone='warm'] .n-modal .n-button--default-type,
body[data-route-tone='warm'] .n-drawer .n-button--default-type,
body[data-route-tone='warm'] .n-popconfirm .n-button--default-type,
body[data-route-tone='warm'] .n-dialog .n-button--default-type {
.route-tone-warm .n-button--default-type:not(.app-cancel-button),
body[data-route-tone='warm'] .n-modal .n-button--default-type:not(.app-cancel-button),
body[data-route-tone='warm'] .n-drawer .n-button--default-type:not(.app-cancel-button),
body[data-route-tone='warm'] .n-popconfirm .n-button--default-type:not(.app-cancel-button),
body[data-route-tone='warm'] .n-dialog .n-button--default-type:not(.app-cancel-button) {
--n-border-hover: 1px solid #ffbf72 !important;
--n-border-focus: 1px solid #ffbf72 !important;
--n-border-pressed: 1px solid #f2a24e !important;
@@ -147,6 +147,122 @@ body[data-route-tone='warm'] .n-dialog .n-button--default-type {
--n-text-color-pressed: #b75f0e !important;
}
body[data-route-tone='warm'] .n-dialog .n-dialog__action .n-button:first-child {
--n-border: 1px solid #dcdfe6 !important;
--n-border-hover: 1px solid #cfd4dc !important;
--n-border-focus: 1px solid #cfd4dc !important;
--n-border-pressed: 1px solid #c4c9d2 !important;
--n-color: #fff !important;
--n-color-hover: #f8fafc !important;
--n-color-focus: #fff !important;
--n-color-pressed: #f3f4f6 !important;
--n-text-color: #374151 !important;
--n-text-color-hover: #1f2937 !important;
--n-text-color-focus: #374151 !important;
--n-text-color-pressed: #111827 !important;
border-color: #dcdfe6 !important;
background: #fff !important;
color: #374151 !important;
box-shadow: none !important;
}
body[data-route-tone='warm'] .n-dialog .n-dialog__action .n-button:first-child .n-button__content {
color: #374151 !important;
}
.n-dialog .n-dialog__action .n-button:first-child:not(:last-child),
.n-modal .n-dialog__action .n-button:first-child:not(:last-child),
.n-modal .n-card__footer .n-button:first-child:not(:last-child),
.n-drawer .n-drawer-footer .n-button:first-child:not(:last-child),
.n-popconfirm .n-popconfirm__action .n-button:first-child:not(:last-child) {
--n-border: 1px solid #dcdfe6 !important;
--n-border-hover: 1px solid #cfd4dc !important;
--n-border-focus: 1px solid #cfd4dc !important;
--n-border-pressed: 1px solid #c4c9d2 !important;
--n-color: #fff !important;
--n-color-hover: #f8fafc !important;
--n-color-focus: #fff !important;
--n-color-pressed: #f3f4f6 !important;
--n-text-color: #374151 !important;
--n-text-color-hover: #1f2937 !important;
--n-text-color-focus: #374151 !important;
--n-text-color-pressed: #111827 !important;
border-color: #dcdfe6 !important;
background: #fff !important;
color: #374151 !important;
box-shadow: none !important;
}
.n-dialog .n-dialog__action .n-button:first-child:not(:last-child):hover,
.n-dialog .n-dialog__action .n-button:first-child:not(:last-child):focus,
.n-dialog .n-dialog__action .n-button:first-child:not(:last-child):active,
.n-modal .n-dialog__action .n-button:first-child:not(:last-child):hover,
.n-modal .n-dialog__action .n-button:first-child:not(:last-child):focus,
.n-modal .n-dialog__action .n-button:first-child:not(:last-child):active,
.n-modal .n-card__footer .n-button:first-child:not(:last-child):hover,
.n-modal .n-card__footer .n-button:first-child:not(:last-child):focus,
.n-modal .n-card__footer .n-button:first-child:not(:last-child):active,
.n-drawer .n-drawer-footer .n-button:first-child:not(:last-child):hover,
.n-drawer .n-drawer-footer .n-button:first-child:not(:last-child):focus,
.n-drawer .n-drawer-footer .n-button:first-child:not(:last-child):active,
.n-popconfirm .n-popconfirm__action .n-button:first-child:not(:last-child):hover,
.n-popconfirm .n-popconfirm__action .n-button:first-child:not(:last-child):focus,
.n-popconfirm .n-popconfirm__action .n-button:first-child:not(:last-child):active {
border-color: #cfd4dc !important;
background: #f8fafc !important;
color: #1f2937 !important;
box-shadow: none !important;
}
.n-dialog .n-dialog__action .n-button:first-child:not(:last-child) .n-button__content,
.n-modal .n-dialog__action .n-button:first-child:not(:last-child) .n-button__content,
.n-modal .n-card__footer .n-button:first-child:not(:last-child) .n-button__content,
.n-drawer .n-drawer-footer .n-button:first-child:not(:last-child) .n-button__content,
.n-popconfirm .n-popconfirm__action .n-button:first-child:not(:last-child) .n-button__content {
color: #374151 !important;
}
.app-cancel-button,
button[class*='cancel' i],
.n-button[class*='cancel' i] {
--n-border: 1px solid #dcdfe6 !important;
--n-border-hover: 1px solid #cfd4dc !important;
--n-border-focus: 1px solid #cfd4dc !important;
--n-border-pressed: 1px solid #c4c9d2 !important;
--n-color: #fff !important;
--n-color-hover: #f8fafc !important;
--n-color-focus: #fff !important;
--n-color-pressed: #f3f4f6 !important;
--n-text-color: #374151 !important;
--n-text-color-hover: #1f2937 !important;
--n-text-color-focus: #374151 !important;
--n-text-color-pressed: #111827 !important;
border: 1px solid #dcdfe6 !important;
background: #fff !important;
color: #374151 !important;
box-shadow: none !important;
}
.app-cancel-button:hover,
.app-cancel-button:focus,
.app-cancel-button:active,
button[class*='cancel' i]:hover,
button[class*='cancel' i]:focus,
button[class*='cancel' i]:active,
.n-button[class*='cancel' i]:hover,
.n-button[class*='cancel' i]:focus,
.n-button[class*='cancel' i]:active {
border-color: #cfd4dc !important;
background: #f8fafc !important;
color: #1f2937 !important;
box-shadow: none !important;
}
.app-cancel-button .n-button__content,
.n-button[class*='cancel' i] .n-button__content {
color: #374151 !important;
}
.logout-confirm-dialog.n-dialog {
width: min(446px, calc(100vw - 48px));
border-radius: 6px;
@@ -168,15 +284,16 @@ body[data-route-tone='warm'] .n-dialog .n-button--default-type {
font-size: 14px;
}
.logout-confirm-dialog .n-dialog__action .n-button:first-child {
.logout-confirm-dialog .n-dialog__action .app-cancel-button,
.logout-confirm-dialog .n-dialog__action .n-button:first-child:not(:last-child) {
min-width: 58px;
border-color: #dce3ef !important;
border-radius: 7px;
background: #f8fafc !important;
color: #405066 !important;
background: #fff !important;
color: #374151 !important;
}
.logout-confirm-dialog .n-dialog__action .n-button:last-child {
.logout-confirm-dialog .n-dialog__action .n-button:not(.app-cancel-button) {
min-width: 64px;
border-color: #ff9f22 !important;
border-radius: 7px;
@@ -184,3 +301,101 @@ body[data-route-tone='warm'] .n-dialog .n-button--default-type {
color: #fff !important;
box-shadow: 0 10px 20px rgba(255, 138, 31, 0.22);
}
.app-cancel-button.n-button,
body[data-route-tone='warm'] .app-cancel-button.n-button,
body[data-route-tone='warm'] .n-dialog .n-dialog__action .app-cancel-button.n-button,
body[data-route-tone='warm'] .n-dialog .n-dialog__action .n-button:first-child:not(:last-child),
.n-dialog .n-dialog__action .n-button:first-child:not(:last-child),
.n-modal .n-dialog__action .n-button:first-child:not(:last-child),
.n-modal .n-card__footer .n-button:first-child:not(:last-child),
.n-drawer .n-drawer-footer .n-button:first-child:not(:last-child),
.n-popconfirm .n-popconfirm__action .n-button:first-child:not(:last-child),
.logout-confirm-dialog .n-dialog__action .app-cancel-button,
.logout-confirm-dialog .n-dialog__action .n-button:first-child:not(:last-child),
.n-button[class*='cancel' i],
button[class*='cancel' i] {
--n-height: 28px !important;
--n-border-radius: 6px !important;
--n-border: 1px solid #dcdfe6 !important;
--n-border-hover: 1px solid #cfd4dc !important;
--n-border-focus: 1px solid #cfd4dc !important;
--n-border-pressed: 1px solid #c4c9d2 !important;
--n-color: #fff !important;
--n-color-hover: #f8fafc !important;
--n-color-focus: #fff !important;
--n-color-pressed: #f3f4f6 !important;
--n-text-color: #374151 !important;
--n-text-color-hover: #1f2937 !important;
--n-text-color-focus: #374151 !important;
--n-text-color-pressed: #111827 !important;
min-width: 58px;
height: 28px;
border: 1px solid #dcdfe6 !important;
border-radius: 6px !important;
background: #fff !important;
color: #374151 !important;
padding: 0 14px;
box-shadow: none !important;
font-weight: 500;
}
.app-cancel-button.n-button:hover,
.app-cancel-button.n-button:focus,
.app-cancel-button.n-button:active,
body[data-route-tone='warm'] .app-cancel-button.n-button:hover,
body[data-route-tone='warm'] .app-cancel-button.n-button:focus,
body[data-route-tone='warm'] .app-cancel-button.n-button:active,
body[data-route-tone='warm'] .n-dialog .n-dialog__action .app-cancel-button.n-button:hover,
body[data-route-tone='warm'] .n-dialog .n-dialog__action .app-cancel-button.n-button:focus,
body[data-route-tone='warm'] .n-dialog .n-dialog__action .app-cancel-button.n-button:active,
body[data-route-tone='warm'] .n-dialog .n-dialog__action .n-button:first-child:not(:last-child):hover,
body[data-route-tone='warm'] .n-dialog .n-dialog__action .n-button:first-child:not(:last-child):focus,
body[data-route-tone='warm'] .n-dialog .n-dialog__action .n-button:first-child:not(:last-child):active,
.n-dialog .n-dialog__action .n-button:first-child:not(:last-child):hover,
.n-dialog .n-dialog__action .n-button:first-child:not(:last-child):focus,
.n-dialog .n-dialog__action .n-button:first-child:not(:last-child):active,
.n-modal .n-dialog__action .n-button:first-child:not(:last-child):hover,
.n-modal .n-dialog__action .n-button:first-child:not(:last-child):focus,
.n-modal .n-dialog__action .n-button:first-child:not(:last-child):active,
.n-modal .n-card__footer .n-button:first-child:not(:last-child):hover,
.n-modal .n-card__footer .n-button:first-child:not(:last-child):focus,
.n-modal .n-card__footer .n-button:first-child:not(:last-child):active,
.n-drawer .n-drawer-footer .n-button:first-child:not(:last-child):hover,
.n-drawer .n-drawer-footer .n-button:first-child:not(:last-child):focus,
.n-drawer .n-drawer-footer .n-button:first-child:not(:last-child):active,
.n-popconfirm .n-popconfirm__action .n-button:first-child:not(:last-child):hover,
.n-popconfirm .n-popconfirm__action .n-button:first-child:not(:last-child):focus,
.n-popconfirm .n-popconfirm__action .n-button:first-child:not(:last-child):active,
.logout-confirm-dialog .n-dialog__action .app-cancel-button:hover,
.logout-confirm-dialog .n-dialog__action .app-cancel-button:focus,
.logout-confirm-dialog .n-dialog__action .app-cancel-button:active,
.logout-confirm-dialog .n-dialog__action .n-button:first-child:not(:last-child):hover,
.logout-confirm-dialog .n-dialog__action .n-button:first-child:not(:last-child):focus,
.logout-confirm-dialog .n-dialog__action .n-button:first-child:not(:last-child):active,
.n-button[class*='cancel' i]:hover,
.n-button[class*='cancel' i]:focus,
.n-button[class*='cancel' i]:active,
button[class*='cancel' i]:hover,
button[class*='cancel' i]:focus,
button[class*='cancel' i]:active {
border-color: #cfd4dc !important;
background: #f8fafc !important;
color: #1f2937 !important;
box-shadow: none !important;
}
.app-cancel-button.n-button .n-button__content,
body[data-route-tone='warm'] .app-cancel-button.n-button .n-button__content,
body[data-route-tone='warm'] .n-dialog .n-dialog__action .app-cancel-button.n-button .n-button__content,
body[data-route-tone='warm'] .n-dialog .n-dialog__action .n-button:first-child:not(:last-child) .n-button__content,
.n-dialog .n-dialog__action .n-button:first-child:not(:last-child) .n-button__content,
.n-modal .n-dialog__action .n-button:first-child:not(:last-child) .n-button__content,
.n-modal .n-card__footer .n-button:first-child:not(:last-child) .n-button__content,
.n-drawer .n-drawer-footer .n-button:first-child:not(:last-child) .n-button__content,
.n-popconfirm .n-popconfirm__action .n-button:first-child:not(:last-child) .n-button__content,
.logout-confirm-dialog .n-dialog__action .app-cancel-button .n-button__content,
.logout-confirm-dialog .n-dialog__action .n-button:first-child:not(:last-child) .n-button__content,
.n-button[class*='cancel' i] .n-button__content {
color: inherit !important;
}

View File

@@ -308,7 +308,7 @@ onDeactivated(() => {
position: relative;
z-index: 1;
min-height: calc(100vh - 70px);
padding: 72px 28px 40px 20px;
padding: 37px 28px 40px 20px;
}
.history-button {
@@ -321,7 +321,7 @@ onDeactivated(() => {
border: 1px solid #d6dce8;
border-radius: 7px;
background: #fff;
box-shadow: 0 8px 20px rgba(31, 41, 55, 0.06);
box-shadow: none;
color: #20242d;
display: inline-flex;
align-items: center;
@@ -358,37 +358,48 @@ onDeactivated(() => {
.hero-panel {
min-height: 560px;
text-align: center;
&::before {
content: '';
display: block;
width: min(760px, calc(100vw - 330px));
height: 50px;
margin: 58px auto 24px;
}
}
.hero-copy {
margin-bottom: 94px;
margin-top: 56px;
margin-bottom: 72px;
transform: none;
h1 {
margin: 0;
color: #20222a;
font-size: 33px;
line-height: 44px;
color: #1f2937;
font-size: 40px;
line-height: 1.2;
font-weight: 800;
letter-spacing: 0;
}
p {
margin: 14px 0 0;
color: #7d8390;
margin: 12px 0 0;
color: #6a7487;
font-size: 16px;
line-height: 22px;
line-height: 1.5;
}
}
.upload-shell {
width: min(744px, calc(100vw - 330px));
min-height: 211px;
width: min(760px, calc(100vw - 330px));
min-height: 268px;
margin: 0 auto;
padding: 13px;
border: 1px solid #1f2026;
border-radius: 20px;
background: rgba(255, 255, 255, 0.56);
box-shadow: 0 34px 70px rgba(23, 28, 42, 0.08);
transform: none;
padding: 14px;
border: 1px solid #dfe6f0;
border-radius: 14px;
background: #fff;
box-shadow: 0 10px 24px rgba(17, 24, 39, 0.06);
cursor: pointer;
transition:
border-color 0.2s ease,
@@ -396,9 +407,9 @@ onDeactivated(() => {
transform 0.2s ease;
&:hover {
border-color: #ff8a1f;
box-shadow: 0 34px 76px rgba(255, 138, 31, 0.16);
transform: translateY(-2px);
border-color: #dfe6f0;
box-shadow: 0 10px 24px rgba(17, 24, 39, 0.06);
transform: none;
}
}
@@ -407,26 +418,26 @@ onDeactivated(() => {
}
.upload-shell :deep(.n-upload-dragger) {
min-height: 176px;
border: 0;
min-height: 220px;
border: 1px dashed #d7deea;
border-radius: 12px;
background: #f0f1f5;
background: linear-gradient(180deg, #fbfcff 0%, #f7f9fc 100%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding: 24px 20px 20px;
justify-content: center;
padding: 20px;
&:hover {
border: 0;
background: #f0f1f5;
border-color: #d7deea;
background: linear-gradient(180deg, #fbfcff 0%, #f7f9fc 100%);
}
}
.file-strip {
height: 36px;
height: 34px;
margin-top: 10px;
border: 1px solid #d8dbe4;
border: 1px solid #dde4ef;
border-radius: 8px;
background: #fff;
padding: 0 10px;
@@ -451,6 +462,11 @@ onDeactivated(() => {
color: #c34532;
font-size: 12px;
cursor: pointer;
opacity: 0;
transform: translateX(4px);
transition:
opacity 0.16s ease,
transform 0.16s ease;
&:disabled {
color: #b7bdc8;
@@ -458,15 +474,21 @@ onDeactivated(() => {
}
}
.file-strip:hover button,
.upload-shell:hover .file-strip button {
opacity: 1;
transform: translateX(0);
}
.upload-shell.is-uploaded :deep(.n-upload-dragger) {
min-height: 176px;
min-height: 220px;
}
.word-icon {
width: 28px;
height: 28px;
margin-bottom: 18px;
border-radius: 8px;
width: 30px;
height: 30px;
margin-bottom: 10px;
border-radius: 9px;
display: grid;
place-items: center;
background: linear-gradient(145deg, #ffa638 0%, #f58c15 100%);
@@ -478,15 +500,15 @@ onDeactivated(() => {
.upload-shell :deep(.n-upload-dragger) strong {
margin-bottom: 8px;
color: #171923;
font-size: 16px;
font-size: 18px;
line-height: 20px;
font-weight: 800;
font-weight: 700;
}
.upload-shell :deep(.n-upload-dragger) span:last-child {
color: #858b98;
font-size: 13px;
line-height: 18px;
line-height: 1.45;
em {
color: #ff7417;
@@ -496,10 +518,12 @@ onDeactivated(() => {
}
.parse-button {
width: 116px;
height: 36px;
margin-top: 40px;
border-radius: 7px;
min-width: 116px;
height: 32px;
margin-top: 26px;
transform: none;
border-radius: 8px;
font-size: 13px;
font-weight: 700;
}
@@ -512,7 +536,7 @@ onDeactivated(() => {
--n-text-color-hover: #fff !important;
--n-text-color-pressed: #fff !important;
background: linear-gradient(180deg, #ff9f22 0%, #ff8614 100%);
box-shadow: 0 10px 22px rgba(255, 138, 31, 0.28);
box-shadow: 0 8px 16px rgba(255, 138, 31, 0.22);
color: #fff;
}
@@ -586,21 +610,21 @@ onDeactivated(() => {
:global(.ai-read-history-button) {
position: fixed;
left: calc(var(--soy-sider-width, 258px) + 20px);
top: calc((var(--soy-header-height, 70px) - 38px) / 2);
top: calc((var(--soy-header-height, 70px) - 36px) / 2);
z-index: 102;
width: 108px;
height: 38px;
border: 1px solid #d6dce8;
border-radius: 7px;
height: 36px;
border: 1px solid #d6d9e1;
border-radius: 8px;
background: #fff;
box-shadow: 0 8px 20px rgba(31, 41, 55, 0.06);
box-shadow: 0 8px 20px rgba(30, 39, 60, 0.08);
color: #20242d;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
font-size: 14px;
font-weight: 800;
font-weight: 700;
cursor: pointer;
line-height: 1;
}

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { nextTick, onMounted, ref } from 'vue';
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import {
getConversationChildList,
getConversationDetail,
@@ -42,6 +42,8 @@ const currentTab = ref('progress');
const currentPage = ref(1);
const totalPage = ref(10);
const docScale = ref(1);
const analysisViewRef = ref<HTMLElement | null>(null);
const analysisPanelWidth = ref(640);
const visible = defineModel<boolean>('visible', {
default: false
});
@@ -50,6 +52,92 @@ const pendingAiSubmit = ref(false);
const emit = defineEmits(['close']);
const ANALYSIS_PANEL_WIDTH_STORAGE_KEY = 'ai_read_analysis_panel_width_v1';
const ANALYSIS_PANEL_MIN_WIDTH = 420;
const ANALYSIS_DOCUMENT_MIN_WIDTH = 520;
const ANALYSIS_RETURN_RAIL_WIDTH = 80;
const ANALYSIS_RESIZE_HANDLE_WIDTH = 8;
let draggingResize = false;
function getAnalysisPanelBounds() {
const rect = analysisViewRef.value?.getBoundingClientRect();
const viewWidth = rect?.width || window.innerWidth;
const availableWidth = Math.max(0, viewWidth - ANALYSIS_RETURN_RAIL_WIDTH - ANALYSIS_RESIZE_HANDLE_WIDTH);
const maxWidth = Math.max(
ANALYSIS_PANEL_MIN_WIDTH,
Math.min(availableWidth - ANALYSIS_DOCUMENT_MIN_WIDTH, availableWidth * 0.7)
);
return {
min: Math.min(ANALYSIS_PANEL_MIN_WIDTH, maxWidth),
max: maxWidth
};
}
function clampAnalysisPanelWidth(width: number) {
const bounds = getAnalysisPanelBounds();
return Math.round(Math.min(bounds.max, Math.max(bounds.min, width)));
}
function setAnalysisPanelWidth(width: number, persist = false) {
const nextWidth = clampAnalysisPanelWidth(width);
analysisPanelWidth.value = nextWidth;
if (persist) {
localStorage.setItem(ANALYSIS_PANEL_WIDTH_STORAGE_KEY, String(nextWidth));
}
}
function applyStoredAnalysisPanelWidth() {
const storedWidth = Number(localStorage.getItem(ANALYSIS_PANEL_WIDTH_STORAGE_KEY));
if (Number.isFinite(storedWidth) && storedWidth > 0) {
setAnalysisPanelWidth(storedWidth);
return;
}
setAnalysisPanelWidth(analysisPanelWidth.value);
}
function updateAnalysisPanelWidthFromPointer(event: PointerEvent, persist = false) {
const rect = analysisViewRef.value?.getBoundingClientRect();
if (!rect) return;
setAnalysisPanelWidth(rect.right - event.clientX, persist);
}
function stopAnalysisResize(event?: PointerEvent) {
if (!draggingResize) return;
draggingResize = false;
document.body.classList.remove('analysis-resizing');
if (event) updateAnalysisPanelWidthFromPointer(event, true);
}
function handleAnalysisResizePointerDown(event: PointerEvent) {
event.preventDefault();
draggingResize = true;
document.body.classList.add('analysis-resizing');
(event.currentTarget as HTMLElement).setPointerCapture?.(event.pointerId);
updateAnalysisPanelWidthFromPointer(event);
}
function handleAnalysisResizeKeydown(event: KeyboardEvent) {
if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
event.preventDefault();
setAnalysisPanelWidth(analysisPanelWidth.value + (event.key === 'ArrowLeft' ? 40 : -40), true);
}
function handleAnalysisResizeMove(event: PointerEvent) {
if (!draggingResize) return;
updateAnalysisPanelWidthFromPointer(event);
}
function handleAnalysisResizeCancel() {
stopAnalysisResize();
}
function handleWindowResize() {
setAnalysisPanelWidth(analysisPanelWidth.value);
}
const back = () => {
// 判断下有没有到100%,不然要提醒别人,他可以选择是否要返回
if (readingProgressRef.value.completeRead() < 100) {
@@ -267,6 +355,28 @@ onMounted(() => {
if (props.ossId) {
ossId.value = props.ossId;
}
window.addEventListener('pointermove', handleAnalysisResizeMove);
window.addEventListener('pointerup', stopAnalysisResize);
window.addEventListener('pointercancel', handleAnalysisResizeCancel);
window.addEventListener('resize', handleWindowResize);
});
watch(visible, async value => {
if (!value) {
stopAnalysisResize();
return;
}
await nextTick();
applyStoredAnalysisPanelWidth();
});
onBeforeUnmount(() => {
stopAnalysisResize();
window.removeEventListener('pointermove', handleAnalysisResizeMove);
window.removeEventListener('pointerup', stopAnalysisResize);
window.removeEventListener('pointercancel', handleAnalysisResizeCancel);
window.removeEventListener('resize', handleWindowResize);
});
// 暴露方案
@@ -367,7 +477,12 @@ const handleSearch = async (payload: any) => {
</script>
<template>
<section v-if="visible" class="analysis-view">
<section
v-if="visible"
ref="analysisViewRef"
class="analysis-view"
:style="{ '--analysis-panel-width': `${analysisPanelWidth}px` }"
>
<aside class="analysis-return-rail">
<button class="analysis-back" type="button" @click="back">
<span></span>
@@ -411,6 +526,16 @@ const handleSearch = async (payload: any) => {
</div>
</section>
<div
class="analysis-resize-handle"
role="separator"
aria-orientation="vertical"
aria-label="调整文档和解析面板宽度"
tabindex="0"
@pointerdown="handleAnalysisResizePointerDown"
@keydown="handleAnalysisResizeKeydown"
></div>
<aside class="analysis-panel">
<nav class="analysis-tabs" aria-label="解析面板">
<button
@@ -488,7 +613,7 @@ const handleSearch = async (payload: any) => {
inset: 0;
z-index: 1600;
display: grid;
grid-template-columns: 80px minmax(620px, 1fr) clamp(640px, 50vw, 960px);
grid-template-columns: 80px minmax(520px, 1fr) 8px minmax(420px, var(--analysis-panel-width));
background: #f2f5f9;
overflow: hidden;
}
@@ -662,6 +787,58 @@ const handleSearch = async (payload: any) => {
transform-origin: top center;
}
.analysis-resize-handle {
position: relative;
z-index: 2;
width: 8px;
height: 100vh;
border-left: 1px solid #d9dde5;
border-right: 1px solid #d9dde5;
background: #eef1f5;
cursor: col-resize;
transition:
background 0.16s ease,
border-color 0.16s ease;
}
.analysis-resize-handle::before {
content: '';
position: absolute;
left: 50%;
top: 50%;
width: 3px;
height: 42px;
border-radius: 999px;
background: #b8c0cd;
transform: translate(-50%, -50%);
transition:
background 0.16s ease,
height 0.16s ease;
}
.analysis-resize-handle:hover,
.analysis-resize-handle:focus-visible,
:global(body.analysis-resizing) .analysis-resize-handle {
border-color: #aac8ff;
background: #e7f0ff;
outline: none;
}
.analysis-resize-handle:hover::before,
.analysis-resize-handle:focus-visible::before,
:global(body.analysis-resizing) .analysis-resize-handle::before {
height: 58px;
background: #2f7cff;
}
:global(body.analysis-resizing) {
cursor: col-resize;
}
:global(body.analysis-resizing) .analysis-view {
user-select: none;
}
.document-empty {
padding: 72px;
}
@@ -709,17 +886,6 @@ const handleSearch = async (payload: any) => {
overflow: visible;
box-shadow: 0 18px 42px rgba(31, 45, 61, 0.08);
transition: width 0.24s ease, box-shadow 0.24s ease, transform 0.24s ease;
&::before {
position: absolute;
left: -17px;
top: 0;
bottom: 0;
width: 1px;
background: #cfd8e5;
content: '';
}
}
.analysis-tabs {
@@ -854,7 +1020,7 @@ const handleSearch = async (payload: any) => {
@media (max-width: 1280px) {
.analysis-view {
grid-template-columns: 64px minmax(520px, 1fr) minmax(520px, 46vw);
grid-template-columns: 64px minmax(420px, 1fr) 8px minmax(420px, var(--analysis-panel-width));
}
.analysis-back {
@@ -880,6 +1046,10 @@ const handleSearch = async (payload: any) => {
grid-template-columns: 56px minmax(0, 1fr);
}
.analysis-resize-handle {
display: none;
}
.analysis-panel {
position: fixed;
right: 12px;

View File

@@ -825,7 +825,7 @@ defineExpose({
</div>
<div v-if="isLocating" class="ai-read-document-preview__locating-mask">
<div class="ai-read-document-preview__locating-actions">
<NButton size="small" type="default" @click="cancelLocate">取消定位</NButton>
<NButton class="app-cancel-button" size="small" type="default" @click="cancelLocate">取消定位</NButton>
</div>
<div class="ai-read-document-preview__locating-card">
<NSpin size="small" />

View File

@@ -104,7 +104,7 @@ watch(visible, () => {
/>
<template #footer>
<NSpace justify="end" :size="16">
<NButton class="ai-read-modal-cancel" @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
<NButton class="ai-read-modal-cancel app-cancel-button" @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
<NButton type="primary" class="button-shadow" :disabled="!ossId" @click="handleSubmit">开始解析</NButton>
</NSpace>
</template>
@@ -132,9 +132,13 @@ watch(visible, () => {
}
.ai-read-modal-cancel {
--n-border-hover: 1px solid #ffbf72 !important;
--n-border-focus: 1px solid #ffbf72 !important;
--n-text-color-hover: #c86f1a !important;
--n-text-color-focus: #c86f1a !important;
--n-color: #fff !important;
--n-color-hover: #f8fafc !important;
--n-color-focus: #fff !important;
--n-color-pressed: #f3f4f6 !important;
--n-text-color: #374151 !important;
--n-text-color-hover: #1f2937 !important;
--n-text-color-focus: #374151 !important;
--n-text-color-pressed: #111827 !important;
}
</style>

View File

@@ -117,11 +117,12 @@ const jumpTo = (message: string) => {
}
if (target) {
setActiveSectionCard(target);
scrollWithinContainer(target);
}
};
const toggleThink = msg => {
const toggleThink = (msg: any) => {
msg.isThinkShrink = !msg.isThinkShrink;
};
// 通过AI查询数据
@@ -137,10 +138,10 @@ const queryData = () => {
files: [props.ossId],
continuous: true
},
res => {
(res: any) => {
appendContent(res.data.content || '');
},
err => {
(err: any) => {
isGenerating.value = false;
console.log('err');
console.log(err);
@@ -339,6 +340,84 @@ function markReportTitle(root?: Element | null) {
});
}
function setActiveSectionCard(target?: Element | null) {
const card = target?.closest?.('.section-detail-card') || target;
const root = document.getElementById('docx-container-inner-slice')?.querySelector<HTMLElement>('.mc-markdown-render');
root?.querySelectorAll('.section-detail-card').forEach(item => {
item.classList.toggle('active', item === card);
});
}
function syncActiveSectionFromScroll() {
const scrollContainer = contentScrollRef.value;
const cards = Array.from(
document.querySelectorAll<HTMLElement>('#docx-container-inner-slice .section-detail-card[data-generated-section]')
);
if (!scrollContainer || !cards.length) return;
const panelRect = scrollContainer.getBoundingClientRect();
const anchorLine = panelRect.top + 120;
let current = cards[0];
cards.forEach(card => {
if (card.getBoundingClientRect().top <= anchorLine) {
current = card;
}
});
setActiveSectionCard(current);
const title = toPlainTagText(current.querySelector('h2, h3')?.textContent || '');
if (title) {
activeTag.value = title;
}
}
function wrapMarkdownSections(root?: Element | null) {
if (!root || root.querySelector('.section-detail-card[data-generated-section]')) return;
const children = Array.from(root.children);
let sectionIndex = 0;
children.forEach(child => {
if (!child.parentElement || child.closest('.section-detail-card')) return;
if (!['H2', 'H3'].includes(child.tagName) || child.classList.contains('report-title')) return;
sectionIndex += 1;
child.textContent = toPlainTagText(child.textContent || '');
const section = document.createElement('section');
section.className = 'section-detail-card';
section.dataset.generatedSection = 'true';
const header = document.createElement('header');
const index = document.createElement('span');
index.className = 'section-index';
index.textContent = String(sectionIndex).padStart(2, '0');
const titleWrap = document.createElement('div');
child.parentElement.insertBefore(section, child);
titleWrap.appendChild(child);
header.append(index, titleWrap);
section.appendChild(header);
let next = section.nextElementSibling;
let hasTable = false;
while (next && !['H2', 'H3'].includes(next.tagName)) {
const current = next;
next = next.nextElementSibling;
if (current.matches('table, .part-table-scroll') || current.querySelector('table, .part-table-scroll')) {
hasTable = true;
}
section.appendChild(current);
}
if (hasTable) {
section.classList.add('table-section');
}
});
root.querySelector('.section-detail-card')?.classList.add('active');
}
function bindParagraphLocateRows(root?: Element | null) {
const rowList = root?.querySelectorAll('tbody tr') || [];
rowList.forEach(row => {
@@ -369,6 +448,7 @@ async function processMarkdownTables() {
wrapMarkdownTables(markdownRoot);
markReportTitle(markdownRoot);
wrapMarkdownSections(markdownRoot);
hideParagraphIdColumns(markdownRoot);
bindParagraphLocateRows(markdownRoot);
}
@@ -540,7 +620,7 @@ defineExpose({
>
重新解读
</ElButton>
<div class="part-tags">
<div class="section-anchor-nav part-tags" aria-label="分项解读目录">
<ElTag
v-for="(item, index) in matchs"
:key="item"
@@ -553,14 +633,14 @@ defineExpose({
</ElTag>
</div>
</ElRow>
<div ref="contentScrollRef" class="part-reading-scroll">
<div ref="contentScrollRef" class="part-reading-scroll" @scroll="syncActiveSectionFromScroll">
<div v-if="isGenerating && !content" class="processing-state w-full flex flex-col items-center justify-center gap-16px">
<div class="processing-title">正在生成分项解读</div>
<div class="processing-desc">系统正在按主题拆分招标文件并提取结构化结果,请稍候...</div>
</div>
<ElEmpty v-else-if="!content" description="暂无分项解读内容" class="m-auto" />
<ElRow v-else>
<div class="w-full">
<div class="sections-panel w-full">
<div id="docx-container-inner-slice">
<McMarkdownCard ref="docxContainer" :enable-think="false" :content="content"></McMarkdownCard>
</div>
@@ -592,14 +672,14 @@ defineExpose({
align-items: center;
justify-content: space-between;
gap: 12px;
border: 1px solid #dfe7f3;
border: 1px solid #e3e8f2;
border-radius: 8px;
background: #fafcff;
padding: 14px;
padding: 15px 14px;
}
.sections-summary h3 {
margin: 0 0 8px;
margin: 0 0 12px;
color: #111827;
font-size: 16px;
font-weight: 800;
@@ -627,35 +707,63 @@ defineExpose({
margin-bottom: 0;
}
.part-tags {
.section-anchor-nav {
position: sticky;
top: 0;
top: -13px;
z-index: 4;
display: flex;
flex-wrap: wrap;
gap: 8px;
align-content: flex-start;
gap: 8px 7px;
max-height: 116px;
overflow: auto;
width: 100%;
border-bottom: 1px solid #e7edf6;
background: rgba(255, 255, 255, 0.96);
padding: 10px 0 12px;
padding: 12px 0 11px;
backdrop-filter: blur(10px);
}
.section-anchor-nav::-webkit-scrollbar,
.part-reading-scroll::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.section-anchor-nav::-webkit-scrollbar-thumb,
.part-reading-scroll::-webkit-scrollbar-thumb {
border-radius: 999px;
background: #8c8c8c;
}
.section-anchor-nav::-webkit-scrollbar-track,
.part-reading-scroll::-webkit-scrollbar-track {
background: #f0f3f7;
}
.part-tag {
flex: 0 0 auto;
min-height: 29px;
height: auto;
border-radius: 5px;
cursor: pointer;
font-size: 13px;
border-color: #b9dcff;
background: #edf6ff;
background: #eef7ff;
color: #1680e8;
font-weight: 700;
line-height: 18px;
padding: 4px 11px;
white-space: nowrap;
transition: background 0.16s ease, border-color 0.16s ease, color 0.16s ease, box-shadow 0.16s ease;
}
.part-tag.active,
.part-tag:hover {
border-color: #7cbcff;
background: #e5f2ff;
}
.part-tag.active {
border-color: #2d8cff;
background: #2d8cff;
color: #fff;
@@ -667,7 +775,13 @@ defineExpose({
min-height: 0;
overflow: auto;
overscroll-behavior: contain;
padding-right: 2px;
padding: 0 2px 0 0;
}
.sections-panel {
display: grid;
gap: 12px;
padding-bottom: 20px;
}
#docx-container-inner-slice {
@@ -743,7 +857,7 @@ defineExpose({
}
.part-reading-scroll :deep(.mc-markdown-render .report-title) {
display: flex;
display: none;
align-items: center;
gap: 10px;
min-height: 52px;
@@ -874,8 +988,181 @@ defineExpose({
.part-reading-scroll :deep(.mc-markdown-render table th:nth-child(5)) {
width: 320px;
}
.part-reading-scroll :deep(.section-detail-card) {
scroll-margin-top: 126px;
border: 1px solid #dfe7f3;
border-radius: 8px;
background: #fff;
overflow: hidden;
transition:
border-color 0.16s ease,
box-shadow 0.16s ease;
}
.part-reading-scroll :deep(.section-detail-card.active) {
border-color: #2d8cff;
box-shadow: none;
}
.part-reading-scroll :deep(.section-detail-card > header) {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 12px;
align-items: center;
border-bottom: 1px solid #edf1f6;
background: #fbfcff;
padding: 14px 14px 15px;
}
.part-reading-scroll :deep(.section-index) {
width: 34px;
height: 34px;
display: grid;
place-items: center;
border-radius: 8px;
background: #eef6ff;
color: #1677ff;
font-size: 13px;
font-weight: 800;
}
.part-reading-scroll :deep(.section-detail-card > header h2),
.part-reading-scroll :deep(.section-detail-card > header h3) {
display: block;
min-height: auto;
margin: 0;
border: 0;
border-radius: 0;
background: transparent;
padding: 0;
color: #111827;
font-size: 17px;
font-weight: 800;
line-height: 1.45;
}
.part-reading-scroll :deep(.section-detail-card > header h2::before),
.part-reading-scroll :deep(.section-detail-card > header h3::before) {
display: none;
content: none;
}
.part-reading-scroll :deep(.section-detail-card > p),
.part-reading-scroll :deep(.section-detail-card > blockquote) {
margin: 0;
border-bottom: 1px solid #edf1f6;
padding: 12px 14px;
color: #3f4654;
font-size: 13px;
line-height: 22px;
}
.part-reading-scroll :deep(.section-detail-card > ul),
.part-reading-scroll :deep(.section-detail-card > ol) {
display: grid;
gap: 0;
margin: 0;
padding: 0;
list-style: none;
}
.part-reading-scroll :deep(.section-detail-card > ul li),
.part-reading-scroll :deep(.section-detail-card > ol li) {
border-bottom: 1px solid #edf1f6;
padding: 12px 14px;
color: #343b48;
font-size: 13px;
line-height: 22px;
}
.part-reading-scroll :deep(.section-detail-card > ul li:last-child),
.part-reading-scroll :deep(.section-detail-card > ol li:last-child) {
border-bottom: 0;
}
.part-reading-scroll :deep(.section-detail-card .part-table-scroll) {
margin: 0;
border: 0;
border-radius: 0;
background: #fff;
box-shadow: none;
padding-bottom: 0;
}
.part-reading-scroll :deep(.section-detail-card table) {
width: 100%;
min-width: 780px;
table-layout: fixed;
border-collapse: collapse;
color: #1f2937;
font-size: 13px;
line-height: 22px;
}
.part-reading-scroll :deep(.section-detail-card table th),
.part-reading-scroll :deep(.section-detail-card table td) {
min-width: 0;
max-width: none;
border-right: 1px solid #e6edf7;
border-bottom: 1px solid #e6edf7;
padding: 12px 13px;
vertical-align: top;
word-break: break-word;
overflow-wrap: anywhere;
white-space: normal;
}
.part-reading-scroll :deep(.section-detail-card table th) {
height: 40px;
background: #f5f7fb;
color: #111827;
font-weight: 800;
text-align: left;
white-space: nowrap;
}
.part-reading-scroll :deep(.section-detail-card table td) {
height: 44px;
color: #1f2937;
}
.part-reading-scroll :deep(.section-detail-card table th:last-child),
.part-reading-scroll :deep(.section-detail-card table td:last-child) {
border-right: 0;
}
.part-reading-scroll :deep(.section-detail-card table tbody tr:last-child td) {
border-bottom: 0;
}
.part-reading-scroll :deep(.section-detail-card table th:nth-child(1)),
.part-reading-scroll :deep(.section-detail-card table td:nth-child(1)) {
width: 76px;
}
.part-reading-scroll :deep(.section-detail-card table th:nth-child(2)),
.part-reading-scroll :deep(.section-detail-card table td:nth-child(2)) {
width: 74px;
}
.part-reading-scroll :deep(.section-detail-card table th:nth-child(3)),
.part-reading-scroll :deep(.section-detail-card table td:nth-child(3)) {
width: 188px;
}
.part-reading-scroll :deep(.section-detail-card table th:nth-child(4)),
.part-reading-scroll :deep(.section-detail-card table td:nth-child(4)) {
width: 64px;
text-align: left;
}
.part-reading-scroll :deep(.section-detail-card table td:nth-child(4)) {
white-space: nowrap;
}
/deep/.tr-bg:hover {
color: rgb(1, 255, 64);
background: #f5faff;
color: #1677ff;
}
/deep/.mc-markdown-render h4,
/deep/.mc-markdown-render h5,

View File

@@ -15,11 +15,11 @@ interface MatchRow {
}
const mainTab = ref<MainTab>('workbench');
const currentStep = ref<CreditStep>('parse');
const currentStep = ref<CreditStep>('upload');
const requirementTab = ref<RequirementTab>('required');
const matchTab = ref<MatchTab>('achievement');
const dataTab = ref<DataTab>('achievement');
const uploadedFile = ref('1、招标文件正文(1).pdf');
const uploadedFile = ref('');
const isParsing = ref(false);
const isMatching = ref(false);
const matchSheetOpen = ref(false);
@@ -896,7 +896,7 @@ function downloadCreditExportFile() {
</div>
</section>
<footer class="credit-match-sheet-footer">
<button class="credit-secondary-btn" type="button" @click="matchSheetOpen = false">取消</button>
<button class="credit-secondary-btn app-cancel-button" type="button" @click="matchSheetOpen = false">取消</button>
<button class="credit-primary-btn" type="button" @click="confirmSelection">确认选择</button>
</footer>
</section>
@@ -966,25 +966,25 @@ function downloadCreditExportFile() {
.credit-history-button {
position: fixed;
top: calc((var(--soy-header-height, 70px) - 38px) / 2);
top: calc((var(--soy-header-height, 70px) - 36px) / 2);
left: calc(var(--soy-sider-width, 258px) + 20px);
z-index: 102;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
gap: 6px;
width: 108px;
height: 38px;
border: 1px solid #dfe6f0;
height: 36px;
border: 1px solid #d6d9e1;
border-radius: 8px;
background: #fff;
padding: 0 14px;
padding: 0;
color: #20242d;
font-size: 14px;
font-weight: 800;
font-weight: 700;
line-height: 1;
white-space: nowrap;
box-shadow: 0 12px 24px rgb(17 24 39 / 0.06);
box-shadow: 0 8px 20px rgb(30 39 60 / 0.08);
cursor: pointer;
}
@@ -997,7 +997,7 @@ function downloadCreditExportFile() {
position: relative;
z-index: 1;
min-height: calc(100vh - 198px);
padding: 78px clamp(24px, 12vw, 264px) 24px;
padding: 58px clamp(24px, 12vw, 264px) 24px;
}
.credit-shell {
@@ -1006,30 +1006,43 @@ function downloadCreditExportFile() {
padding-bottom: 30px;
}
.credit-main-tabs {
.credit-top-nav {
display: flex;
justify-content: center;
}
.credit-main-tabs {
display: inline-flex;
align-items: center;
gap: 8px;
width: max-content;
margin: 28px auto 40px;
border: 1px solid #dfe6f0;
min-height: 50px;
margin: 24px auto 40px;
border: 1px solid #e7e9ee;
border-radius: 999px;
background: #f3f6fa;
padding: 7px;
background: rgba(244, 245, 247, 0.96);
padding: 6px;
box-shadow:
0 10px 24px rgb(17 24 39 / 0.08),
inset 0 1px 0 #fff;
inset 0 1px 0 rgb(255 255 255 / 0.9),
0 10px 24px rgb(22 28 42 / 0.06);
}
.resource-tab {
height: 36px;
display: inline-flex;
height: 38px;
min-width: 86px;
align-items: center;
justify-content: center;
gap: 8px;
border: 0;
border-radius: 999px;
background: transparent;
color: #6c7687;
padding: 0 20px;
font-size: 13px;
font-weight: 800;
color: #5d6370;
padding: 0 15px;
font-size: 15px;
font-weight: 700;
cursor: pointer;
white-space: nowrap;
}
.resource-tab::before {
@@ -1037,15 +1050,15 @@ function downloadCreditExportFile() {
display: inline-block;
width: 8px;
height: 8px;
margin-right: 8px;
margin-right: 0;
border-radius: 999px;
background: #cbd5e1;
}
.resource-tab.active {
background: #fff;
color: #20242d;
box-shadow: 0 5px 14px rgb(17 24 39 / 0.08);
color: #222;
box-shadow: 0 6px 16px rgb(20 24 35 / 0.1);
}
.resource-tab.active::before {
@@ -1062,6 +1075,19 @@ function downloadCreditExportFile() {
padding: 0 20px 18px;
}
.credit-step-panel {
display: none;
}
.credit-step-panel.active {
display: block;
}
.credit-step-panel:nth-of-type(n + 2) {
max-width: 1120px;
margin: 0 auto;
}
.credit-step {
position: relative;
display: grid;
@@ -1119,7 +1145,7 @@ function downloadCreditExportFile() {
.credit-parse-result {
display: grid;
gap: 14px;
gap: 12px;
}
.credit-result-summary,
@@ -1142,7 +1168,7 @@ function downloadCreditExportFile() {
.credit-file-result-card {
display: grid;
gap: 12px;
padding: 16px;
padding: 14px 16px;
}
.credit-result-summary {
@@ -1180,7 +1206,7 @@ function downloadCreditExportFile() {
.credit-requirement-layout {
display: grid;
grid-template-columns: 260px minmax(0, 1fr);
gap: 14px;
gap: 12px;
min-height: 360px;
}
@@ -1297,15 +1323,16 @@ function downloadCreditExportFile() {
}
.credit-parse-fixed-actions {
position: sticky;
bottom: 0;
position: fixed;
right: 28px;
bottom: 20px;
z-index: 2;
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 14px;
padding: 26px 0 6px;
background: linear-gradient(180deg, rgb(255 255 255 / 0) 0%, #fff 34%);
margin-top: 0;
padding: 0;
background: transparent;
}
.credit-parse-fixed-actions .credit-primary-btn {
@@ -1321,16 +1348,23 @@ function downloadCreditExportFile() {
.credit-upload-copy h3 {
margin: 0;
color: #20222a;
font-size: 28px;
font-weight: 700;
line-height: 36px;
}
.credit-upload-copy p {
margin: 10px 0 0;
max-width: 420px;
color: #7a8290;
font-size: 14px;
line-height: 22px;
}
.credit-upload-shell {
width: 760px;
width: min(760px, 100%);
min-height: 268px;
margin: 0 auto;
border: 1px solid #dfe6f0;
border-radius: 14px;
@@ -1363,14 +1397,14 @@ function downloadCreditExportFile() {
.word-icon {
display: grid;
width: 54px;
height: 54px;
width: 30px;
height: 30px;
place-items: center;
border-radius: 14px;
background: linear-gradient(180deg, #2f73ff, #185ad6);
border-radius: 9px;
background: linear-gradient(145deg, #ffa126, #ff8617);
color: #fff;
font-size: 28px;
font-weight: 800;
font-size: 19px;
font-weight: 900;
}
.credit-upload-drop h3 {
@@ -1412,7 +1446,7 @@ function downloadCreditExportFile() {
.credit-upload-actions {
display: flex;
justify-content: center;
width: 760px;
width: min(760px, 100%);
margin: 24px auto 0;
}

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { computed, onUnmounted, ref } from 'vue';
import HtmlFilePreview from '@/components/html-file/index-only-read.vue';
import SettingDrawer from '../../tech-proposal/modules/review-proposal/content-check/modules/setting-drawer.vue';
import { useTechProposalRedesignContext } from '../context';
const {
@@ -39,6 +40,7 @@ const {
handleDetailPageSizeChange,
handleDetailTreeRowClick,
handleEditRule,
handleRuleSubmitted,
loadDetailTree,
openResultDetail,
queryDetailByPage,
@@ -47,10 +49,13 @@ const {
reviewResultCards,
rewriteContentCheck,
ruleScopeOptions,
ruleSetting,
ruleSource,
sectorOptions,
selectedRule,
selectedRuleId,
settingDrawerRef,
settingDrawerVisible,
setDetailDocPresign,
startCheck,
systemRuleObj,
@@ -243,6 +248,18 @@ onUnmounted(stopDetailDocResize);
</table>
<NEmpty v-if="checkRuleRows.length === 0" description="请选择或设置检查规则" />
</div>
<div v-show="contentRuleEditing" class="content-check-rule-drawer-host"></div>
<SettingDrawer
ref="settingDrawerRef"
v-model:visible="settingDrawerVisible"
v-model:value="ruleSetting.template"
drawer-class="content-rule-center-drawer"
:drawer-show-mask="false"
drawer-to=".content-check-rule-drawer-host"
drawer-width="100%"
:hide-title="true"
@submitted="handleRuleSubmitted"
/>
</section>
<div
@@ -371,7 +388,7 @@ onUnmounted(stopDetailDocResize);
:data="detailTable"
:bordered="false"
:single-line="false"
:scroll-x="668"
:scroll-x="432"
:row-props="detailRowProps"
class="detail-data-table"
:row-class-name="(row: any) => (detailSelectedRowId === row.id ? 'selected-row' : '')"
@@ -379,13 +396,13 @@ onUnmounted(stopDetailDocResize);
{
title: '序号',
key: 'index',
width: 56,
width: 48,
render: (_row: any, index: number) =>
(detailPagination.page - 1) * detailPagination.pageSize + index + 1
},
{ title: '错误位置(关键字)', key: 'location', width: 180 },
{ title: '错误详情', key: 'detail', width: 360 },
{ title: '页码', key: 'page', width: 72 }
{ title: '错误位置(关键字)', key: 'location', width: 132 },
{ title: '错误详情', key: 'detail', width: 196 },
{ title: '页码', key: 'page', width: 56 }
]"
/>
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
<script setup lang="ts">
import { onUnmounted, ref } from 'vue';
import { useTechProposalRedesignContext } from '../context';
const {
@@ -32,6 +33,24 @@ const toggleAnalysisCheckedRow = (row: any, checked: boolean) => {
const isAnalysisChecked = (row: any) => analysisCheckedRowKeys.value.includes(row.id);
const referenceFileSelectOpen = ref(false);
const targetFileSelectOpen = ref(false);
const collapseReferenceFileSelect = () => {
referenceFileSelectOpen.value = false;
};
const collapseTargetFileSelect = () => {
targetFileSelectOpen.value = false;
};
onUnmounted(() => {
analysisProposalFileIds.value.proposalFileId1 = [];
analysisProposalFileIds.value.proposalFileId2 = [];
collapseReferenceFileSelect();
collapseTargetFileSelect();
});
const getAnalysisStatusClass = (status?: string) => {
if (status?.includes('完成')) return 'success';
if (status?.includes('正在')) return 'pending';
@@ -62,6 +81,7 @@ const getAnalysisStatusClass = (status?: string) => {
<span>参照单位投标文件</span>
<NSelect
v-model:value="analysisProposalFileIds.proposalFileId1"
v-model:show="referenceFileSelectOpen"
class="regular-file-select design-select"
multiple
placeholder="请选择参照文件"
@@ -70,12 +90,14 @@ const getAnalysisStatusClass = (status?: string) => {
children-field="dtlList"
to="body"
:options="analysisProposalOptions"
@update:value="collapseReferenceFileSelect"
/>
</label>
<label>
<span>目标单位投标文件</span>
<NSelect
v-model:value="analysisProposalFileIds.proposalFileId2"
v-model:show="targetFileSelectOpen"
class="regular-file-select design-select"
multiple
placeholder="请选择目标文件"
@@ -84,6 +106,7 @@ const getAnalysisStatusClass = (status?: string) => {
children-field="dtlList"
to="body"
:options="analysisProposalOptions"
@update:value="collapseTargetFileSelect"
/>
</label>
</div>

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed, onUnmounted, ref } from 'vue';
import FileUploadFolder from '@/components/custom/file-upload-folder.vue';
import { useTechProposalRedesignContext } from '../context';
const {
@@ -9,13 +10,14 @@ const {
deleteResponseModule,
expandAllResponseRules,
generateResponseRule,
handleResponseDocFileFinish,
handleResponseHistoryRuleChange,
openResponseModuleDialog,
queryResponseResult,
responseAddDocForm,
responseAddDocVisible,
responseAiBidding,
responseAiBiddingOptions,
responseFileList,
responseGenerateLoading,
responseHistoryBidding,
responseHistoryCards,
@@ -29,6 +31,7 @@ const {
responseSourceTab,
responseStartCheck,
responseStatisticData,
submitResponseDocModal,
saveResponseRuleData,
selectedResponseDocName,
toggleResponseCheckedKey,
@@ -98,10 +101,22 @@ onUnmounted(stopReviewResize);
</div>
<div v-if="responseSourceTab === 'ai'" class="response-tab-panel-design">
<button class="review-response-upload" type="button" @click="responseAddDocVisible = true">
<strong>上传文件</strong>
<p>点击或拖拽上传响应规则文件</p>
</button>
<FileUploadFolder
v-model:file-list="responseFileList"
class="review-response-upload"
:max="10"
accept=".docx,.doc"
:show-tip="false"
upload-type="file"
show-file-list
action="/info/ossUpload"
@finish="handleResponseDocFileFinish"
>
<div class="review-response-upload-inner">
<strong>上传文件</strong>
<p>点击或拖拽上传响应规则文件</p>
</div>
</FileUploadFolder>
<div v-if="responseAiBidding" class="review-response-upload-file-strip">
<span class="review-response-upload-file-name">{{ selectedResponseDocName }}</span>
<button class="review-response-upload-file-remove" type="button" @click="responseAiBidding = null">
@@ -127,6 +142,7 @@ onUnmounted(stopReviewResize);
其他文件
</label>
</div>
<button class="review-response-save-upload" type="button" @click="submitResponseDocModal">保存上传文件</button>
</div>
<div v-else class="response-tab-panel-design history-panel-design">
@@ -194,22 +210,25 @@ onUnmounted(stopReviewResize);
>
<td>{{ row.index }}</td>
<td>
<button
v-if="row.isGroup"
class="response-tree-toggle"
type="button"
@click="toggleResponseRuleExpand(row.id)"
>
<span class="response-tree-toggle-icon" :class="{ expanded: row.expanded }"></span>
</button>
<label>
<input
type="checkbox"
:checked="responseCheckedKeys.includes(row.id)"
@change="toggleResponseCheckedKey(row.id, ($event.target as HTMLInputElement).checked)"
/>
{{ row.name }}
</label>
<div class="response-rule-name-row">
<button
v-if="row.isGroup"
class="response-tree-toggle"
type="button"
@click="toggleResponseRuleExpand(row.id)"
>
<span class="response-tree-toggle-icon" :class="{ expanded: row.expanded }"></span>
</button>
<span v-else class="response-tree-toggle-placeholder"></span>
<label>
<input
type="checkbox"
:checked="responseCheckedKeys.includes(row.id)"
@change="toggleResponseCheckedKey(row.id, ($event.target as HTMLInputElement).checked)"
/>
<span>{{ row.name }}</span>
</label>
</div>
<p v-if="row.scoringStandard" class="response-rule-desc">
{{ row.scoringStandard }}
</p>
@@ -224,6 +243,7 @@ onUnmounted(stopReviewResize);
>
+ 下级
</button>
<span v-else class="response-action-placeholder"></span>
<button
class="response-action-icon"
type="button"

View File

@@ -325,9 +325,7 @@ onActivated(() => {
background: linear-gradient(180deg, #ffad42 0%, #ff8614 100%) !important;
color: #fff !important;
font-size: 18px;
box-shadow:
0 12px 24px rgba(255, 138, 31, 0.24),
inset 0rem -0.19rem 0.06rem rgba(0, 0, 0, 0.12);
box-shadow: inset 0rem -0.19rem 0.06rem rgba(0, 0, 0, 0.12);
}
}

View File

@@ -69,7 +69,7 @@ const back = () => {
</div>
</div>
<div class="contain-header-actions">
<button class="cancel" type="button" @click="back">取消</button>
<button class="cancel app-cancel-button" type="button" @click="back">取消</button>
</div>
</div>
<div class="contain-main flex-1">
@@ -152,9 +152,9 @@ const back = () => {
}
.cancel {
border: 1px solid #ffd9a9;
background: #fff5e8;
color: #bd6a18;
border: 1px solid #dcdfe6;
background: #fff;
color: #374151;
}
}
}

View File

@@ -16,13 +16,15 @@ const props = withDefaults(
drawerShowMask?: boolean;
drawerTo?: string;
drawerWidth?: number | string;
hideTitle?: boolean;
}>(),
{
readonly: false,
drawerClass: '',
drawerShowMask: true,
drawerTo: undefined,
drawerWidth: '50%'
drawerWidth: '50%',
hideTitle: false
}
);
@@ -797,37 +799,32 @@ defineExpose({
:show-mask="props.drawerShowMask"
:z-index="2600"
>
<NDrawerContent title="设置检查项">
<div class="mb-10px flex items-center justify-between bg-#E3E3E3 px-20px py-10px">
<NText class="text-16px fw-700">{{ drawerDescription }}</NText>
<NSpace>
<NButton text class="text-16px text-#1E5EFA fw-700" @click="commonRuleSetting">通用规则</NButton>
<NButton
<NDrawerContent :title="props.hideTitle ? undefined : '设置检查项'">
<div class="setting-drawer-toolbar">
<NText class="setting-drawer-title">{{ drawerDescription }}</NText>
<div class="setting-drawer-actions">
<button class="setting-drawer-link" type="button" @click="commonRuleSetting">通用规则</button>
<button
v-if="!props.readonly && allSelected"
text
class="text-16px text-#1E5EFA fw-700"
class="setting-drawer-link"
type="button"
@click="toggleAllChecked(false)"
>
全部取消
</NButton>
<NButton
v-else-if="!props.readonly"
text
class="text-16px text-#1E5EFA fw-700"
@click="toggleAllChecked(true)"
>
</button>
<button v-else-if="!props.readonly" class="setting-drawer-link" type="button" @click="toggleAllChecked(true)">
全部勾选
</NButton>
<NButton
</button>
<button
v-if="independentExpandedItems.length <= 0"
text
class="text-16px text-#1E5EFA fw-700"
class="setting-drawer-link"
type="button"
@click="toggleAllExpand"
>
全部展开
</NButton>
<NButton v-else text class="text-16px text-#1E5EFA fw-700" @click="toggleAllExpand">全部收起</NButton>
</NSpace>
</button>
<button v-else class="setting-drawer-link" type="button" @click="toggleAllExpand">全部收起</button>
</div>
</div>
<div :class="{ 'setting-drawer--readonly': props.readonly }">
<Collapse
@@ -2509,8 +2506,7 @@ defineExpose({
<template #footer>
<NSpace>
<NButton
ghost
color="#2D78E8"
class="setting-drawer-cancel app-cancel-button"
@click="
() => {
visible = false;
@@ -2521,6 +2517,7 @@ defineExpose({
</NButton>
<NButton
v-if="!props.readonly"
class="setting-drawer-save"
color="#2D78E8"
@click="
() => {
@@ -2543,6 +2540,47 @@ defineExpose({
line-height: 22px;
}
.setting-drawer-toolbar {
display: flex;
min-height: 52px;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #dfe6f1;
background: #f5f7fb;
margin-bottom: 10px;
padding: 0 20px;
}
.setting-drawer-title {
color: #1f2937;
font-size: 14px;
font-weight: 800;
}
.setting-drawer-actions {
display: inline-flex;
align-items: center;
gap: 16px;
}
.setting-drawer-link {
height: 24px;
border: 0;
background: transparent;
box-shadow: none;
color: #1e5eff;
cursor: pointer;
font-size: 13px;
font-weight: 800;
line-height: 24px;
padding: 0;
&:hover {
color: #0f4fe8;
text-decoration: none;
}
}
.setting-drawer--readonly {
:deep(.n-checkbox),
:deep(.n-switch),
@@ -2555,6 +2593,28 @@ defineExpose({
}
}
.setting-drawer-cancel,
.setting-drawer-save {
--n-height: 28px !important;
width: 56px;
min-width: 56px;
height: 28px !important;
padding: 0 !important;
line-height: 28px;
}
.setting-drawer-cancel {
--n-color: #fff !important;
--n-color-hover: #f8fafc !important;
--n-color-pressed: #f3f4f6 !important;
--n-border: 1px solid #dcdfe6 !important;
--n-border-hover: 1px solid #cfd5df !important;
--n-border-pressed: 1px solid #c7ced9 !important;
--n-text-color: #374151 !important;
--n-text-color-hover: #1f2937 !important;
--n-text-color-pressed: #111827 !important;
}
:deep(.n-select) {
.n-base-selection {
--n-border: none !important;

View File

@@ -30,6 +30,7 @@ interface Props {
}
const TEMPLATE_OSS_ID = import.meta.env.PROD ? '2052923425601626114' : '2052669638985236481';
const appCancelButtonProps = { class: 'app-cancel-button', type: 'default' } as any;
const props = withDefaults(defineProps<Props>(), {
infoId: 0,
@@ -383,6 +384,7 @@ const openReUploadDialog = () => {
return (
<NSpace>
<NButton
class="app-cancel-button"
onClick={() => {
window.$dialog?.destroyAll();
}}
@@ -1196,6 +1198,7 @@ onBeforeUnmount(() => {
title="上传文件"
positive-text="确认"
negative-text="取消"
:negative-button-props="appCancelButtonProps"
@positive-click="submitCallback"
@negative-click="cancelCallback"
>

View File

@@ -106,7 +106,7 @@ const saveTextContent = () => {
<button v-if="currentTab === 'TextContent'" class="save" type="button" @click="saveTextContent">
确定保存
</button>
<button class="cancel" type="button" @click="back">取消</button>
<button class="cancel app-cancel-button" type="button" @click="back">取消</button>
</div>
</div>
<div class="contain-main flex-1">
@@ -209,9 +209,9 @@ const saveTextContent = () => {
}
.cancel {
border: 1px solid #ffd9a9;
background: #fff5e8;
color: #bd6a18;
border: 1px solid #dcdfe6;
background: #fff;
color: #374151;
}
}
}

View File

@@ -75,17 +75,11 @@ const columns = ref<any>([
h(
'div',
{
style: {
cursor: 'pointer',
maxwidth: '200px',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}
class: 'important-info-cell-ellipsis'
},
row.aiType
),
default: () => row.name
default: () => row.aiType
}
);
}
@@ -104,20 +98,7 @@ const columns = ref<any>([
}
},
{
trigger: () =>
h(
'div',
{
style: {
cursor: 'pointer',
maxWidth: '200px',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}
},
row.content
),
trigger: () => h('div', { class: 'important-info-cell-ellipsis important-info-content-cell' }, row.content),
default: () => row.content
}
);
@@ -473,6 +454,18 @@ onMounted(() => {
background-color: transparent;
}
}
:deep(.important-info-cell-ellipsis) {
max-width: 96px;
overflow: hidden;
cursor: pointer;
text-overflow: ellipsis;
white-space: nowrap;
}
:deep(.important-info-content-cell) {
max-width: 176px;
}
}
}

View File

@@ -144,8 +144,8 @@ const getImporantInformation = async () => {
ossId1: item.ossId1,
ossId2: item.ossId2,
flag: item.flag,
smallContent: item.smallContent,
smallContentB: item.smallContentB,
smallContent: item.smallContent || content.smallContent || content.content || '',
smallContentB: item.smallContentB || content.smallContentB || content.contentB || '',
id: item.id,
proposalDtlId1: item.proposalDtlId1,
proposalDtlId2: item.proposalDtlId2
@@ -653,7 +653,9 @@ onMounted(() => {
@click="handleClick(item)"
>
<div class="size-12px border-rd-50%" :class="item.flag ? 'bg-#39C324' : 'bg-#FF0F0F'"></div>
<div class="text-hidden ml-5px min-w-0 flex-1">{{ item.smallContent || item.content }}</div>
<div class="text-hidden ml-5px min-w-0 flex-1" :title="item.smallContent">
{{ item.smallContent }}
</div>
</div>
</div>
</NCollapseItem>

View File

@@ -35,8 +35,8 @@ watch(visible, val => {
:mask-closable="false"
>
<div class="flex flex-col items-center justify-center border-rd-0.51rem bg-white px-12 pb-8 pt-16">
<img src="@/assets/imgs/ai-review/loading-icon.png" alt="" />
<NProgress type="line" :percentage="percentage" class="mt-4 w-42.81rem" />
<img src="@/assets/imgs/ai-review/upload-progress.svg" alt="" />
<NProgress type="line" :percentage="percentage" class="upload-progress mt-4 w-42.81rem" />
<NText v-if="percentage < 100" class="mt-4 text-4 color-#383838">正在上传...</NText>
<NText v-else class="mt-4 text-4 color-#383838">上传完成</NText>
</div>
@@ -45,11 +45,17 @@ watch(visible, val => {
<style lang="scss" scoped>
img {
width: 10.13rem;
height: 11.69rem;
width: 15rem;
height: 9.5rem;
object-fit: contain;
}
:deep(.n-base-close) {
display: none;
}
.upload-progress {
--n-fill-color: #ff9f22 !important;
--n-rail-color: #fff1dc !important;
}
</style>

View File

@@ -154,6 +154,14 @@ const canManageCurrentRuleType = computed(() => activeTemplateType.value === CUS
const isSystemRuleSource = computed(() => activeTemplateType.value === SYSTEM_TEMPLATE_TYPE);
const isSystemRuleForm = computed(() => ruleForm.value.templateType === SYSTEM_TEMPLATE_TYPE);
const currentRuleCount = computed(() => getRuleCount(ruleForm.value.checkConfig));
const categorySortOptions = computed(() => {
const maxSort = Math.max(categoryList.value.length + 1, Number(categoryForm.value.sortOrder) || 1);
return Array.from({ length: maxSort }, (_, index) => {
const value = index + 1;
return { label: String(value), value };
});
});
const detailDrawerWidth = computed(() => (isRuleLibrary.value ? 420 : 640));
const detailDrawerTitle = computed(() => {
if (!isRuleLibrary.value) return isTemplateLibrary.value ? '解析结果' : '详情';
@@ -666,8 +674,11 @@ async function loadDocToEditForm(row: any, type: OperateType) {
async function selectDocCard(row: any) {
if (!isTemplateLibrary.value) return;
if (detailVisible.value) {
detailVisible.value = false;
await nextTick();
}
await loadDocToEditForm(row, 'edit');
detailVisible.value = false;
}
async function openDocPanel(row: any, type: OperateType) {
@@ -866,20 +877,24 @@ function deleteOutline(key: string) {
remove(outlineList.value);
}
function getOutlineText(node: TreeNode) {
return node.serialNumber ? `${node.serialNumber} ${node.name}` : node.name;
}
function renderOutlineNode(nodes: TreeNode[]): VNodeChild[] {
return nodes.map(node =>
h('div', { class: 'outline-group', key: node.key }, [
h('div', { class: ['outline-row', `level-${Math.min(node.level, 3)}`] }, [
detailReadonly.value
? h('span', node.serialNumber ? `${node.serialNumber} ${node.name}` : node.name)
? h('span', getOutlineText(node))
: h('div', { class: 'outline-name-edit' }, [
node.serialNumber ? h('em', node.serialNumber) : null,
h(NInput, {
value: node.name,
value: getOutlineText(node),
size: 'small',
placeholder: '请输入目录名称',
onUpdateValue: (value: string) => {
node.name = value;
delete node.serialNumber;
}
})
]),
@@ -1030,7 +1045,7 @@ onMounted(async () => {
<NFormItem label="模版描述" path="templateDescription">
<NInput v-model:value="detailForm.templateDescription" placeholder="请输入模版描述" />
</NFormItem>
<NFormItem label="文件上传" path="ossId">
<NFormItem class="resource-upload-form-item" label="文件上传" path="ossId">
<FileUploadFolder
v-model:file-list="uploadFileList"
:file-size="50"
@@ -1114,7 +1129,7 @@ onMounted(async () => {
</p>
<div class="template-card-ops">
<button type="button" title="查看" aria-label="查看" @click="openRulePanel(item, 'view')">
<SvgIcon local-icon="view" />
<SvgIcon icon="material-symbols:visibility-outline" />
</button>
<button
v-if="canManageCurrentRuleType"
@@ -1124,7 +1139,7 @@ onMounted(async () => {
:disabled="isLockedCustomRule(item)"
@click="openRulePanel(item, 'edit')"
>
<SvgIcon local-icon="edit" />
<SvgIcon icon="material-symbols:edit-outline" />
</button>
<button
v-if="item.templateType === CUSTOM_TEMPLATE_TYPE"
@@ -1172,10 +1187,15 @@ onMounted(async () => {
aria-label="查看"
@click.stop="isTemplateLibrary ? openDocPanel(item, 'view') : openPreview(item)"
>
<SvgIcon local-icon="view" />
<SvgIcon icon="material-symbols:visibility-outline" />
</button>
<button type="button" title="编辑" aria-label="编辑" @click.stop="openDocPanel(item, 'edit')">
<SvgIcon local-icon="edit" />
<button
type="button"
title="编辑"
aria-label="编辑"
@click.stop="openDocPanel(item, 'edit')"
>
<SvgIcon icon="material-symbols:edit-outline" />
</button>
<NPopconfirm @positive-click="deleteDoc(item.id)">
<template #trigger>
@@ -1203,7 +1223,11 @@ onMounted(async () => {
:show-mask="!(isTemplateLibrary && !detailReadonly)"
:block-scroll="!(isTemplateLibrary && !detailReadonly)"
placement="right"
:class="{ 'resource-result-drawer': isTemplateLibrary, 'rule-detail-drawer': isRuleLibrary }"
:class="{
'resource-result-drawer': isTemplateLibrary,
'resource-upload-drawer': !isRuleLibrary,
'rule-detail-drawer': isRuleLibrary
}"
>
<NDrawerContent :title="detailDrawerTitle" closable>
<NForm
@@ -1335,12 +1359,34 @@ onMounted(async () => {
</FileUploadFolder>
</NFormItem>
</NForm>
<NForm
v-if="isTemplateLibrary"
:model="detailForm"
:rules="detailRules"
class="resource-template-drawer-form"
label-placement="top"
:disabled="detailReadonly"
>
<NFormItem label="工程类型" path="categoryId">
<NSelect
v-model:value="detailForm.categoryId"
:options="categoryList.map(item => ({ label: item.name, value: item.id }))"
placeholder="请选择工程类型"
/>
</NFormItem>
<NFormItem label="模板名称" path="templateName">
<NInput v-model:value="detailForm.templateName" placeholder="请输入模板名称" />
</NFormItem>
<NFormItem label="模板描述" path="templateDescription">
<NInput v-model:value="detailForm.templateDescription" placeholder="请输入模板描述" />
</NFormItem>
</NForm>
<div v-if="isTemplateLibrary" class="outline-panel">
<component :is="() => renderOutlineNode(outlineList)" />
</div>
<template #footer>
<NSpace justify="end">
<NButton @click="detailVisible = false">取消</NButton>
<NButton class="app-cancel-button" @click="detailVisible = false">取消</NButton>
<NButton
v-if="!detailReadonly"
type="primary"
@@ -1354,13 +1400,13 @@ onMounted(async () => {
</NDrawerContent>
</NDrawer>
<NModal v-model:show="categoryVisible" preset="card" title="添加模类型" class="max-w-90% w-420px">
<NModal v-model:show="categoryVisible" preset="card" title="添加模类型" class="category-type-modal max-w-90% w-560px">
<NForm ref="categoryFormRef" :model="categoryForm" :rules="categoryRules" label-placement="top">
<NFormItem label="模名称" path="name">
<NInput v-model:value="categoryForm.name" placeholder="请输入模名称" />
<NFormItem label="模名称" path="name">
<NInput v-model:value="categoryForm.name" placeholder="请输入模名称" />
</NFormItem>
<NFormItem label="排序选择" path="sortOrder">
<NInputNumber v-model:value="categoryForm.sortOrder" :min="1" />
<NSelect v-model:value="categoryForm.sortOrder" :options="categorySortOptions" />
</NFormItem>
<NFormItem label="分类描述" path="description">
<NInput v-model:value="categoryForm.description" type="textarea" placeholder="请输入分类描述" />
@@ -1368,7 +1414,7 @@ onMounted(async () => {
</NForm>
<template #footer>
<NSpace justify="end">
<NButton @click="categoryVisible = false">取消</NButton>
<NButton class="app-cancel-button" @click="categoryVisible = false">取消</NButton>
<NButton type="primary" :loading="saving" @click="saveCategory">保存</NButton>
</NSpace>
</template>
@@ -1384,13 +1430,14 @@ onMounted(async () => {
height: calc(100vh - 64px);
background: #fff;
overflow: hidden;
color: #20222a;
}
.resource-scroll {
height: 100%;
overflow-x: hidden;
overflow-y: auto;
padding: 18px 20px 44px;
padding: 28px 20px 44px;
overscroll-behavior: contain;
}
@@ -1414,8 +1461,8 @@ onMounted(async () => {
gap: 8px;
margin: 0 auto 18px;
border-radius: 999px;
background: rgba(244, 245, 247, 0.96);
border: 1px solid #e7e9ee;
background: rgba(244, 248, 255, 0.96);
border: 1px solid #dbe5f3;
padding: 6px;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.9),
@@ -1433,9 +1480,10 @@ onMounted(async () => {
border-radius: 999px;
background: transparent;
color: #5d6370;
padding: 0 15px;
padding: 0 16px;
font-size: 14px;
font-weight: 700;
white-space: nowrap;
cursor: pointer;
&.active {
@@ -1455,27 +1503,29 @@ onMounted(async () => {
}
.resource-hero {
margin: 0 0 18px;
margin: 32px 0 26px;
text-align: center;
h2 {
margin: 0;
color: #1f2937;
font-size: 30px;
line-height: 44px;
line-height: 42px;
font-weight: 800;
letter-spacing: 0;
}
}
.resource-upload-card {
position: relative;
width: min(744px, 100%);
min-height: 236px;
min-height: 258px;
margin: 0 auto;
border: 1px solid #1f2026;
border-radius: 20px;
border: 1px solid #d9e4f2;
border-radius: 14px;
background: #fff;
padding: 16px;
box-shadow: 0 34px 70px rgba(23, 28, 42, 0.08);
box-shadow: 0 22px 48px rgba(23, 28, 42, 0.04);
}
.resource-fixed-head {
@@ -1506,7 +1556,7 @@ onMounted(async () => {
position: sticky;
top: 0;
z-index: 20;
width: min(760px, 100%);
width: min(744px, 100%);
margin: 0 auto 12px;
background: #fff;
padding: 0 10px 12px;
@@ -1613,11 +1663,13 @@ onMounted(async () => {
.resource-fields {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-auto-rows: 92px;
align-items: stretch;
gap: 12px;
&.bottom {
margin-top: 12px;
margin-top: 14px;
}
}
@@ -1626,34 +1678,133 @@ onMounted(async () => {
}
:deep(.resource-upload-card .n-form-item) {
width: 100%;
height: 92px;
min-height: 92px;
max-height: 92px;
border: 1px solid #dbe4f0;
border-radius: 10px;
background: #f0f1f5;
padding: 12px 16px;
background: #f7faff;
padding: 0 16px;
align-content: center;
margin-bottom: 0;
overflow: hidden;
}
:deep(.resource-upload-card .resource-upload-form-item) {
display: block;
border: 1px dashed #d4dfed;
background: #f7faff;
padding: 0;
}
:deep(.resource-upload-card .resource-upload-form-item .n-form-item-label) {
display: none;
}
:deep(.resource-upload-card .resource-upload-form-item .n-form-item-blank) {
display: block;
height: 100%;
}
:deep(.resource-upload-card .n-upload) {
display: block;
height: 100%;
}
:deep(.resource-upload-card .resource-upload-form-item .w-full),
:deep(.resource-upload-card .resource-upload-form-item .n-upload-trigger) {
display: block;
width: 100%;
height: 100%;
}
:deep(.resource-upload-card .n-upload-dragger) {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
border: 0;
border-radius: 10px;
background: transparent;
padding: 0;
box-shadow: none;
}
:deep(.resource-upload-card .n-upload-dragger:hover) {
box-shadow: none;
}
:deep(.resource-upload-card .n-upload-dragger > .flex-center:first-child) {
display: none;
}
:deep(.resource-upload-card .n-form-item-label) {
min-height: auto;
padding: 0 0 10px !important;
color: #536076;
font-size: 12px;
font-weight: 700;
}
:deep(.resource-upload-card .n-form-item-feedback-wrapper) {
display: none;
min-height: 0;
}
:deep(.resource-upload-card .n-form-item-blank) {
width: 100%;
min-height: auto;
}
:deep(.resource-upload-card .n-input),
:deep(.resource-upload-card .n-base-selection) {
--n-height: 24px !important;
--n-border: none !important;
--n-border-hover: none !important;
--n-border-focus: none !important;
--n-box-shadow-focus: none !important;
--n-font-size: 13px !important;
--n-font-weight: 700 !important;
--n-text-color: #0f172a !important;
--n-placeholder-color: #9aa6b6 !important;
background: transparent;
}
:deep(.resource-upload-card .n-input__input-el),
:deep(.resource-upload-card .n-base-selection-label) {
font-weight: 700;
}
.upload-inner {
height: 92px;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
gap: 4px;
text-align: center;
strong {
color: #1f2937;
font-size: 13px;
line-height: 17px;
font-weight: 800;
}
span {
color: #738198;
font-size: 12px;
line-height: 15px;
}
}
.word-icon {
width: 28px;
height: 28px;
margin-bottom: 4px;
border-radius: 8px;
display: grid;
place-items: center;
@@ -1665,14 +1816,15 @@ onMounted(async () => {
.resource-upload-tip {
margin: 9px 0 0;
color: #8d939f;
color: #7d8797;
font-size: 12px;
line-height: 18px;
}
.resource-template-download {
border: 0;
background: transparent;
color: #ff8418;
color: #ff6b00;
padding: 0;
font: inherit;
font-weight: 700;
@@ -1693,7 +1845,7 @@ onMounted(async () => {
.resource-actions {
text-align: center;
padding: 4px 0 8px;
padding: 32px 0 18px;
}
.primary-action {
@@ -1704,22 +1856,40 @@ onMounted(async () => {
background: linear-gradient(180deg, #ff9f22 0%, #ff8614 100%);
color: #fff;
font-weight: 700;
box-shadow: 0 8px 16px rgba(255, 138, 31, 0.22);
box-shadow: none;
}
:deep(.primary-action.n-button--disabled) {
background: #e7e7eb !important;
color: #fff !important;
box-shadow: none;
}
.template-category-tabs {
width: 100%;
max-width: 100%;
margin: 0 auto;
display: flex;
align-items: center;
gap: 30px;
padding: 2px 0 10px;
border-bottom: 1px solid #dfe6f0;
padding: 0 0 0;
overflow-x: auto;
scrollbar-width: none;
&::-webkit-scrollbar,
&::-webkit-scrollbar-button {
display: none;
width: 0;
height: 0;
}
button {
position: relative;
border: 0;
background: transparent;
color: #868c96;
padding: 0 0 12px;
padding: 0 0 13px;
font-size: 14px;
font-weight: 700;
white-space: nowrap;
@@ -1732,9 +1902,9 @@ onMounted(async () => {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 20px;
height: 3px;
bottom: -1px;
width: 60px;
height: 2px;
border-radius: 999px;
background: #ff8418;
}
@@ -1744,17 +1914,27 @@ onMounted(async () => {
.category-add-button {
width: 24px;
height: 24px;
display: grid;
flex: 0 0 24px;
place-items: center;
margin-left: auto;
border: 0;
border-radius: 50%;
background: #fff5ed;
color: #ff8418;
padding: 0;
font-size: 18px;
line-height: 1;
appearance: none;
&::after {
display: none;
}
}
}
.resource-toolbar {
display: flex;
display: none;
align-items: center;
gap: 12px;
margin-top: 4px;
@@ -1766,16 +1946,19 @@ onMounted(async () => {
.template-category-grid {
width: min(744px, 100%);
margin: 16px auto 0;
position: relative;
z-index: 0;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 14px;
padding-bottom: 30px;
padding-top: 46px;
padding-bottom: 46px;
}
.resource-rule-library + .n-spin-body,
.resource-rule-library ~ .n-spin .template-category-grid {
width: min(760px, 100%);
width: min(744px, 100%);
}
.rule-card {
@@ -1846,8 +2029,8 @@ onMounted(async () => {
&:hover {
border-color: #ff8a1f;
box-shadow: 0 16px 36px rgba(255, 140, 24, 0.16);
transform: translateY(-2px);
box-shadow: 0 10px 22px rgba(23, 28, 42, 0.08);
transform: none;
&::after {
opacity: 1;
@@ -1865,7 +2048,7 @@ onMounted(async () => {
&.is-selected {
border-color: #ff8a1f;
background: #fffaf4;
box-shadow: 0 16px 36px rgba(255, 140, 24, 0.14);
box-shadow: 0 12px 24px rgba(23, 28, 42, 0.08);
}
strong {
@@ -1900,27 +2083,28 @@ onMounted(async () => {
z-index: 2;
display: none;
align-items: center;
gap: 6px;
border: 1px solid rgba(255, 154, 42, 0.2);
border-radius: 999px;
background: rgba(18, 22, 31, 0.76);
padding: 4px;
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.18);
backdrop-filter: blur(8px);
gap: 8px;
border: 0;
border-radius: 0;
background: transparent;
padding: 0;
box-shadow: none;
backdrop-filter: none;
transform: translate(-50%, -50%);
button {
width: 26px;
height: 26px;
width: 28px;
height: 28px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 0;
border-radius: 50%;
background: transparent;
border: 1px solid #ff9a2a;
border-radius: 7px;
background: rgba(0, 0, 0, 0.72);
color: #ffb14d;
padding: 0;
font-size: 16px;
font-size: 14px;
line-height: 28px;
cursor: pointer;
transition:
background 0.16s ease,
@@ -1928,20 +2112,26 @@ onMounted(async () => {
transform 0.16s ease;
&:hover {
background: rgba(255, 138, 31, 0.18);
color: #fff;
background: rgba(0, 0, 0, 0.82);
color: #ffb14d;
transform: translateY(-1px);
}
&:disabled {
border-color: rgba(255, 170, 85, 0.55);
background: rgba(0, 0, 0, 0.36);
color: rgba(255, 186, 108, 0.52);
cursor: not-allowed;
transform: none;
}
&:last-child:hover {
background: rgba(240, 107, 134, 0.2);
color: #ffb4c2;
background: rgba(0, 0, 0, 0.82);
color: #ffb14d;
}
:deep(svg) {
color: currentColor;
}
}
}
@@ -1990,21 +2180,61 @@ onMounted(async () => {
padding: 60px 0;
}
:global(.resource-result-drawer .resource-template-drawer-form) {
display: grid;
grid-template-columns: 180px minmax(0, 1fr);
gap: 10px 12px;
padding: 14px 16px 12px;
border-bottom: 1px solid #edf1f6;
background: #fff;
}
:global(.resource-result-drawer .resource-template-drawer-form .n-form-item) {
margin-bottom: 0;
}
:global(.resource-result-drawer .resource-template-drawer-form .n-form-item:last-child) {
grid-column: 1 / -1;
}
:global(.resource-result-drawer .resource-template-drawer-form .n-form-item-label) {
min-height: auto;
padding: 0 0 6px !important;
color: #536076;
font-size: 12px;
font-weight: 700;
}
:global(.resource-result-drawer .resource-template-drawer-form .n-input),
:global(.resource-result-drawer .resource-template-drawer-form .n-base-selection) {
--n-height: 32px !important;
--n-border: 1px solid #d9e1ee !important;
--n-border-hover: 1px solid #d0d9e8 !important;
--n-border-focus: 1px solid #ff8a1f !important;
--n-border-radius: 6px !important;
--n-box-shadow-focus: 0 0 0 2px rgba(255, 138, 31, 0.12) !important;
--n-color: #f8fafc !important;
--n-color-focus: #fff !important;
}
:global(.resource-result-drawer .outline-panel) {
margin: 0 -24px;
border-top: 1px solid #edf0f5;
height: calc(100vh - 262px);
margin: 0 16px;
padding: 12px 14px 28px;
overflow-y: auto;
background: #fff;
}
:global(.resource-result-drawer .outline-row) {
min-height: 56px;
min-height: 50px;
display: grid;
grid-template-columns: minmax(0, 1fr) 210px;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
column-gap: 20px;
padding: 0 28px;
border-bottom: 1px solid #eceef2;
gap: 24px;
padding: 0;
border-bottom: 1px solid #edf1f6;
color: #2f3745;
font-size: 15px;
font-size: 14px;
transition:
background 0.16s ease,
color 0.16s ease;
@@ -2026,10 +2256,7 @@ onMounted(async () => {
:global(.resource-result-drawer .outline-name-edit) {
position: relative;
min-width: 0;
display: grid;
grid-template-columns: auto minmax(0, 1fr);
align-items: center;
gap: 8px;
display: block;
padding-left: 16px;
}
@@ -2044,12 +2271,6 @@ onMounted(async () => {
transform: translateY(-50%);
}
:global(.resource-result-drawer .outline-name-edit em) {
color: #475569;
font-style: normal;
white-space: nowrap;
}
:global(.resource-result-drawer .outline-name-edit .n-input) {
--n-height: 32px !important;
--n-border-radius: 6px !important;
@@ -2101,26 +2322,23 @@ onMounted(async () => {
}
:global(.resource-result-drawer .outline-actions) {
width: 210px;
display: grid;
grid-template-columns: 64px 64px 44px;
display: flex;
align-items: center;
justify-content: end;
justify-items: center;
column-gap: 12px;
gap: 14px;
white-space: nowrap;
}
:global(.resource-result-drawer .outline-action-btn) {
width: 100%;
height: 28px;
width: auto;
height: auto;
border: 0;
border-radius: 6px;
border-radius: 0;
background: transparent;
color: #ff7b19;
color: #ff8a1f;
padding: 0;
font-size: 13px;
font-weight: 700;
font-weight: 400;
text-align: center;
cursor: pointer;
transition:
@@ -2129,7 +2347,7 @@ onMounted(async () => {
}
:global(.resource-result-drawer .outline-action-btn:hover) {
background: rgba(255, 138, 31, 0.1);
background: transparent;
color: #ff5f00;
}
@@ -2138,35 +2356,193 @@ onMounted(async () => {
}
:global(.resource-result-drawer .outline-action-btn.danger:hover) {
background: rgba(255, 77, 0, 0.1);
background: transparent;
}
:global(.resource-result-drawer .n-drawer-header) {
height: 62px;
height: 56px;
padding: 0 18px !important;
border-bottom: 1px solid #edf0f5 !important;
border-bottom: 1px solid #e8edf5 !important;
background: #fffbf4;
}
:global(.resource-result-drawer .n-drawer-header__main) {
color: #111827;
font-size: 18px;
color: #1f2a3d;
font-size: 16px;
font-weight: 800;
}
:global(.resource-result-drawer .n-drawer-header__close) {
width: 32px;
height: 32px;
border-radius: 8px;
background: #f1f2f5;
color: #5d6470;
}
:global(.resource-result-drawer .n-drawer-body-content-wrapper) {
padding: 0 24px !important;
padding: 0 !important;
}
:global(.resource-upload-drawer .n-upload-dragger),
:global(.resource-upload-drawer .n-upload-dragger:hover) {
box-shadow: none !important;
}
:global(.category-type-modal.n-card) {
border: 1px solid #e5d8c8;
border-radius: 10px;
background: #fff;
overflow: hidden;
box-shadow: 0 22px 44px rgba(17, 24, 39, 0.2);
}
:global(.category-type-modal .n-card-header) {
min-height: 50px;
padding: 0 16px;
border-bottom: 1px solid #edf0f5;
background: #fffbf4;
}
:global(.category-type-modal .n-card-header__main) {
position: relative;
padding-left: 10px;
color: #1f2a3d;
font-size: 16px;
font-weight: 800;
line-height: 22px;
}
:global(.category-type-modal .n-card-header__main::before) {
content: '';
position: absolute;
left: 0;
top: 50%;
width: 4px;
height: 18px;
border-radius: 2px;
background: #ff9f22;
transform: translateY(-50%);
}
:global(.category-type-modal .n-card-header__close) {
color: #5f6b7a;
}
:global(.category-type-modal .n-card__content) {
padding: 14px 16px 11px;
background: #fff;
}
:global(.category-type-modal .n-form-item-label__text) {
color: #4b5a70;
font-size: 12px;
font-weight: 700;
}
:global(.category-type-modal .n-form-item) {
margin-bottom: 8px;
}
:global(.category-type-modal .n-form-item-label) {
min-height: auto;
padding: 0 0 7px !important;
}
:global(.category-type-modal .n-form-item-label__asterisk) {
display: none;
}
:global(.category-type-modal .n-input),
:global(.category-type-modal .n-input-number),
:global(.category-type-modal .n-base-selection) {
--n-height: 32px !important;
--n-border: 1px solid #e4d5c3 !important;
--n-border-hover: 1px solid #e4d5c3 !important;
--n-border-focus: 1px solid #ff8a1f !important;
--n-box-shadow-focus: none !important;
--n-placeholder-color: #9da8b6 !important;
--n-color: #fffaf2 !important;
--n-color-focus: #fffaf2 !important;
--n-text-color: #172033 !important;
width: 100%;
border-radius: 8px;
background: #fffaf2 !important;
}
:global(.category-type-modal .n-input-wrapper),
:global(.category-type-modal .n-input__input),
:global(.category-type-modal .n-input__textarea),
:global(.category-type-modal .n-base-selection-label) {
background: #fffaf2 !important;
}
:global(.category-type-modal .n-input--textarea) {
--n-height: 82px !important;
}
:global(.category-type-modal .n-input--textarea .n-input-wrapper) {
min-height: 82px;
}
:global(.category-type-modal .n-form-item-feedback-wrapper) {
min-height: 0;
}
:global(.category-type-modal .n-card__footer) {
min-height: 54px;
padding: 0 16px;
border-top: 1px solid #edf0f5;
background: #fff;
}
:global(.category-type-modal .n-card__footer .n-button) {
min-width: 76px;
height: 32px;
border-radius: 8px;
font-size: 13px;
font-weight: 700;
}
:global(.category-type-modal .n-card__footer .app-cancel-button) {
border: 0;
background: #fff0df;
color: #c86f1a;
}
:global(.category-type-modal .n-card__footer .n-button--primary-type) {
border: 0;
background: linear-gradient(180deg, #ff9f22 0%, #ff8614 100%);
color: #fff;
box-shadow: none;
}
:global(.resource-result-drawer .n-drawer-footer) {
height: 70px;
height: 60px;
padding: 0 16px !important;
border-top: 1px solid #edf0f5;
border-top: 1px solid #e8edf5;
background: #fff;
}
:global(.resource-result-drawer .n-drawer-footer .n-button:first-child) {
background: #fff4e9;
color: #c86f1a;
background: #fff;
color: #374151;
border: 1px solid #dcdfe6;
}
:global(.resource-result-drawer .n-drawer-footer .n-button) {
min-width: 76px;
height: 32px;
border-radius: 8px;
font-size: 13px;
font-weight: 700;
}
:global(.resource-result-drawer .n-drawer-footer .n-button--primary-type) {
border: 0;
background: linear-gradient(180deg, #ff9f22 0%, #ff8614 100%);
color: #fff;
box-shadow: 0 10px 22px rgba(255, 138, 31, 0.22);
}
:global(.rule-detail-drawer) {
@@ -2212,10 +2588,10 @@ onMounted(async () => {
:global(.rule-detail-drawer .n-drawer-footer .n-button:first-child) {
min-width: 54px;
height: 30px;
border: 0;
border: 1px solid #dcdfe6;
border-radius: 7px;
background: #fff1de;
color: #c86f1a;
background: #fff;
color: #374151;
font-size: 12px;
font-weight: 700;
}

View File

@@ -2097,7 +2097,7 @@ onUnmounted(() => {
<div v-if="isJumpingToCatalogue" class="compose-toc-locating">
<NSpin size="small" />
<span>定位中</span>
<button type="button" @click.stop="handleCancelCatalogueJump">取消</button>
<button class="app-cancel-button" type="button" @click.stop="handleCancelCatalogueJump">取消</button>
</div>
<NTree
v-if="catalogueData.length"
@@ -2153,7 +2153,7 @@ onUnmounted(() => {
<NText class="preview-blocked-state__title">文档预览暂不可用</NText>
<NText depth="3" class="preview-blocked-state__desc">{{ previewBlockedMessage }}</NText>
</div>
<div v-else-if="documentHtml">
<div v-else-if="documentHtml" class="compose-preview-canvas">
<div class="document-preview-wps" :style="previewContentStyle" v-html="documentHtml"></div>
<!-- 加载更多提示 - 移到文档内容最底部 -->
<div v-if="isLoadingMore" class="py-20px text-center">
@@ -2283,7 +2283,7 @@ onUnmounted(() => {
<style lang="scss" scoped>
:global(.catalogue-config-modal.n-card) {
width: min(780px, calc(100vw - 48px));
border-radius: 9px;
border-radius: 8px;
background: #fff;
box-shadow: 0 28px 70px rgba(27, 39, 64, 0.24);
overflow: hidden;
@@ -2298,10 +2298,10 @@ onUnmounted(() => {
:global(.catalogue-config-modal .n-card-header__main) {
position: relative;
padding-left: 10px;
color: #111827;
font-size: 15px;
font-weight: 700;
padding-left: 13px;
color: #0f172a;
font-size: 16px;
font-weight: 800;
line-height: 50px;
}
@@ -3553,19 +3553,47 @@ onUnmounted(() => {
padding: 12px 0 70px;
}
.contain .contain-main-wrapper-middle.compose-doc-scroll {
background: #e9e9e9 !important;
}
.compose-doc-scroll :deep(.n-spin-container),
.compose-doc-scroll :deep(.n-spin-content) {
min-height: 100%;
background: #e9e9e9 !important;
}
.compose-doc-scroll :deep(.n-spin-content > div),
.compose-doc-scroll :deep(.document-preview-wps) {
min-height: 100%;
background: #e9e9e9 !important;
}
.compose-preview-canvas {
min-height: 100%;
background: #e9e9e9 !important;
}
.document-preview-wps {
display: block;
min-height: 100%;
background: #e9e9e9 !important;
:deep(> *) {
background: transparent !important;
}
:deep(.docx-wrapper),
:deep(.document-wrapper),
:deep(.document-preview-container) {
background: transparent !important;
}
:deep(.doc-page) {
margin: 0 auto 70px !important;
border: 1px solid #ddd !important;
box-shadow: none !important;
background: #fff !important;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08) !important;
}
:deep(table) {

View File

@@ -467,14 +467,14 @@ onMounted(() => {
}
&.active {
color: #ff8a1f;
border-color: #ffcf9a;
color: #20242d;
border-color: #d6d9e1;
}
&:hover {
border-color: #ffcf9a;
box-shadow: 0 10px 24px rgba(255, 138, 31, 0.14);
color: #ff8a1f;
border-color: #d6d9e1;
box-shadow: 0 8px 20px rgba(30, 39, 60, 0.08);
color: #20242d;
transform: translateY(-1px);
}
}

View File

@@ -575,7 +575,7 @@ onMounted(async () => {
<template>
<div class="catalogue-config-panel">
<div class="catalogue-config-body">
<NForm label-placement="left" :label-width="104" :show-feedback="false" class="catalogue-config-form">
<NForm label-placement="left" :label-width="86" :show-feedback="false" class="catalogue-config-form">
<NFormItem label="目录模板">
<NSelect
v-model:value="selectedCatalogueId"
@@ -654,7 +654,7 @@ onMounted(async () => {
</NModal>
<div class="catalogue-config-footer">
<button class="catalogue-footer-button is-cancel" type="button" @click="handleClose">取消</button>
<button class="catalogue-footer-button is-cancel app-cancel-button" type="button" @click="handleClose">取消</button>
<button class="catalogue-footer-button is-save" type="button" :disabled="saving" @click="handleSave">
{{ saving ? '保存中...' : '保存配置' }}
</button>
@@ -673,7 +673,7 @@ onMounted(async () => {
.catalogue-config-body {
min-height: 0;
flex: 1;
padding: 14px 16px 20px;
padding: 14px 16px 18px;
overflow: auto;
}
@@ -686,7 +686,7 @@ onMounted(async () => {
:deep(.n-form-item-label) {
justify-content: flex-start;
min-height: 34px;
padding-right: 14px;
padding-right: 12px;
color: #1f2d3d;
font-size: 14px;
line-height: 34px;
@@ -700,13 +700,21 @@ onMounted(async () => {
--n-border: 1px solid #f0d7b8 !important;
--n-border-hover: 1px solid #f5b45f !important;
--n-border-active: 1px solid #f59e0b !important;
--n-border-focus: 1px solid #f59e0b !important;
--n-box-shadow-active: 0 0 0 2px rgba(245, 158, 11, 0.12) !important;
--n-box-shadow-focus: 0 0 0 2px rgba(245, 158, 11, 0.12) !important;
background: #fffaf2;
}
:deep(.n-base-selection-label) {
background: #fffaf2;
}
:deep(.n-base-selection--active),
:deep(.n-base-selection--focus) {
border-color: #f59e0b !important;
box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.12) !important;
}
}
.catalogue-switch-row {
@@ -755,7 +763,7 @@ onMounted(async () => {
}
.catalogue-tree-box {
margin-top: 15px;
margin-top: 12px;
padding-left: 2px;
overflow-x: auto;
@@ -792,11 +800,11 @@ onMounted(async () => {
.catalogue-config-footer {
display: flex;
flex: 0 0 56px;
flex: 0 0 58px;
align-items: center;
justify-content: flex-end;
gap: 10px;
padding: 0 16px;
padding: 0 21px;
border-top: 1px solid #e8edf5;
background: #fff;
}
@@ -812,9 +820,9 @@ onMounted(async () => {
}
.catalogue-footer-button.is-cancel {
border: 1px solid #ffc47d;
background: #fff8ef;
color: #d97706;
border: 1px solid #dcdfe6;
background: #fff;
color: #374151;
}
.catalogue-footer-button.is-save {