| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- <template>
- <view class="agreement-container">
- <view class="agreement-list">
- <view
- v-for="item in staticAgreementList"
- :key="item.path"
- class="agreement-card"
- hover-class="agreement-card--active"
- @click="navigateToAgreementPage(item.path)"
- >
- <text class="agreement-card__title">《{{ item.name }}》</text>
- <text class="agreement-card__arrow">›</text>
- </view>
- <view
- v-for="item in certSuccessList"
- :key="item.templateUrl"
- class="agreement-card"
- hover-class="agreement-card--active"
- @click="openPdfFile(item)"
- >
- <text class="agreement-card__title">《{{ item.name }}》</text>
- <text class="agreement-card__arrow">›</text>
- </view>
- </view>
- </view>
- </template>
- <script setup lang="ts">
- import { computed, ref } from 'vue'
- import { onLoad } from '@dcloudio/uni-app'
- import { getSubjectLocationAgreementApi } from '@/services/modules/mine/common'
- import type { agreementInfosItem } from '@/services/modules/mine/type'
- import { useUserStore } from '@/stores/modules/user'
- const AGREEMENT_PAGE_BASE = '/pages-mine/agreement'
- const CERT_SUCCESS_STATUS = 'CERT'
- const userStore = useUserStore()
- const currentUserInfo = computed(() => userStore.currentUserInfo)
- const certSuccessList = ref<agreementInfosItem[]>([])
- const showHonestAgreement = computed(() => hasSignedAgreement('HONEST_AGREEMENT'))
- const showHonestAgreementV2 = computed(() => hasSignedAgreement('HONEST_AGREEMENT_V2'))
- const staticAgreementList = computed(() => {
- const list = [
- {
- name: '要易云平台用户协议',
- path: 'platform-user-agreement',
- },
- {
- name: '隐私权政策',
- path: 'privacy-policy',
- },
- {
- name: '个人信息使用授权书',
- path: 'personal-info-authorization',
- },
- ]
- if (showHonestAgreement.value) {
- list.push({
- name: '廉洁承诺书',
- path: 'integrity-commitment',
- })
- }
- if (showHonestAgreementV2.value) {
- list.push({
- name: '承诺书2.0',
- path: 'integrity-commitment-v2',
- })
- }
- return list
- })
- onLoad(() => {
- void getCertSuccessList()
- })
- async function getCertSuccessList(): Promise<void> {
- const subjectLocations = getCertifiedSubjectLocations()
- if (!subjectLocations.length) {
- certSuccessList.value = []
- return
- }
- uni.showLoading({
- title: '加载中...',
- mask: true,
- })
- let hasError = false
- try {
- const agreementInfoGroups = await Promise.all(
- subjectLocations.map(async (subjectLocation) => {
- try {
- const res = await getSubjectLocationAgreementApi({
- subjectLocation,
- })
- return normalizeAgreementInfos(res.data?.agreementInfos)
- } catch (error) {
- hasError = true
- console.log('getSubjectLocationAgreementApi error', error)
- return []
- }
- })
- )
- certSuccessList.value = dedupeAgreementInfos(agreementInfoGroups.flat())
- if (hasError) {
- showToast('部分协议加载失败')
- }
- } finally {
- uni.hideLoading()
- }
- }
- 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 {
- 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> {
- return new Promise((resolve, reject) => {
- uni.downloadFile({
- url,
- success: (res) => {
- if (res.statusCode === 200 && res.tempFilePath) {
- resolve(res.tempFilePath)
- return
- }
- reject(new Error('download file failed'))
- },
- fail: reject,
- })
- })
- }
- function openDocument(filePath: string): Promise<void> {
- return new Promise((resolve, reject) => {
- uni.openDocument({
- filePath,
- fileType: 'pdf',
- showMenu: true,
- success: () => {
- resolve()
- },
- fail: reject,
- })
- })
- }
- function 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
- }
- function showToast(title: string): void {
- uni.showToast({
- title,
- icon: 'none',
- })
- }
- </script>
- <style lang="scss" scoped>
- .agreement-container {
- min-height: 100vh;
- padding: 28rpx 24rpx 40rpx;
- box-sizing: border-box;
- background:
- radial-gradient(
- circle at 50% -120rpx,
- rgba(47, 140, 255, 0.14) 0%,
- rgba(47, 140, 255, 0) 420rpx
- ),
- linear-gradient(180deg, #f6f8fc 0%, #f7f8fa 100%);
- }
- .agreement-list {
- display: flex;
- flex-direction: column;
- gap: 18rpx;
- }
- .agreement-card {
- position: relative;
- display: flex;
- align-items: center;
- min-height: 112rpx;
- padding: 0 28rpx 0 32rpx;
- box-sizing: border-box;
- border: 1rpx solid rgba(31, 41, 55, 0.06);
- border-radius: 24rpx;
- background:
- linear-gradient(180deg, rgba(255, 255, 255, 0.96) 0%, rgba(255, 255, 255, 0.9) 100%), #ffffff;
- box-shadow:
- 0 16rpx 40rpx rgba(15, 23, 42, 0.05),
- 0 2rpx 8rpx rgba(15, 23, 42, 0.03);
- overflow: hidden;
- transition:
- transform 0.18s ease,
- opacity 0.18s ease;
- }
- .agreement-card--active {
- opacity: 0.86;
- transform: scale(0.985);
- }
- .agreement-card__title {
- flex: 1;
- min-width: 0;
- padding-right: 24rpx;
- font-size: 31rpx;
- font-weight: 600;
- line-height: 44rpx;
- color: #1f2937;
- letter-spacing: 0.2rpx;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .agreement-card__arrow {
- flex-shrink: 0;
- font-size: 48rpx;
- font-weight: 300;
- line-height: 1;
- color: #aeb8c6;
- }
- </style>
|