yuanmingze 2 місяців тому
батько
коміт
c92227117d

+ 84 - 79
src/pages-mine/agreement/index.vue

@@ -36,6 +36,11 @@ import type { agreementInfosItem } from '@/services/modules/mine/type'
 
 import { useUserStore } from '@/stores/modules/user'
 
+interface StaticAgreementItem {
+  name: string
+  path: string
+}
+
 const AGREEMENT_PAGE_BASE = '/pages-mine/agreement'
 const CERT_SUCCESS_STATUS = 'CERT'
 
@@ -45,11 +50,32 @@ const currentUserInfo = computed(() => userStore.currentUserInfo)
 
 const certSuccessList = ref<agreementInfosItem[]>([])
 
+const showToast = (title: string): void => {
+  uni.showToast({
+    title,
+    icon: 'none',
+  })
+}
+
+const hasSignedAgreement = (agreementCode: string): boolean => {
+  const signedAgreement = currentUserInfo.value?.signedAgreement
+
+  if (Array.isArray(signedAgreement)) {
+    return signedAgreement.includes(agreementCode)
+  }
+
+  if (typeof signedAgreement === 'string') {
+    return signedAgreement.includes(agreementCode)
+  }
+
+  return false
+}
+
 const showHonestAgreement = computed(() => hasSignedAgreement('HONEST_AGREEMENT'))
 const showHonestAgreementV2 = computed(() => hasSignedAgreement('HONEST_AGREEMENT_V2'))
 
-const staticAgreementList = computed(() => {
-  const list = [
+const staticAgreementList = computed<StaticAgreementItem[]>(() => {
+  const list: StaticAgreementItem[] = [
     {
       name: '要易云平台用户协议',
       path: 'platform-user-agreement',
@@ -81,11 +107,39 @@ const staticAgreementList = computed(() => {
   return list
 })
 
-onLoad(() => {
-  void getCertSuccessList()
-})
+const getCertifiedSubjectLocations = (): string[] => {
+  const certList = currentUserInfo.value?.certList ?? []
+
+  const subjectLocations = certList
+    .filter((item) => item?.commonCertStatus === CERT_SUCCESS_STATUS)
+    .map((item) => item?.subjectLocation?.trim())
+    .filter((subjectLocation): subjectLocation is string => Boolean(subjectLocation))
+
+  return Array.from(new Set(subjectLocations))
+}
+
+const normalizeAgreementInfos = (list: agreementInfosItem[] = []): agreementInfosItem[] => {
+  return list
+    .filter((item) => Boolean(item?.templateUrl?.trim()))
+    .map((item) => ({
+      name: item.name?.trim() || '协议',
+      templateUrl: item.templateUrl.trim(),
+    }))
+}
+
+const dedupeAgreementInfos = (list: agreementInfosItem[]): agreementInfosItem[] => {
+  const map = new Map<string, agreementInfosItem>()
+
+  list.forEach((item) => {
+    if (!map.has(item.templateUrl)) {
+      map.set(item.templateUrl, item)
+    }
+  })
+
+  return Array.from(map.values())
+}
 
-async function getCertSuccessList(): Promise<void> {
+const getCertSuccessList = async (): Promise<void> => {
   const subjectLocations = getCertifiedSubjectLocations()
 
   if (!subjectLocations.length) {
@@ -127,69 +181,13 @@ async function getCertSuccessList(): Promise<void> {
   }
 }
 
-function getCertifiedSubjectLocations(): string[] {
-  const certList = currentUserInfo.value?.certList ?? []
-
-  const subjectLocations = certList
-    .filter((item) => item?.commonCertStatus === CERT_SUCCESS_STATUS)
-    .map((item) => item?.subjectLocation?.trim())
-    .filter((subjectLocation): subjectLocation is string => Boolean(subjectLocation))
-
-  return Array.from(new Set(subjectLocations))
-}
-
-function normalizeAgreementInfos(list: agreementInfosItem[] = []): agreementInfosItem[] {
-  return list
-    .filter((item) => Boolean(item?.templateUrl?.trim()))
-    .map((item) => ({
-      name: item.name?.trim() || '协议',
-      templateUrl: item.templateUrl.trim(),
-    }))
-}
-
-function dedupeAgreementInfos(list: agreementInfosItem[]): agreementInfosItem[] {
-  const map = new Map<string, agreementInfosItem>()
-
-  list.forEach((item) => {
-    if (!map.has(item.templateUrl)) {
-      map.set(item.templateUrl, item)
-    }
-  })
-
-  return Array.from(map.values())
-}
-
-function navigateToAgreementPage(path: string): void {
+const navigateToAgreementPage = (path: string): void => {
   uni.navigateTo({
     url: `${AGREEMENT_PAGE_BASE}/${path}`,
   })
 }
 
-async function openPdfFile(item: agreementInfosItem): Promise<void> {
-  const fileUrl = item.templateUrl?.trim()
-
-  if (!fileUrl) {
-    showToast('文件地址不存在')
-    return
-  }
-
-  uni.showLoading({
-    title: '文件加载中',
-    mask: true,
-  })
-
-  try {
-    const filePath = await downloadFile(fileUrl)
-    await openDocument(filePath)
-  } catch (error) {
-    console.log('openPdfFile error', error)
-    showToast('文件打开失败')
-  } finally {
-    uni.hideLoading()
-  }
-}
-
-function downloadFile(url: string): Promise<string> {
+const downloadFile = (url: string): Promise<string> => {
   return new Promise((resolve, reject) => {
     uni.downloadFile({
       url,
@@ -206,7 +204,7 @@ function downloadFile(url: string): Promise<string> {
   })
 }
 
-function openDocument(filePath: string): Promise<void> {
+const openDocument = (filePath: string): Promise<void> => {
   return new Promise((resolve, reject) => {
     uni.openDocument({
       filePath,
@@ -220,26 +218,33 @@ function openDocument(filePath: string): Promise<void> {
   })
 }
 
-function hasSignedAgreement(agreementCode: string): boolean {
-  const signedAgreement = currentUserInfo.value?.signedAgreement
+const openPdfFile = async (item: agreementInfosItem): Promise<void> => {
+  const fileUrl = item.templateUrl?.trim()
 
-  if (Array.isArray(signedAgreement)) {
-    return signedAgreement.includes(agreementCode)
+  if (!fileUrl) {
+    showToast('文件地址不存在')
+    return
   }
 
-  if (typeof signedAgreement === 'string') {
-    return signedAgreement.includes(agreementCode)
-  }
+  uni.showLoading({
+    title: '文件加载中',
+    mask: true,
+  })
 
-  return false
+  try {
+    const filePath = await downloadFile(fileUrl)
+    await openDocument(filePath)
+  } catch (error) {
+    console.log('openPdfFile error', error)
+    showToast('文件打开失败')
+  } finally {
+    uni.hideLoading()
+  }
 }
 
-function showToast(title: string): void {
-  uni.showToast({
-    title,
-    icon: 'none',
-  })
-}
+onLoad(() => {
+  void getCertSuccessList()
+})
 </script>
 
 <style lang="scss" scoped>

+ 428 - 0
src/pages-mine/compliance-education/detail.vue

@@ -0,0 +1,428 @@
+<!--
+ * @desc 合规教育-查看每一项
+ * @author linyuanjie
+ * @date 2023/9/21
+-->
+
+<template>
+  <view class="compliance-detail-page">
+    <view class="detail-header">
+      <view class="header-label">合规教育文件</view>
+      <view class="header-title">
+        {{ fileName || '文件详情' }}
+      </view>
+      <view class="header-desc"> 点击下方文件可在线查看 </view>
+    </view>
+
+    <view v-if="loading" class="state-box">
+      <view class="state-text">加载中...</view>
+    </view>
+
+    <view v-else-if="!rulesItem" class="state-box">
+      <view class="empty-icon">📄</view>
+      <view class="state-text">未找到对应文件</view>
+    </view>
+
+    <view v-else-if="fileList.length === 0" class="state-box">
+      <view class="empty-icon">📁</view>
+      <view class="state-text">暂无可查看文件</view>
+    </view>
+
+    <view v-else class="file-section">
+      <view class="section-title">
+        <view class="title-main">文件列表</view>
+        <view class="title-count">共 {{ fileList.length }} 个</view>
+      </view>
+
+      <view
+        v-for="item in fileList"
+        :key="item.url"
+        class="file-card"
+        :class="{ 'file-card--unsupported': !isSupportedFile(item.url) }"
+        @click="handleReadFile(item)"
+      >
+        <view class="file-icon">
+          {{ getFileBadgeLabel(item.url) }}
+        </view>
+
+        <view class="file-content">
+          <view class="file-name">
+            {{ item.fileName }}
+          </view>
+          <view class="file-desc">
+            {{ getFileDesc(item.url) }}
+          </view>
+        </view>
+
+        <view class="file-arrow">›</view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup lang="ts">
+import { computed, ref } from 'vue'
+
+import { onLoad } from '@dcloudio/uni-app'
+
+import { getDeptRegulationsListApi } from '@/services/modules/mine/common'
+import type {
+  ComplianceEducationFile,
+  DeptRegulationsItem,
+} from '@/services/modules/mine/common/type'
+
+type PreviewType = 'document' | 'image'
+
+const DOCUMENT_EXTENSIONS = ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf'] as const
+const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'] as const
+
+const BASE_URL = import.meta.env.VITE_BASE_API as string
+
+const loading = ref(false)
+const fileName = ref('')
+const rulesItem = ref<DeptRegulationsItem | null>(null)
+
+const fileList = computed<ComplianceEducationFile[]>(() => {
+  return rulesItem.value?.fileUrl ?? []
+})
+
+onLoad((options) => {
+  const routeFileName = decodeRouteParam(options?.fileName)
+
+  fileName.value = routeFileName
+
+  uni.setNavigationBarTitle({
+    title: routeFileName || '文件详情',
+  })
+
+  void getDeptRulesItem()
+})
+
+const getDeptRulesItem = async (): Promise<void> => {
+  if (!fileName.value) {
+    uni.showToast({
+      title: '缺少文件名称',
+      icon: 'none',
+    })
+    return
+  }
+
+  loading.value = true
+
+  try {
+    const res = await getDeptRegulationsListApi()
+
+    if (res.code !== 0) {
+      rulesItem.value = null
+      return
+    }
+
+    const list = Array.isArray(res.data) ? res.data : []
+
+    rulesItem.value =
+      list.find((item: DeptRegulationsItem) => {
+        return item.fileName === fileName.value
+      }) ?? null
+  } catch (error) {
+    rulesItem.value = null
+
+    console.error('[compliance-education-detail] getDeptRulesItem failed:', error)
+
+    uni.showToast({
+      title: '加载失败,请稍后重试',
+      icon: 'none',
+    })
+  } finally {
+    loading.value = false
+  }
+}
+
+const handleReadFile = async (file: ComplianceEducationFile): Promise<void> => {
+  const fileType = getFileExtension(file.url)
+  const previewType = getPreviewType(fileType)
+
+  if (!previewType) {
+    uni.showToast({
+      title: `不支持${fileType || '该'}文件查看,请联系管理员`,
+      icon: 'none',
+    })
+    return
+  }
+
+  const fileUrl = resolveFileUrl(file.url)
+
+  try {
+    if (previewType === 'document') {
+      await openDocument(fileUrl, fileType)
+      return
+    }
+
+    previewImage(fileUrl)
+  } catch (error) {
+    console.error('[compliance-education-detail] open file failed:', error)
+
+    uni.showToast({
+      title: '文件打开失败,请稍后重试',
+      icon: 'none',
+    })
+  }
+}
+
+const openDocument = (url: string, fileType: string): Promise<void> => {
+  return new Promise((resolve, reject) => {
+    uni.downloadFile({
+      url,
+      success: (downloadRes) => {
+        if (downloadRes.statusCode !== 200) {
+          reject(downloadRes)
+          return
+        }
+
+        uni.openDocument({
+          filePath: downloadRes.tempFilePath,
+          fileType,
+          success: () => {
+            resolve()
+          },
+          fail: reject,
+        })
+      },
+      fail: reject,
+    })
+  })
+}
+
+const previewImage = (url: string): void => {
+  uni.previewImage({
+    urls: [url],
+    current: url,
+  })
+}
+
+const getPreviewType = (fileType: string): PreviewType | null => {
+  if ((DOCUMENT_EXTENSIONS as readonly string[]).includes(fileType)) {
+    return 'document'
+  }
+
+  if ((IMAGE_EXTENSIONS as readonly string[]).includes(fileType)) {
+    return 'image'
+  }
+
+  return null
+}
+
+const isSupportedFile = (url: string): boolean => {
+  return getPreviewType(getFileExtension(url)) !== null
+}
+
+const getFileExtension = (url: string): string => {
+  const pureUrl = url.split(/[?#]/)[0] ?? ''
+  const lastDotIndex = pureUrl.lastIndexOf('.')
+
+  if (lastDotIndex === -1) {
+    return ''
+  }
+
+  return pureUrl.slice(lastDotIndex + 1).toLowerCase()
+}
+
+const resolveFileUrl = (url: string): string => {
+  if (/^https?:\/\//i.test(url)) {
+    return url
+  }
+
+  const normalizedBaseUrl = BASE_URL.replace(/\/$/, '')
+  const normalizedPath = url.startsWith('/') ? url : `/${url}`
+
+  return `${normalizedBaseUrl}${normalizedPath}`
+}
+
+const getFileBadgeLabel = (url: string): string => {
+  const fileType = getFileExtension(url)
+
+  return fileType ? fileType.toUpperCase() : 'FILE'
+}
+
+const getFileDesc = (url: string): string => {
+  const fileType = getFileExtension(url)
+  const previewType = getPreviewType(fileType)
+
+  if (previewType === 'document') {
+    return '点击后下载并打开文档'
+  }
+
+  if (previewType === 'image') {
+    return '点击后预览图片'
+  }
+
+  return '暂不支持在线查看'
+}
+
+const decodeRouteParam = (value: unknown): string => {
+  if (typeof value !== 'string') {
+    return ''
+  }
+
+  try {
+    return decodeURIComponent(value)
+  } catch {
+    return value
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.compliance-detail-page {
+  min-height: 100vh;
+  padding: 32rpx;
+  box-sizing: border-box;
+  background: linear-gradient(180deg, #eef5ff 0%, #f7f8fa 360rpx), #f7f8fa;
+}
+
+.detail-header {
+  padding: 34rpx 32rpx 38rpx;
+  border-radius: 28rpx;
+  background: linear-gradient(135deg, rgba(39, 111, 245, 0.96), rgba(72, 150, 255, 0.92)), #276ff5;
+  box-shadow: 0 20rpx 48rpx rgba(39, 111, 245, 0.22);
+}
+
+.header-label {
+  display: inline-flex;
+  align-items: center;
+  height: 40rpx;
+  padding: 0 18rpx;
+  border-radius: 999rpx;
+  font-size: 22rpx;
+  color: rgba(255, 255, 255, 0.92);
+  background: rgba(255, 255, 255, 0.16);
+}
+
+.header-title {
+  margin-top: 22rpx;
+  font-size: 38rpx;
+  font-weight: 700;
+  line-height: 1.45;
+  color: #ffffff;
+  word-break: break-all;
+}
+
+.header-desc {
+  margin-top: 12rpx;
+  font-size: 26rpx;
+  line-height: 1.5;
+  color: rgba(255, 255, 255, 0.82);
+}
+
+.file-section {
+  margin-top: 30rpx;
+}
+
+.section-title {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 18rpx;
+}
+
+.title-main {
+  font-size: 32rpx;
+  font-weight: 700;
+  color: #1f2937;
+}
+
+.title-count {
+  font-size: 24rpx;
+  color: #8a94a6;
+}
+
+.file-card {
+  display: flex;
+  align-items: center;
+  min-height: 132rpx;
+  padding: 26rpx 28rpx;
+  margin-bottom: 20rpx;
+  box-sizing: border-box;
+  border-radius: 24rpx;
+  background: #ffffff;
+  box-shadow: 0 12rpx 36rpx rgba(20, 35, 60, 0.06);
+}
+
+.file-card:active {
+  transform: scale(0.985);
+  opacity: 0.92;
+}
+
+.file-card--unsupported {
+  opacity: 0.72;
+}
+
+.file-icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 76rpx;
+  height: 76rpx;
+  flex-shrink: 0;
+  border-radius: 22rpx;
+  font-size: 22rpx;
+  font-weight: 700;
+  color: #276ff5;
+  background: #edf4ff;
+}
+
+.file-content {
+  min-width: 0;
+  flex: 1;
+  margin-left: 22rpx;
+}
+
+.file-name {
+  font-size: 30rpx;
+  font-weight: 600;
+  line-height: 1.45;
+  color: #1f2937;
+  overflow: hidden;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+  word-break: break-all;
+}
+
+.file-desc {
+  margin-top: 8rpx;
+  font-size: 24rpx;
+  line-height: 1.4;
+  color: #8a94a6;
+}
+
+.file-arrow {
+  flex-shrink: 0;
+  margin-left: 20rpx;
+  font-size: 48rpx;
+  line-height: 1;
+  color: #b8c0cc;
+}
+
+.state-box {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 420rpx;
+  margin-top: 28rpx;
+  border-radius: 24rpx;
+  background: #ffffff;
+  box-shadow: 0 12rpx 36rpx rgba(20, 35, 60, 0.05);
+}
+
+.empty-icon {
+  margin-bottom: 20rpx;
+  font-size: 64rpx;
+  line-height: 1;
+}
+
+.state-text {
+  font-size: 28rpx;
+  color: #8a94a6;
+}
+</style>

+ 229 - 0
src/pages-mine/compliance-education/index.vue

@@ -0,0 +1,229 @@
+<!--
+ * @desc 合规教育
+ * @author linyuanjie
+ * @date 2023/9/22
+-->
+
+<template>
+  <view class="compliance-page">
+    <view class="page-header">
+      <view class="header-title">合规教育</view>
+      <view class="header-desc">请认真阅读并学习以下合规制度文件</view>
+    </view>
+
+    <view v-if="loading" class="state-box">
+      <view class="state-text">加载中...</view>
+    </view>
+
+    <view v-else-if="deptRulesList.length === 0" class="state-box">
+      <view class="empty-icon">📄</view>
+      <view class="state-text">暂无合规教育材料</view>
+    </view>
+
+    <view v-else class="rules-list">
+      <view
+        v-for="(item, index) in deptRulesList"
+        :key="item.id"
+        class="rules-card"
+        @click="handleReadRule(item)"
+      >
+        <view class="card-left">
+          <view class="index-badge">
+            {{ formatIndex(index) }}
+          </view>
+
+          <view class="card-content">
+            <view class="rule-name">
+              {{ item.fileName }}
+            </view>
+            <view class="rule-desc"> 点击查看文件详情 </view>
+          </view>
+        </view>
+
+        <view class="card-arrow">›</view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue'
+
+import { onLoad } from '@dcloudio/uni-app'
+
+import { getDeptRegulationsListApi } from '@/services/modules/mine/common'
+import type { DeptRegulationsItem } from '@/services/modules/mine/common/type'
+
+const loading = ref(false)
+const deptRulesList = ref<DeptRegulationsItem[]>([])
+
+const getDeptRulesList = async (): Promise<void> => {
+  loading.value = true
+
+  try {
+    const res = await getDeptRegulationsListApi()
+
+    if (res.code !== 0) {
+      deptRulesList.value = []
+      return
+    }
+
+    const list = Array.isArray(res.data) ? res.data : []
+
+    deptRulesList.value = list.filter((item: DeptRegulationsItem) => {
+      return item.enableFlag === '0' && item.fileUrl.length > 0
+    })
+  } catch (error) {
+    deptRulesList.value = []
+
+    console.error('[compliance-education] getDeptRulesList failed:', error)
+
+    uni.showToast({
+      title: '加载失败,请稍后重试',
+      icon: 'none',
+    })
+  } finally {
+    loading.value = false
+  }
+}
+
+const handleReadRule = (item: DeptRegulationsItem): void => {
+  uni.navigateTo({
+    url: `/pages-mine/compliance-education/detail?fileName=${encodeURIComponent(item.fileName)}`,
+  })
+}
+
+const formatIndex = (index: number): string => {
+  return String(index + 1).padStart(2, '0')
+}
+
+onLoad(() => {
+  void getDeptRulesList()
+})
+</script>
+
+<style lang="scss" scoped>
+.compliance-page {
+  min-height: 100vh;
+  padding: 32rpx;
+  box-sizing: border-box;
+  background: linear-gradient(180deg, #eef5ff 0%, #f7f8fa 360rpx), #f7f8fa;
+}
+
+.page-header {
+  padding: 36rpx 32rpx 44rpx;
+  border-radius: 28rpx;
+  background: linear-gradient(135deg, rgba(39, 111, 245, 0.95), rgba(71, 148, 255, 0.92)), #276ff5;
+  box-shadow: 0 20rpx 48rpx rgba(39, 111, 245, 0.22);
+}
+
+.header-title {
+  font-size: 44rpx;
+  font-weight: 700;
+  line-height: 1.35;
+  color: #ffffff;
+}
+
+.header-desc {
+  margin-top: 12rpx;
+  font-size: 26rpx;
+  line-height: 1.5;
+  color: rgba(255, 255, 255, 0.82);
+}
+
+.rules-list {
+  margin-top: 28rpx;
+}
+
+.rules-card {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  min-height: 132rpx;
+  padding: 28rpx;
+  margin-bottom: 20rpx;
+  box-sizing: border-box;
+  border-radius: 24rpx;
+  background: #ffffff;
+  box-shadow: 0 12rpx 36rpx rgba(20, 35, 60, 0.06);
+}
+
+.rules-card:active {
+  transform: scale(0.985);
+  opacity: 0.92;
+}
+
+.card-left {
+  display: flex;
+  align-items: center;
+  min-width: 0;
+}
+
+.index-badge {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 64rpx;
+  height: 64rpx;
+  flex-shrink: 0;
+  border-radius: 20rpx;
+  font-size: 24rpx;
+  font-weight: 700;
+  color: #276ff5;
+  background: #edf4ff;
+}
+
+.card-content {
+  min-width: 0;
+  margin-left: 22rpx;
+}
+
+.rule-name {
+  max-width: 520rpx;
+  overflow: hidden;
+  font-size: 30rpx;
+  font-weight: 600;
+  line-height: 1.45;
+  color: #1f2937;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.rule-desc {
+  margin-top: 8rpx;
+  font-size: 24rpx;
+  line-height: 1.4;
+  color: #8a94a6;
+}
+
+.card-arrow {
+  flex-shrink: 0;
+  margin-left: 20rpx;
+  font-size: 48rpx;
+  line-height: 1;
+  color: #b8c0cc;
+}
+
+.state-box {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 420rpx;
+  margin-top: 28rpx;
+  border-radius: 24rpx;
+  background: #ffffff;
+  box-shadow: 0 12rpx 36rpx rgba(20, 35, 60, 0.05);
+}
+
+.empty-icon {
+  margin-bottom: 20rpx;
+  font-size: 64rpx;
+  line-height: 1;
+}
+
+.state-text {
+  font-size: 28rpx;
+  color: #8a94a6;
+}
+</style>

+ 1 - 1
src/pages-mine/feedback/index.vue

@@ -34,7 +34,7 @@
 <script setup lang="ts">
 import { computed, ref } from 'vue'
 
-import { saveFeedbackApi } from '@/services/modules/common'
+import { saveFeedbackApi } from '@/services/modules/mine/common/index.ts'
 
 import { useUserStore } from '@/stores/modules/user'
 

+ 14 - 0
src/pages.json

@@ -77,6 +77,20 @@
             "navigationStyle": "default"
           }
         },
+        {
+          "path": "compliance-education/index",
+          "style": {
+            "navigationBarTitleText": "合规教育",
+            "navigationStyle": "default"
+          }
+        },
+        {
+          "path": "compliance-education/detail",
+          "style": {
+            "navigationBarTitleText": "合规教育",
+            "navigationStyle": "default"
+          }
+        },
         {
           "path": "agreement/index",
           "style": {

+ 1 - 1
src/pages/mine/mine.config.ts

@@ -44,7 +44,7 @@ export const functionList = [
     key: 'education',
     txt: '合规教育',
     icon: 'https://yy-cloud-oss.oss-cn-beijing.aliyuncs.com/img/hgjy1.png',
-    action: 'detail',
+    path: '/pages-mine/compliance-education/index',
   },
   {
     key: 'exam',

+ 1 - 8
src/services/modules/common/index.ts

@@ -1,5 +1,5 @@
 import http from '../../index'
-import type { DictTypeResponse, SaveFeedbackRequest, SignAgreementRequest } from './type'
+import type { DictTypeResponse, SignAgreementRequest } from './type'
 
 export const signAgreementApi = (data: SignAgreementRequest) => {
   return http.post<boolean>('/admin/api/sign-agreement', data, {
@@ -11,10 +11,3 @@ export const signAgreementApi = (data: SignAgreementRequest) => {
 export const getDictTypeApi = (type: string) => {
   return http.get<DictTypeResponse>(`/admin/dict/type/${type}`)
 }
-
-export const saveFeedbackApi = (data: SaveFeedbackRequest) => {
-  return http.post<boolean>('/admin/api/saveFeedBack', data, {
-    loading: true,
-    loadingText: '提交中...',
-  })
-}

+ 0 - 4
src/services/modules/common/type.d.ts

@@ -21,7 +21,3 @@ export interface DictItem {
   updateTime: string
 }
 
-export interface SaveFeedbackRequest {
-  ygContent: string
-  yjUserid: string
-}

+ 17 - 2
src/services/modules/mine/common/index.ts

@@ -1,10 +1,25 @@
 import http from '../../../index'
-import type { SubjectLocationAgreementRequest, SubjectLocationAgreementResponse } from './type'
+import type {
+  DeptRegulationsListResponse,
+  SaveFeedbackRequest,
+  SubjectLocationAgreementRequest,
+  SubjectLocationAgreementResponse,
+} from './type'
 
-// 获取当前企业配置的任务类型
 export const getSubjectLocationAgreementApi = (data: SubjectLocationAgreementRequest) => {
   return http.get<SubjectLocationAgreementResponse>(
     '/admin/api/sign/contract/subjectlocation-agreement',
     data
   )
 }
+
+export const saveFeedbackApi = (data: SaveFeedbackRequest) => {
+  return http.post<boolean>('/admin/api/saveFeedBack', data, {
+    loading: true,
+    loadingText: '提交中...',
+  })
+}
+
+export const getDeptRegulationsListApi = () => {
+  return http.get<DeptRegulationsListResponse>('/admin/dept/dept-regulations/list')
+}

+ 24 - 0
src/services/modules/mine/common/type.d.ts

@@ -11,3 +11,27 @@ export interface agreementInfosItem {
   name: string
   templateUrl: string
 }
+
+export interface SaveFeedbackRequest {
+  ygContent: string
+  yjUserid: string
+}
+
+export type DeptRegulationsListResponse = DeptRegulationsItem[]
+
+export interface DeptRegulationsItem {
+  id: number
+  deptId: number
+  fileName: string
+  fileUrl: ComplianceEducationFile[]
+  sort: number
+  delFlag: string
+  enableFlag: string
+  createTime: string
+  updateTime: string | null
+}
+
+export interface ComplianceEducationFile {
+  fileName: string
+  url: string
+}