From e32d17213665f032280db2f5fe85f3812670ec2d Mon Sep 17 00:00:00 2001
From: ai-coding <ai-coding@devgenie.local>
Date: 星期三, 29 七月 2026 14:50:07 +0800
Subject: [PATCH] Merge origin/feat/730-xywang into feat/deploy-uat

---
 src/views/userContent/contentCenter/components/AgreementVersionModal.vue |  174 +++++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 131 insertions(+), 43 deletions(-)

diff --git a/src/views/userContent/contentCenter/components/AgreementVersionModal.vue b/src/views/userContent/contentCenter/components/AgreementVersionModal.vue
index 4aba588..33d2b6d 100644
--- a/src/views/userContent/contentCenter/components/AgreementVersionModal.vue
+++ b/src/views/userContent/contentCenter/components/AgreementVersionModal.vue
@@ -1,13 +1,15 @@
 <script setup lang="ts">
 import { computed, reactive, ref, watch } from 'vue'
 import type { FormInst, FormRules } from 'naive-ui'
-import { NButton, NForm, NFormItem, NInput, NModal, NSpace } from 'naive-ui'
+import { NButton, NForm, NFormItem, NInput, NModal, NSelect, NSpace } from 'naive-ui'
 import AiRichTextEditor from '@/components/custom/Editor/AiRichTextEditor/index.vue'
+import { AGREEMENT_CATEGORY_OPTIONS } from '../constants/agreementCategory'
 
 export interface AgreementVersionFormValue {
   id?: number
   version: string
   content: string
+  agreementCategory?: number | null
 }
 
 const props = withDefaults(defineProps<{
@@ -32,7 +34,9 @@
 const formModel = reactive<AgreementVersionFormValue>({
   version: '',
   content: '',
+  agreementCategory: null,
 })
+const contentErrorMessage = ref('')
 
 const plainTextLength = computed(() => {
   return formModel.content
@@ -43,17 +47,12 @@
 })
 
 const rules: FormRules = {
-  content: [
+  agreementCategory: [
     {
       required: true,
-      validator: () => {
-        if (!plainTextLength.value)
-          return new Error('请输入协议内容')
-        // if (plainTextLength.value > 20000)
-        //   return new Error('协议内容最多输入20000个字符')
-        return true
-      },
-      trigger: ['blur', 'change', 'input'],
+      type: 'number',
+      message: '请选择协议类型',
+      trigger: ['blur', 'change'],
     },
   ],
 }
@@ -62,6 +61,8 @@
   formModel.id = props.rowData?.id
   formModel.version = props.rowData?.version || ''
   formModel.content = props.rowData?.content || ''
+  formModel.agreementCategory = props.rowData?.agreementCategory ?? null
+  contentErrorMessage.value = ''
   formRef.value?.restoreValidation()
 }
 
@@ -72,7 +73,9 @@
 async function handleSave() {
   if (props.readonly)
     return
-  
+
+  contentErrorMessage.value = plainTextLength.value ? '' : '请输入协议内容'
+
   try {
     await formRef.value?.validate()
   } catch (errors) {
@@ -80,12 +83,21 @@
     return
   }
 
+  if (contentErrorMessage.value)
+    return
+
   emit('save', {
     id: formModel.id,
     version: formModel.version,
     content: formModel.content,
+    agreementCategory: formModel.agreementCategory,
   })
 }
+
+watch(() => formModel.content, () => {
+  if (contentErrorMessage.value && plainTextLength.value)
+    contentErrorMessage.value = ''
+})
 
 watch(() => props.visible, (visible) => {
   if (visible)
@@ -100,30 +112,52 @@
     preset="card"
     :title="title"
     class="agreement-version-modal"
-    :style="{ width: '1000px' }"
+    :style="{ width: '1000px', height: '90vh', maxHeight: '90vh', display: 'flex', flexDirection: 'column' }"
+    :header-style="{ flexShrink: '0' }"
     :segmented="{ content: true, action: true }"
-    :content-style="{ padding: '24px 24px 0 24px' }"
+    :content-style="{ padding: '24px 24px 0 24px', display: 'flex', flexDirection: 'column', flex: '1', minHeight: '0', overflow: 'hidden' }"
     @close="handleClose"
   >
-    <NForm
-      ref="formRef"
-      :model="formModel"
-      :rules="rules"
-      label-placement="top"
-      require-mark-placement="left"
-    >
-      <NFormItem label="版本号" required>
-        <NInput v-model:value="formModel.version" disabled placeholder="系统自动生成" />
-      </NFormItem>
-      <NFormItem label="协议内容" path="content" required>
+    <div class="agreement-version-body">
+      <NForm
+        ref="formRef"
+        :model="formModel"
+        :rules="rules"
+        label-placement="top"
+        require-mark-placement="left"
+        class="agreement-fixed-form"
+      >
+        <div class="form-row">
+          <NFormItem label="版本号" required class="form-row-item">
+            <NInput v-model:value="formModel.version" disabled placeholder="系统自动生成" />
+          </NFormItem>
+          <NFormItem label="协议类型" path="agreementCategory" required class="form-row-item">
+            <NSelect
+              v-model:value="formModel.agreementCategory"
+              :options="AGREEMENT_CATEGORY_OPTIONS"
+              :disabled="readonly"
+              placeholder="请选择协议类型"
+            />
+          </NFormItem>
+        </div>
+      </NForm>
+
+      <div class="agreement-content-label">
+        协议内容
+        <span v-if="!readonly" class="agreement-content-required">*</span>
+      </div>
+      <div class="agreement-content-area">
         <div class="rich-text-wrapper">
           <div class="editor-font-tip">
             推荐字号:大标题(21)小标题(15)正文(14)
           </div>
-          <AiRichTextEditor v-model="formModel.content" :read-only="readonly" height="360px" />
+          <AiRichTextEditor v-model="formModel.content" :read-only="readonly" height="100%" />
         </div>
-      </NFormItem>
-    </NForm>
+        <div v-if="contentErrorMessage" class="agreement-content-error">
+          {{ contentErrorMessage }}
+        </div>
+      </div>
+    </div>
 
     <template #action>
       <NSpace justify="end" :size="8">
@@ -139,9 +173,67 @@
 </template>
 
 <style scoped lang="scss">
+.agreement-version-body {
+  display: flex;
+  flex-direction: column;
+  flex: 1;
+  min-height: 0;
+  overflow: hidden;
+}
+
+.agreement-fixed-form {
+  flex-shrink: 0;
+}
+
+.form-row {
+  display: flex;
+  gap: 24px;
+}
+
+.form-row-item {
+  flex: 1;
+  min-width: 0;
+}
+
+.agreement-content-label {
+  flex-shrink: 0;
+  margin-bottom: 8px;
+  font-size: 14px;
+  color: #171f26;
+}
+
+.agreement-content-required {
+  margin-left: 4px;
+  color: #d03050;
+}
+
+.agreement-content-area {
+  flex: 1;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+  padding-bottom: 24px;
+}
+
+.agreement-content-error {
+  flex-shrink: 0;
+  padding-top: 6px;
+  font-size: 12px;
+  color: #d03050;
+}
+
 .rich-text-wrapper {
   position: relative;
   width: 100%;
+  flex: 1;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+}
+
+.rich-text-wrapper :deep(.ai-editor-container) {
+  flex: 1;
+  min-height: 0;
 }
 
 .editor-font-tip {
@@ -159,34 +251,30 @@
 }
 
 .rich-text-wrapper :deep(.aie-container) {
-  min-height: 360px;
   font-size: 14px;
 }
+</style>
 
-.rich-text-wrapper :deep(.aie-main) {
-  min-height: 360px;
-}
-
-.content-count {
-  width: 100%;
-  margin-top: 8px;
-  text-align: right;
-  font-size: 12px;
-  color: rgba(23, 31, 38, 0.45);
-}
-
-:deep(.agreement-version-modal .n-card-header) {
+<style lang="scss">
+/*
+ * 以下规则不能加 scoped:preset="card" 的 NModal 实际渲染的 NCard/NCard 内部结构
+ * 是由 naive-ui 在组件外部独立创建的 DOM 节点,不带当前组件的 data-v-xxx 属性,
+ * scoped 下的 :deep() 选择器无法命中,必须用全局样式规则。
+ */
+.agreement-version-modal .n-card-header {
   padding: 20px 24px;
+  flex-shrink: 0;
 }
 
-:deep(.agreement-version-modal .n-card-header__main) {
+.agreement-version-modal .n-card-header__main {
   font-size: 16px;
   font-weight: 600;
   color: #171f26;
 }
 
-:deep(.agreement-version-modal .n-card__action) {
+.agreement-version-modal .n-card__action {
   background-color: transparent !important;
   padding: 16px 24px;
+  flex-shrink: 0;
 }
 </style>

--
Gitblit v1.7.1