leiyun пре 2 месеци
родитељ
комит
c0416f2ca0
12 измењених фајлова са 630 додато и 30 уклоњено
  1. +21
    -0
      App.vue
  2. +303
    -0
      components/h5-signature/h5-signature.vue
  3. +17
    -2
      config/env.js
  4. +8
    -0
      manifest.json
  5. +3
    -3
      pages.json
  6. +13
    -1
      pages/content/content.vue
  7. +37
    -1
      pages/index/index.vue
  8. +154
    -16
      pages/login/index.vue
  9. +23
    -3
      pages/profile/profile.vue
  10. +26
    -4
      pages/sign/sign.vue
  11. +10
    -0
      pages/sign/signature.vue
  12. +15
    -0
      vite.config.js

+ 21
- 0
App.vue Прегледај датотеку

@@ -22,4 +22,25 @@
}

/* 每个页面公共css */

/* #ifdef H5 */
h1, h2, h3, h4, h5, h6 {
font-size: inherit;
font-weight: inherit;
}
ol, ul, menu {
list-style: none;
}
ul, ol {
list-style: none;
margin: 0;
padding: 0;
}
.page {
min-height: calc(100vh - 50px) !important;
}
uni-page-head .uni-page-head {
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.08);
}
/* #endif */
</style>

+ 303
- 0
components/h5-signature/h5-signature.vue Прегледај датотеку

@@ -0,0 +1,303 @@
<template>
<view v-if="show" class="sign-page" @touchmove.stop.prevent>
<view class="canvas-wrap">
<view class="hint-text">
<text>请在此处手写签名</text>
<text class="hint-sub">请横屏书写</text>
</view>
<view id="signContainer" class="sign-container" />
</view>
<view class="bottom-btns">
<view class="btn-close" @click="handleClose">
<text>取消</text>
</view>
<view class="btn-clear" @click="handleClear">
<text>清除</text>
</view>
<view class="btn-save" @click="handleSave">
<text>{{ saving ? '上传中' : '保存' }}</text>
</view>
</view>
</view>
</template>

<script setup>
import { ref, watch, nextTick } from 'vue'
import { upload } from '@/utils/request.js'

const props = defineProps({
show: { type: Boolean, default: false }
})
const emit = defineEmits(['close', 'confirm'])

let signature = null
const hasDrawn = ref(false)
const saving = ref(false)
let scriptLoaded = false

const loadScript = () => {
return new Promise((resolve, reject) => {
if (window.SmoothSignature) { resolve(); return }
if (scriptLoaded) {
const check = setInterval(() => {
if (window.SmoothSignature) { clearInterval(check); resolve() }
}, 50)
return
}
scriptLoaded = true
const s = document.createElement('script')
s.src = 'https://cdn.csybhelp.com/lib/smooth-signature.umd.min.js'
s.onload = resolve
s.onerror = reject
document.head.appendChild(s)
})
}

const initSignature = async () => {
await loadScript()
await nextTick()
setTimeout(() => {
const container = document.getElementById('signContainer')
if (!container) return
container.innerHTML = ''
const canvas = document.createElement('canvas')
canvas.style.width = '100%'
canvas.style.height = '100%'
container.appendChild(canvas)
signature = new window.SmoothSignature(canvas, {
width: container.offsetWidth,
height: container.offsetHeight,
minWidth: 2,
maxWidth: 6,
color: '#333',
bgColor: 'transparent'
})
canvas.addEventListener('touchstart', () => { hasDrawn.value = true })
canvas.addEventListener('mousedown', () => { hasDrawn.value = true })
}, 200)
}

watch(() => props.show, async (val) => {
if (val) {
hasDrawn.value = false
saving.value = false
signature = null
await initSignature()
}
})

const handleClear = () => {
if (signature) signature.clear()
hasDrawn.value = false
}

const handleClose = () => { emit('close') }

const handleSave = async () => {
if (!hasDrawn.value || !signature) {
return uni.showToast({ title: '请先签名', icon: 'none' })
}
if (saving.value) return
saving.value = true
uni.showLoading({ title: '保存中...' })
try {
// 从 smooth-signature 获取 PNG DataURL
const dataUrl = signature.getPNG()
if (!dataUrl) throw { msg: '获取签名失败' }

// 旋转签名(逆时针90度,横屏→竖屏,与小程序端保持一致)
const rotatedDataUrl = await rotateImage(dataUrl, -90)
const blob = dataURLtoBlob(rotatedDataUrl)
const formData = new FormData()
formData.append('file', blob, 'signature.png')

// H5 下直接用 XMLHttpRequest 上传(uni.uploadFile 需要 filePath)
const token = uni.getStorageSync('pap-cytx-token') || ''
const baseUrl = uni.$u && uni.$u.http && uni.$u.http.config
? uni.$u.http.config.baseURL
: '/pro-api'
const url = baseUrl + '/api/mp/upload'

const result = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('POST', url)
if (token) xhr.setRequestHeader('Authorization', 'Bearer ' + token)
xhr.onload = () => {
try {
const res = JSON.parse(xhr.responseText)
if (res.code === 0 && res.data && res.data.url) resolve(res.data.url)
else reject({ msg: res.msg || '上传失败' })
} catch (e) { reject({ msg: '上传失败' }) }
}
xhr.onerror = () => reject({ msg: '网络错误' })
xhr.send(formData)
})

uni.hideLoading()
emit('confirm', { url: result })
} catch (err) {
uni.hideLoading()
uni.showToast({ title: err.msg || '上传失败', icon: 'none' })
} finally {
saving.value = false
}
}

const dataURLtoBlob = (dataUrl) => {
const arr = dataUrl.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) u8arr[n] = bstr.charCodeAt(n)
return new Blob([u8arr], { type: mime })
}

const rotateImage = (dataUrl, degrees) => {
return new Promise((resolve) => {
const img = new Image()
img.onload = () => {
const canvas = document.createElement('canvas')
const rad = (degrees * Math.PI) / 180
// 旋转 -90 度:宽高互换
if (Math.abs(degrees) === 90) {
canvas.width = img.height
canvas.height = img.width
} else {
canvas.width = img.width
canvas.height = img.height
}
const ctx = canvas.getContext('2d')
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(rad)
ctx.drawImage(img, -img.width / 2, -img.height / 2)
resolve(canvas.toDataURL('image/png'))
}
img.src = dataUrl
})
}
</script>

<style lang="scss" scoped>
.sign-page {
position: fixed;
top: 30rpx;
left: 30rpx;
right: 30rpx;
bottom: 30rpx;
z-index: 9999;
display: flex;
flex-direction: column;
background: #edf2fc;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5);
}

.canvas-wrap {
flex: 1;
position: relative;
overflow: hidden;

.hint-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(90deg);
pointer-events: none;
z-index: 0;
display: flex;
flex-direction: column;
align-items: center;
gap: 16rpx;
width: 80vh;

text {
font-size: 48rpx;
color: #c0c8d8;
font-weight: 300;
letter-spacing: 16rpx;
white-space: nowrap;
}

.hint-sub {
font-size: 28rpx;
letter-spacing: 8rpx;
}
}

.sign-container {
width: 100%;
height: 100%;
position: relative;
z-index: 1;
}
}

.bottom-btns {
display: flex;
height: calc(100rpx + env(safe-area-inset-bottom));
flex-shrink: 0;
gap: 12rpx;
padding: 12rpx;
padding-bottom: calc(12rpx + env(safe-area-inset-bottom));
background: #edf2fc;

.btn-close {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background: #8c939d;
border-radius: 12rpx;

text {
writing-mode: vertical-rl;
transform: rotate(90deg);
font-size: 32rpx;
color: #fff;
letter-spacing: 4rpx;
height: 120rpx;
text-align: center;
}
}

.btn-clear {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background: #ff4040;
border-radius: 12rpx;

text {
writing-mode: vertical-rl;
transform: rotate(90deg);
font-size: 32rpx;
color: #fff;
letter-spacing: 4rpx;
height: 120rpx;
text-align: center;
}
}

.btn-save {
flex: 2;
display: flex;
align-items: center;
justify-content: center;
background: #0e63e3;
border-radius: 12rpx;

text {
writing-mode: vertical-rl;
transform: rotate(90deg);
font-size: 32rpx;
color: #fff;
letter-spacing: 4rpx;
height: 120rpx;
text-align: center;
}
}
}
</style>

+ 17
- 2
config/env.js Прегледај датотеку

@@ -10,7 +10,22 @@ const envConf = {
// 正式版-正式环境
release: {
BASE_URL: 'https://cytx.csybhelp.com',
},
// H5 环境
h5: {
BASE_URL: '/pro-api',
}
}
export default envConf[uni.getAccountInfoSync().miniProgram.envVersion || 'develop']; // 可在此处手
// export default envConf['develop'];

function getEnvConfig() {
// #ifdef H5
return envConf['h5']
// #endif
// #ifdef MP-WEIXIN
return envConf[uni.getAccountInfoSync().miniProgram.envVersion || 'develop']
// #endif
// eslint-disable-next-line no-unreachable
return envConf['develop']
}

export default getEnvConfig()

+ 8
- 0
manifest.json Прегледај датотеку

@@ -60,5 +60,13 @@
"uniStatistics" : {
"enable" : false
},
"h5" : {
"title" : "肠愈同行",
"router" : {
"mode" : "history",
"base" : "/"
},
"devServer" : {}
},
"vueVersion" : "3"
}

+ 3
- 3
pages.json Прегледај датотеку

@@ -11,20 +11,20 @@
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": ""
"navigationBarTitleText": "肠愈同行"
}
},
{
"path": "pages/profile/profile",
"style": {
"navigationBarTitleText": ""
"navigationBarTitleText": "我的"
}
},
{
"path": "pages/content/content",
"style": {
"navigationStyle": "default",
"navigationBarTitleText": ""
"navigationBarTitleText": "详情"
}
},
{


+ 13
- 1
pages/content/content.vue Прегледај датотеку

@@ -1,7 +1,7 @@
<template>
<view class="page">
<view class="content-body" v-if="loaded">
<mp-html :content="content" />
<mp-html :content="content" :tag-style="mpHtmlTagStyle" />
</view>
<view v-else class="loading">
<u-loading-icon />
@@ -15,6 +15,18 @@ import { onLoad } from '@dcloudio/uni-app'
import { get } from '@/utils/request.js'
import mpHtml from '@/uni_modules/mp-html/components/mp-html/mp-html.vue'

const mpHtmlTagStyle = {
h1: 'font-size:inherit;font-weight:inherit;',
h2: 'font-size:inherit;font-weight:inherit;',
h3: 'font-size:inherit;font-weight:inherit;',
h4: 'font-size:inherit;font-weight:inherit;',
h5: 'font-size:inherit;font-weight:inherit;',
h6: 'font-size:inherit;font-weight:inherit;',
ol: 'list-style:none;margin:0;padding:0;',
ul: 'list-style:none;margin:0;padding:0;',
menu: 'list-style:none;'
}

const content = ref('')
const loaded = ref(false)



+ 37
- 1
pages/index/index.vue Прегледај датотеку

@@ -1,13 +1,20 @@
<template>
<view class="page">
<!-- 顶部横幅 -->
<!-- #ifdef MP-WEIXIN -->
<image class="banner" src="https://cdn.csybhelp.com/images/cytx/index-bg.png" mode="widthFix" />
<!-- #endif -->
<!-- #ifdef H5 -->
<view class="banner-h5">
<img class="banner-img" src="https://cdn.csybhelp.com/images/cytx/index-bg.png" />
</view>
<!-- #endif -->

<!-- 正文内容区域 -->
<view class="content-area">
<view v-if="content" class="letter">
<view class="body">
<mp-html :content="content" />
<mp-html :content="content" :tag-style="mpHtmlTagStyle" />
</view>
</view>
<view v-else-if="!loaded" class="loading-text">加载中...</view>
@@ -24,6 +31,18 @@ import { get } from '@/utils/request.js'
import { getToken, getUserInfo, setUserInfo } from '@/utils/cache.js'
import mpHtml from '@/uni_modules/mp-html/components/mp-html/mp-html.vue'

const mpHtmlTagStyle = {
h1: 'font-size:inherit;font-weight:inherit;',
h2: 'font-size:inherit;font-weight:inherit;',
h3: 'font-size:inherit;font-weight:inherit;',
h4: 'font-size:inherit;font-weight:inherit;',
h5: 'font-size:inherit;font-weight:inherit;',
h6: 'font-size:inherit;font-weight:inherit;',
ol: 'list-style:none;margin:0;padding:0;',
ul: 'list-style:none;margin:0;padding:0;',
menu: 'list-style:none;'
}

const CONTENT_CACHE_KEY = 'cytx-index-content'

const content = ref('')
@@ -146,6 +165,20 @@ onShow(() => {
display: block;
}

.banner-h5 {
width: 100%;
height: 350px;
overflow: hidden;

.banner-img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center bottom;
display: block;
}
}

.content-area {
background: #fff;
border-radius: 24rpx;
@@ -153,6 +186,9 @@ onShow(() => {
position: relative;
z-index: 1;
padding: 56rpx 40rpx 32rpx;
/* #ifdef H5 */
margin-top: -172px;
/* #endif */
}

.letter {


+ 154
- 16
pages/login/index.vue Прегледај датотеку

@@ -7,9 +7,29 @@
</view>

<view class="btn-area">
<button class="login-btn" @tap="handleLogin" :loading="loading">
<!-- #ifdef MP-WEIXIN -->
<button class="login-btn" @tap="handleWxLogin" :loading="loading">
微信一键登录
</button>
<!-- #endif -->

<!-- #ifdef H5 -->
<view class="phone-form">
<view class="form-item">
<input class="form-input" type="number" v-model="phone" placeholder="请输入手机号" maxlength="11" />
</view>
<view class="form-item code-row">
<input class="form-input code-input" type="number" v-model="smsCode" placeholder="请输入验证码" maxlength="6" />
<button class="sms-btn" :disabled="countdown > 0" @tap="sendSms">
{{ countdown > 0 ? countdown + 's' : '获取验证码' }}
</button>
</view>
<button class="login-btn" @tap="handlePhoneLogin" :loading="loading">
登录
</button>
</view>
<!-- #endif -->

<view class="tip">登录即表示同意
<text class="link" @tap="goPrivacy">《隐私协议》</text>
</view>
@@ -18,13 +38,36 @@
</template>

<script setup>
import { ref } from 'vue'
import { ref, onUnmounted } from 'vue'
import { post } from '@/utils/request.js'
import { setToken, setUserInfo } from '@/utils/cache.js'

const loading = ref(false)
const phone = ref('')
const smsCode = ref('')
const countdown = ref(0)
let timer = null

onUnmounted(() => {
if (timer) { clearInterval(timer); timer = null }
})

const handleLogin = async () => {
const loginSuccess = (res) => {
setToken(res.data.token)
setUserInfo(res.data.userInfo)
uni.showToast({ title: '登录成功', icon: 'success' })
setTimeout(() => {
const pages = getCurrentPages()
if (pages.length > 1) {
uni.navigateBack()
} else {
uni.switchTab({ url: '/pages/profile/profile' })
}
}, 500)
}

// #ifdef MP-WEIXIN
const handleWxLogin = async () => {
if (loading.value) return
loading.value = true
try {
@@ -36,26 +79,65 @@ const handleLogin = async () => {
})
})
const res = await post('/api/mp/login', { code })
setToken(res.data.token)
setUserInfo(res.data.userInfo)
uni.showToast({ title: '登录成功', icon: 'success' })
setTimeout(() => {
const pages = getCurrentPages()
if (pages.length > 1) {
uni.navigateBack()
} else {
uni.switchTab({ url: '/pages/profile/profile' })
}
}, 500)
loginSuccess(res)
} catch (e) {
if (e && e.msg) uni.showToast({ title: e.msg, icon: 'none' })
} finally {
loading.value = false
}
}
// #endif

// #ifdef H5
const sendSms = async () => {
if (countdown.value > 0) return
if (!phone.value || phone.value.length !== 11) {
return uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
}
try {
const res = await post('/api/mp/sendSmsCode', { mobile: phone.value, bizType: 'login' })
if (res.data && res.data.code) {
uni.showToast({ title: `验证码: ${res.data.code}`, icon: 'none', duration: 3000 })
} else {
uni.showToast({ title: '验证码已发送', icon: 'none' })
}
countdown.value = 60
timer = setInterval(() => {
countdown.value--
if (countdown.value <= 0) { clearInterval(timer); timer = null }
}, 1000)
} catch (e) {
if (e && e.msg) uni.showToast({ title: e.msg, icon: 'none' })
}
}

const handlePhoneLogin = async () => {
if (loading.value) return
if (!phone.value || phone.value.length !== 11) {
return uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
}
if (!smsCode.value || smsCode.value.length !== 6) {
return uni.showToast({ title: '请输入6位验证码', icon: 'none' })
}
loading.value = true
try {
const res = await post('/api/mp/phoneLogin', { mobile: phone.value, code: smsCode.value })
loginSuccess(res)
} catch (e) {
if (e && e.msg) uni.showToast({ title: e.msg, icon: 'none' })
} finally {
loading.value = false
}
}
// #endif

const goPrivacy = () => {
// #ifdef H5
uni.navigateTo({ url: '/pages/content/content?key=privacy_policy_h5' })
// #endif
// #ifdef MP-WEIXIN
uni.navigateTo({ url: '/pages/content/content?key=privacy_policy' })
// #endif
}
</script>

@@ -67,6 +149,9 @@ const goPrivacy = () => {
flex-direction: column;
align-items: center;
padding-top: 25vh;
/* #ifdef H5 */
padding-top: 10vh;
/* #endif */
}

.logo-area {
@@ -94,8 +179,6 @@ const goPrivacy = () => {
color: #b0b3b8;
letter-spacing: 2rpx;
}


}

.btn-area {
@@ -133,4 +216,59 @@ const goPrivacy = () => {
}
}
}

.phone-form {
width: 100%;

.form-item {
margin-bottom: 24rpx;
}

.form-input {
width: 100%;
height: 88rpx;
border: 1rpx solid #dcdfe6;
border-radius: 44rpx;
padding: 0 36rpx;
font-size: 30rpx;
box-sizing: border-box;
background: #f8f9fa;
}

.code-row {
display: flex;
gap: 20rpx;

.code-input {
flex: 2;
}
}

.sms-btn {
flex: 1;
height: 88rpx;
line-height: 88rpx;
padding: 0;
background: #0F78E9;
color: #fff;
border: none;
border-radius: 44rpx;
font-size: 26rpx;
white-space: nowrap;
text-align: center;

&::after {
border: none;
}

&[disabled] {
background: #a0cfff;
color: #fff;
}
}

.login-btn {
margin-top: 16rpx;
}
}
</style>

+ 23
- 3
pages/profile/profile.vue Прегледај датотеку

@@ -13,7 +13,7 @@
</view>
<view class="info" @tap="!isLoggedIn && goLogin()">
<view class="name">{{ displayName }}</view>
<view v-if="isLoggedIn && userInfo && userInfo.patient && userInfo.patient.patient_no" class="patient-no">{{ userInfo.patient.patient_no }}</view>
<view v-if="isLoggedIn && userInfo && userInfo.patient && userInfo.patient.patient_no" class="patient-no">No·{{ userInfo.patient.patient_no }}</view>
</view>
</view>
</view>
@@ -65,7 +65,7 @@
<text class="text">修改手机号</text>
<u-icon name="arrow-right" size="16" color="#c0c4cc" />
</view>
<view class="menu-item" @tap="goTo('/pages/content/content?key=privacy_policy')">
<view class="menu-item" @tap="goPrivacy">
<view class="menu-icon">
<u-icon name="lock-fill" size="20" color="#909399" />
</view>
@@ -104,10 +104,18 @@ const patientStatus = computed(() => {
})
const isRejected = computed(() => patientStatus.value === 2)

const randomTag = () => {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
let s = ''
for (let i = 0; i < 4; i++) s += chars.charAt(Math.floor(Math.random() * chars.length))
return s
}
const _userTag = randomTag()

const displayName = computed(() => {
if (!isLoggedIn.value) return '点击登录'
if (isAuthed.value && userInfo.value && userInfo.value.patient && userInfo.value.patient.name) return userInfo.value.patient.name
return (userInfo.value && userInfo.value.nickname) || '微信用户'
return (userInfo.value && userInfo.value.nickname) || ('用户' + _userTag)
})

onShow(() => {
@@ -163,6 +171,15 @@ const goTo = (url) => {
uni.navigateTo({ url })
}

const goPrivacy = () => {
// #ifdef H5
uni.navigateTo({ url: '/pages/content/content?key=privacy_policy_h5' })
// #endif
// #ifdef MP-WEIXIN
uni.navigateTo({ url: '/pages/content/content?key=privacy_policy' })
// #endif
}

const goMyInfo = () => {
if (!checkAuth()) return
uni.navigateTo({ url: '/pages/myinfo/myinfo' })
@@ -265,6 +282,9 @@ const handleLogout = () => {
position: relative;
padding: 220rpx 40rpx 60rpx;
overflow: hidden;
/* #ifdef H5 */
padding-top: 120rpx;
/* #endif */
}

.header-bg {


+ 26
- 4
pages/sign/sign.vue Прегледај датотеку

@@ -35,7 +35,7 @@
<!-- 签名区域 -->
<view class="section">
<view class="sign-label">请在下方签名确认</view>
<view class="canvas-wrap" @tap="goSignature">
<view class="canvas-wrap" @tap="openSignature">
<image v-if="signImageUrl" class="sign-preview" :src="signImageUrl" mode="aspectFit" />
<view v-else class="canvas-placeholder">
<u-icon name="edit-pen" size="28" color="#ccc" />
@@ -43,7 +43,7 @@
</view>
</view>
<view v-if="signImageUrl" class="sign-actions">
<view class="clear-btn" @tap="goSignature">重新签名</view>
<view class="clear-btn" @tap="openSignature">重新签名</view>
</view>
</view>

@@ -51,6 +51,10 @@
<view class="btn-wrap">
<u-button text="确认签署" :loading="submitting" @click="confirmSign" color="#0E63E3" size="large" />
</view>

<!-- #ifdef H5 -->
<h5-signature :show="showH5Sign" @close="showH5Sign = false" @confirm="onH5SignConfirm" />
<!-- #endif -->
</view>
</template>

@@ -68,6 +72,9 @@ const guardianIdCard = ref('')
const guardianRelation = ref('')
const signImageUrl = ref('')
const submitting = ref(false)
// #ifdef H5
const showH5Sign = ref(false)
// #endif

const onSignatureResult = (data) => {
if (data.url) signImageUrl.value = data.url
@@ -75,17 +82,20 @@ const onSignatureResult = (data) => {

onLoad((options) => {
signType.value = options.type || 'privacy'
// 回显上次填写的数据
if (options.amount) incomeAmount.value = decodeURIComponent(options.amount)
if (options.guardianName) guardianName.value = decodeURIComponent(options.guardianName)
if (options.guardianIdCard) guardianIdCard.value = decodeURIComponent(options.guardianIdCard)
if (options.guardianRelation) guardianRelation.value = decodeURIComponent(options.guardianRelation)
// #ifdef MP-WEIXIN
uni.$on('signatureResult', onSignatureResult)
// #endif
loadContent()
})

onBeforeUnmount(() => {
// #ifdef MP-WEIXIN
uni.$off('signatureResult', onSignatureResult)
// #endif
})

const loadContent = async () => {
@@ -97,9 +107,21 @@ const loadContent = async () => {
} catch (e) {}
}

const goSignature = () => {
const openSignature = () => {
// #ifdef MP-WEIXIN
uni.navigateTo({ url: '/pages/sign/signature' })
// #endif
// #ifdef H5
showH5Sign.value = true
// #endif
}

// #ifdef H5
const onH5SignConfirm = (data) => {
showH5Sign.value = false
if (data.url) signImageUrl.value = data.url
}
// #endif

const confirmSign = async () => {
if (!signImageUrl.value) {


+ 10
- 0
pages/sign/signature.vue Прегледај датотеку

@@ -3,6 +3,7 @@
<view class="canvas-wrap">
<view class="hint-text">
<text>请在此处手写签名</text>
<text class="hint-sub">请横屏书写</text>
</view>
<canvas id="signCanvas" canvas-id="signCanvas" type="2d" class="sign-canvas" disable-scroll
@touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd" />
@@ -181,6 +182,10 @@ const handleSave = () => {
pointer-events: none;
white-space: nowrap;
z-index: 0;
display: flex;
flex-direction: column;
align-items: center;
gap: 16rpx;

text {
font-size: 48rpx;
@@ -188,6 +193,11 @@ const handleSave = () => {
font-weight: 300;
letter-spacing: 16rpx;
}

.hint-sub {
font-size: 28rpx;
letter-spacing: 8rpx;
}
}

.sign-canvas {


+ 15
- 0
vite.config.js Прегледај датотеку

@@ -0,0 +1,15 @@
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'

export default defineConfig({
plugins: [uni()],
server: {
proxy: {
'/pro-api': {
target: 'http://192.168.3.66:8361',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/pro-api/, '')
}
}
}
})

Loading…
Откажи
Сачувај