提交 fcab8972 作者: 方治民

feat: 添加通用的 about 页面、feedback 意见反馈、webview 页面兼容性调整等

上级 bb1abb3b
......@@ -8,11 +8,29 @@
}
},
// ================================ 通用页面分割线 ====================================
// === 关于我们 ===
{
"path": "pages/about/index",
"style": {
"navigationBarTitleText": "关于我们",
"backgroundColorTop": "#FFFFFF",
"backgroundColorBottom": "#FFFFFF"
}
},
// === 问题反馈 ===
{
"path": "pages/common/feedback/index",
"style": {
"navigationBarTitleText": "问题反馈",
"backgroundColorTop": "#FFFFFF",
"backgroundColorBottom": "#FFFFFF"
}
},
// === Webview ===
{
"path": "pages/common/webview/index",
"style": {
"titleNView": true,
"navigationBarTitleText": ""
}
},
......@@ -20,7 +38,6 @@
{
"path": "pages/common/viewer/pdf",
"style": {
"titleNView": true,
"navigationBarTitleText": ""
}
},
......@@ -32,13 +49,9 @@
"titleNView": false,
// #endif
"navigationBarTitleText": "开发中",
"enablePullDownRefresh": false,
"backgroundColorTop": "#FFFFFF",
"backgroundColorBottom": "#FFFFFF",
"backgroundColor": "#FFFFFE",
"mp-alipay": {
"allowsBounceVertical": "YES"
}
"backgroundColor": "#FFFFFE"
}
},
// === 版本更新 ===
......
<script setup lang="ts">
import dayjs from 'dayjs'
import Link from '@/utils/page/link'
const startYear = ref(2005)
const currentYear = ref(dayjs().year())
</script>
<template>
<view class="wrap">
<view class="logoBox">
<image class="logo" src="/static/logo.png" />
<view class="text-#555 font-bold p-2">{{ $app.name }}</view>
</view>
<fui-footer isFixed>
<template #text>
<view class="links">
<text class="underline" @tap="Link.to(Link.services, '服务协议')">服务协议</text>
<text class="underline" @tap="Link.to(Link.privacy, '隐私政策')">隐私政策</text>
</view>
<view class="mt-1">Copyright © {{ startYear }}-{{ currentYear }} {{ Link.copyright.name }}</view>
<view class="mt-2" v-if="Link.copyright.name !== Link.company.name">
技术支持:
<fui-link underline color="#b2b2b2" size="24" :href="Link.company.link" :text="Link.company.name" />
</view>
</template>
</fui-footer>
</view>
</template>
<style lang="less" scoped>
.wrap {
padding: 0 46rpx;
padding-top: 100rpx;
font-size: 32rpx;
height: 100vh;
background-color: white;
box-sizing: border-box;
}
.logoBox {
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
.logo {
width: 160rpx;
height: 160rpx;
box-shadow: 0 0 15rpx #d7d7d7;
border-radius: 20rpx;
}
.logo_text {
width: 126rpx;
height: 40rpx;
margin-top: 20rpx;
}
}
.links {
padding: 10rpx;
* {
padding: 10rpx 20rpx;
}
}
</style>
<script lang="ts" setup>
import { USER_ID_KEY } from '/@/enums/cacheEnum'
const db = uniCloud.database()
const dbCollectionName = 'opendb-feedback'
const model = reactive({
userId: uni.getStorageSync(USER_ID_KEY),
content: '',
mobile: '',
imgs: [],
loading: false,
})
function submit() {
Message.loading('提交中...')
// 使用 clientDB 提交数据
db.collection(dbCollectionName)
.add({
user_id: model.userId,
content: model.content,
mobile: model.mobile,
imgs: model.imgs,
})
.then(() => {
Message.toast('提交成功')
setTimeout(() => uni.navigateBack(), 500)
})
.catch((err) => {
uni.showModal({
content: err.message || '请求服务失败',
showCancel: false,
})
})
.finally(() => {
uni.hideLoading()
})
}
</script>
<template>
<view class="feedback-body">
<text class="text-black">问题反馈和意见建议 <text style="color: red">*</text></text>
<textarea
placeholder="请描述您遇到的问题或对本产品的建议..."
v-model="model.content"
class="feedback-textare"
maxlength="-1"
></textarea>
<view class="image-title">
<text class="text-black">上传问题截图<text class="text-grey"> (选填,最多可上传6张) </text> </text>
<view class="text-grey">{{ model.imgs.length }}/6</view>
</view>
<view class="filepicker">
<uni-file-picker file-mediatype="image" :limit="6" return-type="array" v-model="model.imgs" />
</view>
<text class="text-black">联系方式<text class="text-grey">(选填)</text> </text>
<input class="feedback-input" v-model="model.mobile" placeholder="请输入您的手机号" />
<view class="btn">
<fui-button
:disabled="!model.content || model.loading"
:loading="model.loading"
type="primary"
@tap="submit"
>
提 交
</fui-button>
<!-- 安全区 -->
<fui-safe-area />
</view>
</view>
</template>
<style lang="scss" scoped>
.text-black {
color: #303133;
font-size: 32rpx;
}
.text-grey {
color: #bcbcbc;
font-size: 24rpx;
margin-left: 15rpx;
}
.feedback-quick {
padding-right: 10rpx;
color: #606266;
font-size: 32rpx;
}
.feedback-body {
padding: 30rpx;
background-color: white;
min-height: 100vh;
box-sizing: border-box;
}
.feedback-textare {
margin-top: 30rpx;
margin-bottom: 30rpx;
height: 266rpx;
color: #303133;
font-size: 28rpx;
line-height: 2em;
width: 100%;
box-sizing: border-box;
padding: 20rpx 30rpx;
border-radius: 20rpx;
background-color: #f5f6f8;
}
.feedback-input {
font-size: 28rpx;
color: #303133;
background-color: #f5f6f8;
border-radius: 20rpx;
height: 100rpx;
min-height: 100rpx;
padding: 0 30rpx;
margin-top: 30rpx;
margin-bottom: 60rpx;
}
.btn-submit {
border-radius: 20rpx;
color: #ffffff;
margin-top: 100rpx;
background-color: #007aff;
margin-bottom: 70rpx;
}
.image-title {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
color: #606266;
}
.filepicker {
margin-top: 30rpx;
margin-bottom: 30rpx;
}
.btn {
position: fixed;
bottom: 0;
left: 0;
right: 0;
margin: 30rpx;
}
</style>
<script lang="ts" setup>
import URI from 'urijs'
const page = reactive({
security: false,
title: '',
link: '',
styles: {
......@@ -10,10 +13,16 @@
},
})
onLoad(({ title, link }) => {
page.title = title ? decodeURI(title) : ''
onLoad(({ title, link, sub }) => {
page.title = title ? decodeURIComponent(title) : ''
page.link = decodeURIComponent(link)
// 嵌入页面进行二级子页面跳转时,参数会被多次 encode
if (sub) {
page.title = decodeURIComponent(page.title)
page.link = decodeURIComponent(page.link)
}
if (!link) {
Message.toast('页面打开失败,参数错误')
uni.navigateBack()
......@@ -26,11 +35,52 @@
title: page.title,
})
}
// #ifdef APP-PLUS
// 白名单同源检测
if (isSameOrigin(new URI(page.link))) {
page.security = true
} else {
// 对于不安全或不信任的网站地址或可能触发 plus API 的来源,采用手动创建 webview 的方式打开
const { windowHeight, statusBarHeight } = uni.getSystemInfoSync()
const webview = plus.webview.create(page.link, 'no-security-webview', {
plusrequire: 'none',
progress: page.styles.progress,
top: `${statusBarHeight + 44}px`,
height: `${windowHeight}px`,
})
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const currentWebview = currentPage.$getAppWebview()
currentWebview.setStyle({ progress: page.styles.progress })
currentWebview.append(webview)
}
// #endif
// #ifndef APP-PLUS
page.security = true
// #endif
})
// 安全域白名单,防止 APP 环境的意外注入和非法安全调用 plus API 的问题
const allows = ['yiring.com']
// 在安全域白名单中,但仍然需要排除的链接(通常是代理的网站链接)
const denyLinks = []
function isSameOrigin(url: URI) {
if (denyLinks.includes(url.href())) {
return false
}
return allows.some((item) => {
const regex = new RegExp(`^(.*\\.)?${item}$`, 'i')
return regex.test(url.hostname())
})
}
</script>
<template>
<view class="wrap" v-if="page.link">
<web-view :src="page.link" :update-title="!page.title" :webview-styles="page.styles" />
<template v-if="page.security">
<web-view :src="page.link" :update-title="!page.title" :webview-styles="page.styles" />
</template>
</view>
</template>
export default {
/**
* 公司信息
*/
company: {
name: '长沙壹润信息科技发展有限公司',
link: 'https://yiring.com',
},
/**
* 版权信息
*/
copyright: {
name: '长沙壹润信息科技发展有限公司',
link: 'https://yiring.com',
},
/**
* 服务协议地址
* @description TODO: 服务协议地址需要根据实际情况进行修改
*/
services: 'https://app.yiring.com/services.html',
/**
* 隐私政策地址
* @description TODO: 隐私政策地址需要根据实际情况进行修改
*/
privacy: 'https://app.yiring.com/privacy.html',
/**
* 从内部打开指定页面
* @param link 链接
* @param title 页面标题
*/
to(link: string, title: string) {
uni.navigateTo({
url: `/pages/common/webview/index?title=${encodeURIComponent(title)}&link=${encodeURIComponent(link)}`,
})
},
/**
* 从外部打开指定页面
* @param link 链接
*/
open(link: string) {
// #ifdef APP-PLUS
if (link?.startsWith('tel:')) {
uni.makePhoneCall({
phoneNumber: link.replace('tel:', ''),
})
} else {
plus.runtime.openURL(link)
}
// #endif
// #ifdef H5
window.open(link)
// #endif
},
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论