浏览代码

📦 传承人自查首页

快乐的梦鱼 5 天之前
父节点
当前提交
30e9cb8fa4

+ 248 - 0
.tmp-showdoc-1.html

@@ -0,0 +1,248 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <meta
+      name="viewport"
+      content="width=device-width,viewport-fit=cover, initial-scale=1.0"
+    />
+    <meta name="renderer" content="webkit" />
+    <meta http-equiv="X-UA-Compatible" content="edge" />
+    <meta http-equiv="Expires" content="0" />
+    <meta http-equiv="Pragma" content="no-cache" />
+    <meta
+      http-equiv="Cache-control"
+      content="no-cache, no-store, must-revalidate"
+    />
+    <meta http-equiv="Cache" content="no-cache" />
+    <link rel="apple-touch-icon" href="https://showdoc-server.cdn.dfyun.com.cn/logo/w_64.png" />
+    <link rel="apple-touch-icon-precomposed" href="https://showdoc-server.cdn.dfyun.com.cn/logo/w_64.png" />
+    <link rel="icon" href="https://showdoc-server.cdn.dfyun.com.cn/favicon.ico" />
+    <title>ShowDoc</title>
+    <meta name="keywords" content="在线API文档 技术文档 数据字典 在线手册" />
+    <meta
+      name="description"
+      content="一个非常适合IT团队的在线API文档、技术文档工具。你可以使用Showdoc来编写在线API文档、技术文档、数据字典、在线手册"
+    />
+    <script src="https://showdoc-server.cdn.dfyun.com.cn/server-cdn-status.js?v=3"></script>
+    <script src="https://showdoc.cdn.dfyun.com.cn/cdn-status.js?v=2"></script>
+    <script type="text/javascript">
+      // ========================================
+      // 环境检测(优先执行)
+      // ========================================
+      var host = window.location.host
+      var port = window.location.port
+      
+      // 开发环境:Vite dev server(端口 8081)
+      var isDevEnv = (host.indexOf('localhost') > -1 || host.indexOf('127.0.0.1') > -1) && port === '8081'
+      
+      // 测试环境:构建后的代码 + 测试域名(不使用 CDN)
+      var isTestEnv = !isDevEnv && (
+        host.indexOf('wu.com') > -1 ||
+        host.indexOf('gaoyixia.com') > -1 ||
+        host.indexOf('test.showdoc') > -1 ||
+        host.indexOf('local.showdoc') > -1 ||
+        host.indexOf('sd.gaoyixia.com') > -1 ||
+        host.indexOf('127.0.0.1') > -1 ||
+        host.indexOf('192.168.') > -1 ||
+        host.indexOf('localhost') > -1
+      )
+
+      // ========================================
+      // 全局配置
+      // ========================================
+      if (isDevEnv) {
+        // 开发环境
+        window.DocConfig = {
+          server: '/server/index.php?s=',
+          originalServer: '/server/index.php?s=',
+          lang: 'zh-cn',
+          staticPath: '/'
+        }
+      } else if (isTestEnv) {
+        // 测试环境:使用本地资源(根目录)
+        window.DocConfig = {
+          server: '/server/index.php?s=',
+          originalServer: '/server/index.php?s=',
+          lang: 'zh-cn',
+          staticPath: '/'
+        }
+      } else {
+        // 生产环境(默认使用 CDN)
+        window.DocConfig = {
+          server: 'https://showdoc-server.cdn.dfyun.com.cn/server/index.php?s=',
+          originalServer: '/server/index.php?s=',
+          lang: 'zh-cn',
+          staticPath: 'https://showdoc.cdn.dfyun.com.cn/'
+        }
+        
+        // 生产环境 CDN 容灾
+        // Server CDN 容灾
+        if (typeof window.SHOWDOC_SERVER_CDN_STASTUS == 'undefined') {
+          window.DocConfig.server = '/server/index.php?s='
+        }
+        // 静态资源 CDN 容灾
+        if (typeof window.SHOWDOC_CDN_STASTUS == 'undefined') {
+          window.DocConfig.staticPath = '/'
+        }
+      }
+    </script>
+    <script type="module" crossorigin src="https://showdoc.cdn.dfyun.com.cn/assets/index-bf750cc3.js"></script>
+    <link rel="modulepreload" crossorigin href="https://showdoc.cdn.dfyun.com.cn/assets/vendor-775275a9.js">
+    <link rel="modulepreload" crossorigin href="https://showdoc.cdn.dfyun.com.cn/assets/vendor-vue-dc600416.js">
+    <link rel="modulepreload" crossorigin href="https://showdoc.cdn.dfyun.com.cn/assets/components-79c21c71.js">
+    <link rel="modulepreload" crossorigin href="https://showdoc.cdn.dfyun.com.cn/assets/core-9aca1148.js">
+    <link rel="modulepreload" crossorigin href="https://showdoc.cdn.dfyun.com.cn/assets/assets-7d25f5ec.js">
+    <link rel="stylesheet" href="https://showdoc.cdn.dfyun.com.cn/assets/vendor-49dc2278.css">
+    <link rel="stylesheet" href="https://showdoc.cdn.dfyun.com.cn/assets/vendor-vue-e0182f4b.css">
+    <link rel="stylesheet" href="https://showdoc.cdn.dfyun.com.cn/assets/components-2a251b15.css">
+    <link rel="stylesheet" href="https://showdoc.cdn.dfyun.com.cn/assets/assets-c462eb69.css">
+    <link rel="stylesheet" href="https://showdoc.cdn.dfyun.com.cn/assets/index-726c1082.css">
+  </head>
+
+  <body class="grey-bg">
+    <div class="index-item-block">
+      <div>INDEX_HTML</div>
+      <div>ITEM_HTML</div>
+    </div>
+    <div id="app"></div>
+
+    <!-- CDN 容灾脚本 -->
+    <script>
+      // CDN 容灾:如果 CDN 不可用,替换所有资源为本地路径
+      if (typeof window.SHOWDOC_CDN_STASTUS == 'undefined') {
+        // 把全局变量中的静态资源路径改为本地
+        window.DocConfig.staticPath = '/'
+        changeCDNToRoot()
+        setTimeout(() => {
+          changeCDNToRoot()
+        }, 500)
+      }
+
+      // 修改资源路径从 CDN 到本地,并重新加载
+      function changeCDNToRoot() {
+        const loadScript = src => {
+          return new Promise((resolve, reject) => {
+            const scriptTag = document.createElement('script')
+            scriptTag.src = src
+            scriptTag.type = 'module'
+            scriptTag.crossOrigin = 'anonymous'
+            scriptTag.onload = resolve
+            scriptTag.onerror = reject
+            document.head.appendChild(scriptTag)
+          })
+        }
+
+        const loadStylesheet = href => {
+          return new Promise((resolve, reject) => {
+            const linkTag = document.createElement('link')
+            linkTag.href = href
+            linkTag.rel = 'stylesheet'
+            linkTag.onload = resolve
+            linkTag.onerror = reject
+            document.head.appendChild(linkTag)
+          })
+        }
+
+        const updateLinkTags = async () => {
+          // 转换为数组,避免 HTMLCollection live 更新问题
+          const links = Array.from(document.getElementsByTagName('link'))
+          
+          for (const linkTag of links) {
+            const href = linkTag.getAttribute('href')
+
+            if (href && href.includes('dfyun')) {
+              const newHref = href.replace('https://showdoc.cdn.dfyun.com.cn/', '/')
+              await loadStylesheet(newHref)
+              
+              // 检查 parentNode 是否存在
+              if (linkTag.parentNode) {
+                linkTag.parentNode.removeChild(linkTag)
+              }
+            }
+          }
+        }
+
+        const updateScriptTags = async () => {
+          // 转换为数组,避免 HTMLCollection live 更新问题
+          const scripts = Array.from(document.getElementsByTagName('script'))
+          
+          for (const scriptTag of scripts) {
+            const src = scriptTag.getAttribute('src')
+
+            if (src && src.includes('dfyun') && !src.includes('showdoc-server')) {
+              const newSrc = src.replace('https://showdoc.cdn.dfyun.com.cn/', '/')
+              await loadScript(newSrc)
+              
+              // 检查 parentNode 是否存在
+              if (scriptTag.parentNode) {
+                scriptTag.parentNode.removeChild(scriptTag)
+              }
+            }
+          }
+        }
+
+        const updateImgTags = () => {
+          const imgs = Array.from(document.getElementsByTagName('img'))
+          
+          for (const imgTag of imgs) {
+            let src = imgTag.getAttribute('src')
+
+            if (src && src.includes('dfyun')) {
+              src = src.replace('https://showdoc.cdn.dfyun.com.cn/', '/')
+              imgTag.setAttribute('src', src)
+            }
+          }
+        }
+
+        updateLinkTags()
+          .then(() => {
+            return updateScriptTags()
+          })
+          .then(() => {
+            console.log('[CDN] 已切换到本地资源')
+            updateImgTags()
+          })
+          .catch(error => {
+            console.error('[CDN] 切换失败:', error)
+          })
+      }
+    </script>
+
+
+    <!-- 防止被镜像站 -->
+    <script language="JavaScript">
+      var host = window.location.host
+      if (
+        host.indexOf('localhost') === -1 &&
+        host.indexOf('wu') === -1 &&
+        host.indexOf('showdoc') === -1 &&
+        host.indexOf('star7th.com') === -1 &&
+        host.indexOf('192.168.') === -1 &&
+        host.indexOf('gaoyixia.com') === -1 &&
+        host.indexOf('dongjingyu.cn') === -1 &&
+        host.indexOf('127.0.0.1') === -1
+      ) {
+        var href = window.location.href
+        var j = href.replace(new RegExp(host, 'g'), 'www.showdoc.com.cn')
+        window.location.href = j
+      }
+    </script>
+
+    <!-- 百度统计 -->
+    <script>
+      if (window.location.host == 'www.showdoc.com.cn') {
+        var _hmt = _hmt || []
+        ;(function() {
+          var hm = document.createElement('script')
+          hm.src = 'https://hm.baidu.com/hm.js?84e82fa31c8ca8671f6b6f972b7e54fb'
+          var s = document.getElementsByTagName('script')[0]
+          s.parentNode.insertBefore(hm, s)
+        })()
+      }
+    </script>
+
+    <!-- 主应用入口 -->
+    
+  </body>
+</html>

+ 512 - 0
src/api/collect/AssessmentContent.ts

@@ -0,0 +1,512 @@
+import { DataModel, transformArrayDataModel, transformDataModel, type KeyValue } from '@imengyu/js-request-transform';
+import { AppServerRequestModule } from '../RequestModules';
+import { transformSomeToArray } from '../Utils';
+
+/**
+ * 自查评估 / 传承协议相关接口(ShowDoc)
+ * @see https://www.showdoc.com.cn/minnanCE/11559060626966335 列表
+ * @see https://www.showdoc.com.cn/minnanCE/11559060626887140 传承协议编辑
+ * @see https://www.showdoc.com.cn/minnanCE/11559060626966336 评估表编辑/新增
+ * @see https://www.showdoc.com.cn/minnanCE/11559060626966337 详情
+ */
+
+/** 计分点项(getCheckItems) */
+export class CheckItemInfo extends DataModel<CheckItemInfo> {
+  constructor() {
+    super(CheckItemInfo, '自查计分项目');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number' },
+      pid: { clientSide: 'number', serverSide: 'number' },
+      level: { clientSide: 'number', serverSide: 'number' },
+      points: { clientSide: 'number', serverSide: 'number' },
+      isTitle: { clientSide: 'number', serverSide: 'number' },
+      haschild: { clientSide: 'number', serverSide: 'number' },
+      checkType: { clientSide: 'number', serverSide: 'number' },
+      weigh: { clientSide: 'number', serverSide: 'number' },
+    };
+  }
+
+  id = 0 as number;
+  pid = 0 as number;
+  name = '' as string;
+  level = 0 as number;
+  points = 0 as number;
+  isTitle = 0 as number;
+  spacer = '' as string;
+  haschild = 0 as number;
+  checkType = 0 as number;
+  weigh = 0 as number;
+}
+
+/** 自查评估表列表行 */
+export class SelfAssessmentListRow extends DataModel<SelfAssessmentListRow> {
+  constructor() {
+    super(SelfAssessmentListRow, '自查评估表列表项');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number' },
+      userId: { clientSide: 'number', serverSide: 'number' },
+      year: { clientSide: 'number', serverSide: 'number' },
+      weigh: { clientSide: 'number', serverSide: 'number' },
+      deductPoints: { clientSide: 'number', serverSide: 'number' },
+      points: { clientSide: 'number', serverSide: 'number' },
+      self: { clientSide: 'number', serverSide: 'number' },
+      ichUnit: { clientSide: 'number', serverSide: 'number' },
+      unitPoints: { clientSide: 'number', serverSide: 'number' },
+      county: { clientSide: 'number', serverSide: 'number' },
+      countyPoints: { clientSide: 'number', serverSide: 'number' },
+      district: { clientSide: 'number', serverSide: 'number' },
+      districtPoints: { clientSide: 'number', serverSide: 'number' },
+      province: { clientSide: 'number', serverSide: 'number' },
+      provincePoints: { clientSide: 'number', serverSide: 'number' },
+      level: { clientSide: 'number', serverSide: 'string' },
+    };
+    this._convertKeyType = (key) => {
+      if (key.endsWith('Text') || key.endsWith('_text')) {
+        return { clientSide: 'string', serverSide: 'undefined' };
+      }
+      return undefined;
+    };
+  }
+
+  id = 0 as number;
+  userId = 0 as number;
+  year = 0 as number;
+  inheritor = '' as string|null;
+  unit = '' as string|null;
+  ichName = '' as string|null;
+  mobile = '' as string|null;
+  idCard = '' as string|null;
+  level = null as number|string|null;
+  address = '' as string|null;
+  weigh = 0 as number;
+  deductPoints = 0 as number;
+  points = 0 as number;
+  self = null as number|null;
+  ichUnit = null as number|null;
+  unitPoints = 0 as number;
+  county = null as number|null;
+  countyPoints = 0 as number;
+  district = null as number|null;
+  districtPoints = 0 as number;
+  province = null as number|null;
+  provincePoints = 0 as number;
+  createtime = '' as string;
+  updatetime = '' as string;
+  selfText = '' as string;
+}
+
+/** 传承协议列表行 */
+export class AgreementListRow extends DataModel<AgreementListRow> {
+  constructor() {
+    super(AgreementListRow, '传承协议列表项');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number' },
+      userId: { clientSide: 'number', serverSide: 'number' },
+      year: { clientSide: 'number', serverSide: 'number' },
+      apprentice: { clientSide: 'number', serverSide: 'number' },
+      activity: { clientSide: 'number', serverSide: 'number' },
+      course: { clientSide: 'number', serverSide: 'number' },
+      level: { clientSide: 'number', serverSide: 'number' },
+    };
+  }
+
+  id = 0 as number;
+  userId = 0 as number;
+  level = null as number|null;
+  year = 0 as number;
+  partyA = '' as string|null;
+  partyB = '' as string|null;
+  apprentice = 0 as number;
+  activity = 0 as number;
+  course = 0 as number;
+  mobile = '' as string|null;
+  partyAMobile = '' as string|null;
+  idCard = '' as string|null;
+  health = '' as string|null;
+  ich = '' as string|null;
+  partyASign = '' as string|null;
+  partyBSign = '' as string|null;
+  createtime = '' as string;
+  updatetime = '' as string;
+  deletetime = '' as string|null;
+}
+
+/** 传承人基础信息(ich/check/basic) */
+export class InheritorCheckBasicInfo extends DataModel<InheritorCheckBasicInfo> {
+  constructor() {
+    super(InheritorCheckBasicInfo, '传承人自查基础信息');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      userId: { clientSide: 'number', serverSide: 'number' },
+      level: { clientSide: 'number', serverSide: 'number' },
+      batch: { clientSide: 'number', serverSide: 'number' },
+    };
+    this._convertKeyType = (key) => {
+      if (key.endsWith('Text') || key.endsWith('_text')) {
+        return { clientSide: 'string', serverSide: 'undefined' };
+      }
+      return undefined;
+    };
+  }
+
+  userId = 0 as number;
+  name = '' as string;
+  region = '' as string;
+  gender = '' as string;
+  education = '' as string|null;
+  ichName = '' as string;
+  unit = '' as string;
+  level = 0 as number;
+  batch = 0 as number;
+  mobile = '' as string;
+  genderText = '' as string;
+  educationText = '' as string;
+  levelText = '' as string;
+  regionText = '' as string;
+}
+
+/** 详情中已选计分项 */
+export class SelfAssessmentCheckItemAnswer extends DataModel<SelfAssessmentCheckItemAnswer> {
+  constructor() {
+    super(SelfAssessmentCheckItemAnswer, '自查计分选项');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number' },
+      itemId: { clientSide: 'number', serverSide: 'number' },
+      formId: { clientSide: 'number', serverSide: 'number' },
+      points: { clientSide: 'number', serverSide: 'number' },
+      count: { clientSide: 'number', serverSide: 'number' },
+      isTitle: { clientSide: 'number', serverSide: 'number' },
+    };
+  }
+
+  id = 0 as number;
+  itemId = 0 as number;
+  formId = 0 as number;
+  answerText = '' as string;
+  points = 0 as number;
+  count = 0 as number;
+  isTitle = 0 as number;
+}
+
+/** 自查评估表详情 */
+export class SelfAssessmentDetail extends DataModel<SelfAssessmentDetail> {
+  constructor() {
+    super(SelfAssessmentDetail, '自查评估表详情');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number' },
+      userId: { clientSide: 'number', serverSide: 'number' },
+      year: { clientSide: 'number', serverSide: 'number' },
+      weigh: { clientSide: 'number', serverSide: 'number' },
+      deductPoints: { clientSide: 'number', serverSide: 'number' },
+      points: { clientSide: 'number', serverSide: 'number' },
+      self: { clientSide: 'number', serverSide: 'number' },
+      ichUnit: { clientSide: 'number', serverSide: 'number' },
+      unitPoints: { clientSide: 'number', serverSide: 'number' },
+      county: { clientSide: 'number', serverSide: 'number' },
+      countyPoints: { clientSide: 'number', serverSide: 'number' },
+      district: { clientSide: 'number', serverSide: 'number' },
+      districtPoints: { clientSide: 'number', serverSide: 'number' },
+      province: { clientSide: 'number', serverSide: 'number' },
+      provincePoints: { clientSide: 'number', serverSide: 'number' },
+      level: { clientSide: 'number', serverSide: 'string' },
+    };
+    this._convertKeyType = (key) => {
+      if (key.endsWith('Text') || key.endsWith('_text')) {
+        return { clientSide: 'string', serverSide: 'undefined' };
+      }
+      return undefined;
+    };
+  }
+
+  id = 0 as number;
+  userId = 0 as number;
+  year = 0 as number;
+  inheritor = '' as string|null;
+  unit = '' as string|null;
+  ichName = '' as string|null;
+  mobile = '' as string|null;
+  idCard = '' as string|null;
+  level = null as number|string|null;
+  address = '' as string|null;
+  content = '' as string|null;
+  weigh = 0 as number;
+  deductContent = '' as string|null;
+  deductPoints = 0 as number;
+  points = 0 as number;
+  self = null as number|null;
+  ichUnit = null as number|null;
+  unitPoints = 0 as number;
+  county = null as number|null;
+  countyPoints = 0 as number;
+  district = null as number|null;
+  districtPoints = 0 as number;
+  province = null as number|null;
+  provincePoints = 0 as number;
+  createtime = '' as string;
+  updatetime = '' as string;
+  deletetime = '' as string|null;
+  selfText = '' as string;
+  /** 接口为按 key 分组的对象,已转为各值的 DataModel */
+  checkItems = {} as Record<string, SelfAssessmentCheckItemAnswer>;
+}
+
+/** 传承协议详情 */
+export class AgreementDetail extends DataModel<AgreementDetail> {
+  constructor() {
+    super(AgreementDetail, '传承协议详情');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number' },
+      userId: { clientSide: 'number', serverSide: 'number' },
+      year: { clientSide: 'number', serverSide: 'number' },
+      apprentice: { clientSide: 'number', serverSide: 'number' },
+      activity: { clientSide: 'number', serverSide: 'number' },
+      course: { clientSide: 'number', serverSide: 'number' },
+      level: { clientSide: 'number', serverSide: 'number' },
+    };
+  }
+
+  id = 0 as number;
+  userId = 0 as number;
+  level = null as number|null;
+  year = 0 as number;
+  partyA = '' as string|null;
+  partyB = '' as string|null;
+  apprentice = 0 as number;
+  activity = 0 as number;
+  course = 0 as number;
+  mobile = '' as string|null;
+  partyAMobile = '' as string|null;
+  idCard = '' as string|null;
+  health = '' as string|null;
+  ich = '' as string|null;
+  partyASign = '' as string|null;
+  partyBSign = '' as string|null;
+  createtime = '' as string;
+  updatetime = '' as string;
+  deletetime = '' as string|null;
+}
+
+/** 提交自查评估表(ich/check/save) */
+export class SelfAssessmentSubmit extends DataModel<SelfAssessmentSubmit> {
+  constructor() {
+    super(SelfAssessmentSubmit, '自查评估表提交');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number' },
+      userId: { clientSide: 'number', serverSide: 'number' },
+      year: { clientSide: 'number', serverSide: 'number' },
+      level: { clientSide: 'number', serverSide: 'number' },
+      deductPoints: { clientSide: 'number', serverSide: 'number' },
+      points: { clientSide: 'number', serverSide: 'number' },
+      self: { clientSide: 'number', serverSide: 'number' },
+      checkItems: {
+        clientSide: 'array',
+        clientSideChildDataModel: SelfAssessmentSubmitCheckItem,
+        serverSide: 'array',
+      },
+    };
+  }
+
+  id = null as number|null;
+  userId = 0 as number;
+  year = 0 as number;
+  inheritor = '' as string;
+  unit = '' as string;
+  ichName = '' as string;
+  mobile = '' as string;
+  idCard = '' as string;
+  level = 0 as number;
+  address = '' as string;
+  content = '' as string;
+  deductContent = '' as string;
+  deductPoints = 0 as number;
+  points = 0 as number;
+  self = 0 as number;
+  checkItems = [] as SelfAssessmentSubmitCheckItem[];
+}
+
+export class SelfAssessmentSubmitCheckItem extends DataModel<SelfAssessmentSubmitCheckItem> {
+  constructor() {
+    super(SelfAssessmentSubmitCheckItem, '自查计分选项提交');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number' },
+      count: { clientSide: 'number', serverSide: 'number' },
+    };
+  }
+
+  id = 0 as number;
+  count = 1 as number;
+}
+
+/** 提交传承协议(文档路径名为 saveAgreememt) */
+export class AgreementSubmit extends DataModel<AgreementSubmit> {
+  constructor() {
+    super(AgreementSubmit, '传承协议提交');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number' },
+      year: { clientSide: 'number', serverSide: 'number' },
+      level: { clientSide: 'number', serverSide: 'number' },
+      apprentice: { clientSide: 'number', serverSide: 'number' },
+      activity: { clientSide: 'number', serverSide: 'number' },
+      course: { clientSide: 'number', serverSide: 'number' },
+    };
+  }
+
+  id = null as number|null;
+  year = 0 as number;
+  level = 0 as number;
+  partyA = '' as string;
+  partyB = '' as string;
+  apprentice = 0 as number;
+  activity = 0 as number;
+  course = 0 as number;
+  mobile = '' as string;
+  partyAMobile = '' as string;
+  idCard = '' as string;
+  health = '' as string;
+  ich = '' as string;
+  partyASign = '' as string;
+  partyBSign = '' as string;
+}
+
+export type IchCheckPaginated<T> = {
+  total: number;
+  perPage: number;
+  currentPage: number;
+  lastPage: number;
+  data: T[];
+};
+
+function normalizePaginated<T extends DataModel>(rowClass: new () => T, raw: KeyValue): IchCheckPaginated<T> {
+  const rows = Array.isArray(raw.data) ? (raw.data as KeyValue[]) : [];
+  return {
+    total: Number(raw.total ?? 0),
+    perPage: Number(raw.per_page ?? 0),
+    currentPage: Number(raw.current_page ?? 0),
+    lastPage: Number(raw.last_page ?? 0),
+    data: transformArrayDataModel(rowClass, rows, 'data'),
+  };
+}
+
+export class AssessmentContentApi extends AppServerRequestModule<DataModel> {
+
+  constructor() {
+    super();
+  }
+
+  /**
+   * 计分点列表
+   * @param level 23=国家级,24=省级,25=市级;0=通用
+   */
+  async getCheckItems(level: number) {
+    const res = await this.post('/ich/check/getCheckItems', '自查计分项目', { level });
+    const list = transformSomeToArray(res.data) as KeyValue[];
+    return transformArrayDataModel(CheckItemInfo, list, 'data');
+  }
+
+  /**
+   * 自查评估表分页列表
+   * @see https://www.showdoc.com.cn/minnanCE/11559060626966335
+   */
+  async getSelfAssessmentList(data: {
+    userId?: number;
+    level?: number;
+    year?: number;
+    keywords?: string;
+    page?: number;
+    pageSize?: number;
+  }) {
+    const res = await this.post('/ich/check/getList', '评估表列表', {
+      user_id: data.userId,
+      level: data.level,
+      year: data.year,
+      keywords: data.keywords,
+      page: data.page,
+      pageSize: data.pageSize,
+    });
+    return normalizePaginated(SelfAssessmentListRow, (res.data ?? {}) as KeyValue);
+  }
+
+  /**
+   * 传承协议分页列表
+   */
+  async getAgreementList(data: {
+    userId?: number;
+    level?: number;
+    year?: number;
+    keywords?: string;
+    page?: number;
+    pageSize?: number;
+  }) {
+    const res = await this.post('/ich/check/getAgreementList', '传承协议列表', {
+      user_id: data.userId,
+      level: data.level,
+      year: data.year,
+      keywords: data.keywords,
+      page: data.page,
+      pageSize: data.pageSize,
+    });
+    return normalizePaginated(AgreementListRow, (res.data ?? {}) as KeyValue);
+  }
+
+  /**
+   * 保存传承协议(ShowDoc 文档 URL 为 saveAgreememt)
+   */
+  async saveAgreement(dataModel: AgreementSubmit) {
+    return this.post('/ich/check/saveAgreememt', '传承协议保存', dataModel.toServerSide());
+  }
+
+  /**
+   * 保存自查评估表
+   */
+  async saveSelfAssessment(dataModel: SelfAssessmentSubmit) {
+    return this.post('/ich/check/save', '自查评估表保存', dataModel.toServerSide());
+  }
+
+  /**
+   * 传承人基础信息(默认当前用户;管理员可传 userId)
+   */
+  async getInheritorBasic(userId?: number) {
+    const res = await this.post('/ich/check/basic', '传承人自查基础信息', {
+      user_id: userId,
+    });
+    return transformDataModel(InheritorCheckBasicInfo, (res.data ?? {}) as KeyValue);
+  }
+
+  /**
+   * 自查评估表详情
+   */
+  async getSelfAssessmentDetail(id: number) {
+    const res = await this.post('/ich/check/detail', '评估表详情', { id });
+    const body = (res.data ?? {}) as KeyValue;
+    const checkItemsRaw = body.check_items as Record<string, KeyValue> | undefined;
+    const { check_items: _ci, ...rest } = body;
+    const m = transformDataModel(SelfAssessmentDetail, rest as KeyValue);
+    const out: Record<string, SelfAssessmentCheckItemAnswer> = {};
+    if (checkItemsRaw && typeof checkItemsRaw === 'object') {
+      for (const k of Object.keys(checkItemsRaw)) {
+        out[k] = transformDataModel(SelfAssessmentCheckItemAnswer, checkItemsRaw[k]!);
+      }
+    }
+    m.checkItems = out;
+    return m;
+  }
+
+  /**
+   * 传承协议详情
+   */
+  async getAgreementDetail(id: number) {
+    const res = await this.post('/ich/check/agreementDetail', '传承协议详情', { id });
+    return transformDataModel(AgreementDetail, (res.data ?? {}) as KeyValue);
+  }
+}
+
+export default new AssessmentContentApi();

+ 21 - 0
src/pages.json

@@ -282,6 +282,27 @@
             "navigationBarTitleText": "作品",
             "enablePullDownRefresh": false
           }
+        },
+        {
+          "path": "assessment/index",
+          "style": {
+            "navigationBarTitleText": "自查评估",
+            "enablePullDownRefresh": false
+          }
+        },
+        {
+          "path": "assessment/argeement-sign",
+          "style": {
+            "navigationBarTitleText": "传承协议签名",
+            "enablePullDownRefresh": false
+          }
+        },
+        {
+          "path": "assessment/evaluation-form",
+          "style": {
+            "navigationBarTitleText": "自查评估表",
+            "enablePullDownRefresh": false
+          }
         }
       ]
     }

+ 6 - 0
src/pages/collect/assessment/argeement-sign.vue

@@ -0,0 +1,6 @@
+<template>
+</template>
+
+<script setup lang="ts">
+import FlexCol from '@/components/layout/FlexCol.vue';
+</script>

+ 6 - 0
src/pages/collect/assessment/evaluation-form.vue

@@ -0,0 +1,6 @@
+<template>
+</template>
+
+<script setup lang="ts">
+import FlexCol from '@/components/layout/FlexCol.vue';
+</script>

+ 15 - 0
src/pages/collect/assessment/index.vue

@@ -0,0 +1,15 @@
+<template>
+  <FlexCol padding="space.lg">
+    <CellGroup round> 
+      <Cell icon="https://mncdn.wenlvti.net/uploads/20250313/9fb29e8bdb66490034145c90f892773a.png" title="传承协议签名" showArrow touchable @click="navTo('argeement-sign')" />
+      <Cell icon="https://mncdn.wenlvti.net/uploads/20250313/66d4665b1da5075e60148312469b2630.png" title="自查评估表" showArrow touchable @click="navTo('evaluation-form')" />
+    </CellGroup>
+  </FlexCol>
+</template>
+
+<script setup lang="ts">
+import FlexCol from '@/components/layout/FlexCol.vue';
+import CellGroup from '@/components/basic/CellGroup.vue';
+import Cell from '@/components/basic/Cell.vue';
+import { navTo } from '@/components/utils/PageAction';
+</script>

+ 16 - 1
src/pages/collect/inheritor.vue

@@ -40,6 +40,10 @@
         <Cell icon="https://mncdn.wenlvti.net/uploads/20250313/d2e9010323d098aa51e268fc32f14d3d.png" title="传习所" showArrow touchable @click="navTo('forms/seminar')" />
         <Cell icon="https://mncdn.wenlvti.net/uploads/20250313/66d4665b1da5075e60148312469b2630.png" title="作品" showArrow touchable @click="navTo('works')" />
       </CellGroup>
+      <CellGroup round>
+        <Cell icon="https://mncdn.wenlvti.net/uploads/20250313/cbc47d0b9cad7891e6154359952858c6.png" title="返回闽南文化" showArrow touchable @click="back()" />f
+        <Cell icon="https://mncdn.wenlvti.net/uploads/20250313/cbc47d0b9cad7891e6154359952858c6.png" title="退出登录" showArrow touchable @click="onLogout" />
+      </CellGroup>
     </FlexCol>
     <XBarSpace />
   </FlexCol>
@@ -58,8 +62,19 @@ import StatusBarSpace from '@/components/layout/space/StatusBarSpace.vue';
 import NavBar from '@/components/nav/NavBar.vue';
 import Avatar from '@/components/display/Avatar.vue';
 import Button from '@/components/basic/Button.vue';
-import { navTo } from '@/components/utils/PageAction';
+import { back, navTo } from '@/components/utils/PageAction';
+import { confirm } from '@/components/utils/DialogAction';
 
 const authStore = useAuthStore();
 
+function onLogout() {
+  confirm({
+    content: '您确定要退出登录吗?',
+  }).then((res) => {
+    if (res) {
+      authStore.logout();
+      back();
+    }
+  });
+}
 </script>