# Conflicts:
#	src/pages.json
This commit is contained in:
zjf committed 2025-05-22 14:52:22 +08:00
commit 86467d7298
32 files changed
+396 -583

No files matched your search

-1
View File
@@ -62,7 +62,6 @@
"@zebra-ui/swiper": "^2.2.8",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"gdt-jsapi": "^1.9.51",
"luch-request": "^3.1.1",
"pinia": "^2.2.6",
"qrcode": "^1.5.4",
-16
View File
@@ -65,9 +65,6 @@ importers:
dayjs:
specifier: ^1.11.13
version: 1.11.13
gdt-jsapi:
specifier: ^1.9.51
version: 1.9.51
luch-request:
specifier: ^3.1.1
version: 3.1.1
@@ -1304,9 +1301,6 @@ packages:
'@polka/url@1.0.0-next.28':
resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==}
'@remax/mini-types@0.1.0':
resolution: {integrity: sha512-bVDcbUrp6p0PZwfS0xGVQS6k9f8B35BtZaYt/W/IGKh7VALYQ+tS2KcytLRmIJ/Vmohaw5ikIRN89I+frPdh4g==}
'@rollup/pluginutils@5.1.4':
resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==}
engines: {node: '>=14.0.0'}
@@ -2426,9 +2420,6 @@ packages:
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
gdt-jsapi@1.9.51:
resolution: {integrity: sha512-VN1iIK7kJU2g2oxY/n8waCREhWxcVwR8qHAE1keVnUJHI6mTdgTelICzq++niRRCaLqUVWC1pYvftEVgk4sq8A==}
generic-names@4.0.0:
resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==}
@@ -6021,8 +6012,6 @@ snapshots:
'@polka/url@1.0.0-next.28': {}
'@remax/mini-types@0.1.0': {}
'@rollup/pluginutils@5.1.4(rollup@4.34.2)':
dependencies:
'@types/estree': 1.0.6
@@ -7275,11 +7264,6 @@ snapshots:
function-bind@1.1.2: {}
gdt-jsapi@1.9.51:
dependencies:
'@babel/runtime': 7.26.7
'@remax/mini-types': 0.1.0
generic-names@4.0.0:
dependencies:
loader-utils: 3.3.1
+10 -6
View File
@@ -1,7 +1,6 @@
<script lang="ts">
import isDevEnv from "./sheep/utils/isDevEnv"
import { getAppInfo } from "@@/utils/getAppInfo"
import getLaunchParam from "@/sheep/utils/getLaunchParam"
import { isDevEnv, getLaunchParam } from "@/sheep/utils/app"
import { getAppInfo } from "@@/utils/app"
// import { onLaunch, onShow, onHide } from "@dcloudio/uni-app"
// onLaunch(() => {})
@@ -153,9 +152,6 @@ export default {
case "coopMerchant":
uni.switchTab({ url: "/pages/tabbar/coopMerchant" })
break
case "thirdApp":
uni.navigateTo({ url: "/pages/thirdApp/thirdAppList" })
break
default:
break
}
@@ -163,6 +159,10 @@ export default {
// 诗画环境
const userRole = uni.getStorageSync("userRole")
const launchParam = getLaunchParam()
if (launchParam == "thirdApp") {
uni.navigateTo({ url: "/pages/thirdApp/thirdAppList" })
return
}
if (launchParam) {
switch (userRole) {
case "merchant":
@@ -206,6 +206,10 @@ export default {
} else if (appInfo == "sh") {
// 是诗画环境
const userRole = uni.getStorageSync("userRole")
if (launchParam == "thirdApp") {
uni.navigateTo({ url: "/pages/thirdApp/thirdApp" })
return
}
if (launchParam) {
switch (userRole) {
case "user":
+10 -15
View File
@@ -246,12 +246,19 @@
]
},
{
"root": "pages/test",
"root": "pages/user",
"pages": [
{
"path": "login",
"style": {
"navigationBarTitleText": "",
"navigationBarTitleText": "密码登录",
"disableScroll": true
}
},
{
"path": "userRole",
"style": {
"navigationBarTitleText": "用户身份",
"disableScroll": true
}
}
@@ -302,18 +309,6 @@
}
]
},
{
"root": "pages/user",
"pages": [
{
"path": "userRole",
"style": {
"navigationBarTitleText": "用户身份",
"disableScroll": true
}
}
]
},
{
"root": "pages/pay",
"pages": [
@@ -462,7 +457,7 @@
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "浦江小程序",
"navigationBarTitleText": "浦江智慧后勤h5",
"navigationBarBackgroundColor": "#1777FF",
"backgroundColor": "#ecf4f9",
"navigationStyle": "custom"
+2 -2
View File
@@ -1,9 +1,9 @@
<script setup lang="ts">
import { ref, reactive, nextTick } from "vue"
import { ref, reactive } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { isEmpty } from "radash"
import { hooks, router, constant } from "@@/index"
import { hooks, router } from "@@/index"
import { faceApi } from "@@/api"
import { filePath } from "@@/utils/file"
+151
View File
@@ -0,0 +1,151 @@
<template>
<view class="min-h-screen bg-white px-30rpx pb-30rpx">
<!-- 顶部导航 -->
<view class="flex items-center h-88rpx relative">
<view class="absolute left-0 h-88rpx flex items-center pr-20rpx" @tap="goBack">
<text class="text-36rpx text-gray-800">&lt;</text>
<text class="text-28rpx text-gray-800 ml-4rpx">返回</text>
</view>
<view class="flex-1 text-center text-34rpx font-500">问题反馈</view>
</view>
<!-- 问题分类 -->
<view class="mt-40rpx">
<view class="text-32rpx font-500 mb-24rpx">问题分类</view>
<view class="flex flex-wrap gap-24rpx">
<view
v-for="(item, index) in categories"
:key="index"
:class="[
'px-32rpx py-12rpx rounded-20rpx text-28rpx transition-colors duration-200',
selectedCategory === item ? 'bg-blue-200 text-blue-600' : 'bg-gray-100',
]"
@tap="selectedCategory = item"
>
{{ item }}
</view>
</view>
</view>
<!-- 详细描述 -->
<view class="mt-40rpx">
<view class="text-32rpx font-500 mb-24rpx">
详细描述
<text class="text-red-500 ml-8rpx">*</text>
</view>
<view class="w-full bg-gray-50 rounded-16rpx p-20rpx box-border">
<textarea
v-model="description"
placeholder="您在哪个页面,遇到了什么问题,详细描述有助于帮您快速解决"
placeholder-style="color: #999999;"
:maxlength="200"
class="w-full h-240rpx bg-gray-50 text-28rpx leading-40rpx p-0 box-border"
/>
<view class="text-right text-gray-400 text-24rpx mt-8rpx">{{ description.length }}/200</view>
</view>
</view>
<!-- 图片上传 -->
<view class="mt-40rpx">
<view class="text-32rpx font-500 mb-24rpx">上传图片 ({{ images.length }}/3)</view>
<view class="flex flex-wrap gap-24rpx">
<view v-for="(img, index) in images" :key="index" class="relative w-160rpx h-160rpx">
<image :src="img" mode="aspectFill" class="w-full h-full rounded-16rpx" />
<view
class="absolute -right-12rpx -top-12rpx w-40rpx h-40rpx bg-black bg-opacity-60 text-white rounded-full flex items-center justify-center active:bg-opacity-80"
@tap="deleteImage(index)"
>
×
</view>
</view>
<view
v-if="images.length < 3"
class="w-160rpx h-160rpx rounded-16rpx border border-dashed border-gray-200 flex items-center justify-center active:bg-gray-50"
@tap="handleUpload"
>
<view class="text-48rpx text-gray-400">+</view>
</view>
</view>
</view>
<!-- 联系方式 -->
<view class="mt-40rpx">
<view class="text-32rpx font-500 mb-24rpx">联系方式</view>
<view class="w-full bg-gray-50 rounded-16rpx px-20rpx box-border">
<input
v-model="contact"
placeholder="手机号/邮箱/微信号"
placeholder-style="color: #999999;"
class="w-full h-88rpx text-28rpx bg-gray-50"
/>
</view>
</view>
<!-- 邮箱联系我们 -->
<view class="mt-48rpx flex items-center justify-between text-28rpx">
<view class="text-gray-800">邮箱联系我们</view>
<view class="flex items-center gap-16rpx">
<text class="text-gray-600">user_service@36kr.com</text>
<view class="px-24rpx py-8rpx bg-gray-100 rounded-8rpx text-28rpx active:bg-gray-200" @tap="copyEmail"> 复制 </view>
</view>
</view>
<!-- 提交按钮 -->
<view
class="my-64rpx h-88rpx bg-blue-500 text-white rounded-20rpx flex items-center justify-center text-32rpx active:bg-blue-600 transition-colors duration-200"
@tap="handleSubmit"
>
提 交
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from "vue"
const categories = ["产品建议", "功能Bug", "内容订正", "其他"]
const selectedCategory = ref("产品建议")
const description = ref("")
const images = ref<string[]>([])
const contact = ref("")
const goBack = () => {
uni.navigateBack()
}
const handleUpload = () => {
uni.chooseImage({
count: 3 - images.value.length,
success: (res) => {
images.value = [...images.value, ...res.tempFilePaths]
},
})
}
const deleteImage = (index: number) => {
images.value.splice(index, 1)
}
const copyEmail = () => {
uni.setClipboardData({
data: "user_service@36kr.com",
success: () => {
uni.showToast({
title: "复制成功",
icon: "none",
})
},
})
}
const handleSubmit = () => {
if (!description.value) {
uni.showToast({
title: "请填写详细描述",
icon: "none",
})
return
}
// 实现提交逻辑
}
</script>
-337
View File
@@ -1,337 +0,0 @@
<template>
<view class="feedback-page">
<!-- 顶部导航 -->
<view class="header">
<view class="back-btn" @tap="goBack">
<text class="back-icon">&lt;</text>
<text class="back-text">返回</text>
</view>
<view class="title">其它问题</view>
</view>
<!-- 问题分类 -->
<view class="category-section">
<view class="section-title">问题分类</view>
<view class="category-list">
<view
v-for="(item, index) in categories"
:key="index"
:class="['category-item', { active: selectedCategory === item }]"
@tap="selectedCategory = item"
>
{{ item }}
</view>
</view>
</view>
<!-- 详细描述 -->
<view class="description-section">
<view class="section-title">
详细描述
<text class="required">*</text>
</view>
<view class="textarea-wrap">
<textarea
v-model="description"
placeholder="您在哪个页面,遇到了什么问题,详细描述有助于帮您快速解决"
placeholder-style="color: #999999;"
:maxlength="200"
class="feedback-textarea"
></textarea>
<view class="word-count">{{ description.length }}/200</view>
</view>
</view>
<!-- 图片上传 -->
<view class="upload-section">
<view class="section-title">上传图片 ({{ images.length }}/3)</view>
<view class="upload-area">
<view
v-for="(img, index) in images"
:key="index"
class="image-item"
>
<image :src="img" mode="aspectFill"/>
<view class="delete-btn" @tap="deleteImage(index)">×</view>
</view>
<view
v-if="images.length < 3"
class="upload-btn"
@tap="handleUpload"
>
<view class="plus">+</view>
</view>
</view>
</view>
<!-- 联系方式 -->
<!-- <view class="contact-section">-->
<!-- <view class="section-title">联系方式</view>-->
<!-- <view class="input-wrap">-->
<!-- <input -->
<!-- v-model="contact"-->
<!-- placeholder="手机号/邮箱/微信号"-->
<!-- placeholder-style="color: #999999;"-->
<!-- class="contact-input"-->
<!-- />-->
<!-- </view>-->
<!-- </view>-->
<!-- &lt;!&ndash; 邮箱联系 &ndash;&gt;-->
<!-- <view class="email-contact">-->
<!-- <view class="contact-text">联系我们</view>-->
<!-- <view class="email">-->
<!-- user_service@36kr.com-->
<!-- <view class="copy-btn" @tap="copyEmail">复制</view>-->
<!-- </view>-->
<!-- </view>-->
<!-- 提交按钮 -->
<view class="submit-btn" @tap="handleSubmit">提交</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const categories = ['产品建议', '功能Bug', '内容订正', '其他']
const selectedCategory = ref('产品建议')
const description = ref('')
const images = ref([])
const contact = ref('')
const goBack = () => {
uni.navigateBack()
}
const handleUpload = () => {
uni.chooseImage({
count: 3 - images.value.length,
success: (res) => {
images.value = [...images.value, ...res.tempFilePaths]
}
})
}
const deleteImage = (index: number) => {
images.value.splice(index, 1)
}
const copyEmail = () => {
uni.setClipboardData({
data: 'user_service@36kr.com',
success: () => {
uni.showToast({
title: '复制成功',
icon: 'none'
})
}
})
}
const handleSubmit = () => {
if (!description.value) {
uni.showToast({
title: '请填写详细描述',
icon: 'none'
})
return
}
// 实现提交逻辑
}
</script>
<style lang="scss" scoped>
.feedback-page {
padding: 0 30rpx;
min-height: 100vh;
background: #fff;
}
.header {
display: flex;
align-items: center;
height: 88rpx;
position: relative;
}
.back-btn {
position: absolute;
left: 0;
height: 88rpx;
display: flex;
align-items: center;
padding-right: 20rpx;
}
.back-icon {
font-size: 36rpx;
color: #333;
}
.back-text {
font-size: 28rpx;
color: #333;
margin-left: 4rpx;
}
.title {
flex: 1;
text-align: center;
font-size: 34rpx;
font-weight: 500;
}
.section-title {
margin: 40rpx 0 24rpx;
font-size: 32rpx;
font-weight: 500;
}
.required {
color: #ff4d4f;
margin-left: 8rpx;
}
.category-list {
display: flex;
flex-wrap: wrap;
gap: 24rpx;
}
.category-item {
padding: 12rpx 32rpx;
border-radius: 32rpx;
background: #f5f5f5;
font-size: 28rpx;
}
.category-item.active {
background: #e6f4ff;
color: #1677ff;
}
.textarea-wrap {
width: 100%;
background-color: #F8F8F8;
border-radius: 16rpx;
padding: 20rpx;
box-sizing: border-box;
}
.feedback-textarea {
width: 100%;
height: 240rpx;
background-color: #F8F8F8;
font-size: 28rpx;
line-height: 40rpx;
padding: 0;
box-sizing: border-box;
}
.word-count {
text-align: right;
color: #999;
font-size: 24rpx;
margin-top: 8rpx;
}
.upload-area {
display: flex;
flex-wrap: wrap;
gap: 24rpx;
}
.image-item, .upload-btn {
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
border: 1rpx dashed #e5e5e5;
position: relative;
}
.image-item image {
width: 100%;
height: 100%;
border-radius: 16rpx;
}
.upload-btn {
display: flex;
align-items: center;
justify-content: center;
}
.plus {
font-size: 48rpx;
color: #999;
}
.delete-btn {
position: absolute;
right: -12rpx;
top: -12rpx;
width: 40rpx;
height: 40rpx;
background: rgba(0, 0, 0, 0.6);
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.input-wrap {
width: 100%;
background-color: #F8F8F8;
border-radius: 16rpx;
padding: 0 20rpx;
box-sizing: border-box;
}
.contact-input {
width: 100%;
height: 88rpx;
font-size: 28rpx;
background-color: #F8F8F8;
}
.email-contact {
margin: 48rpx 0;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 28rpx;
}
.email {
display: flex;
align-items: center;
gap: 16rpx;
}
.copy-btn {
padding: 8rpx 24rpx;
background: #f5f5f5;
border-radius: 8rpx;
font-size: 28rpx;
}
.submit-btn {
margin: 64rpx 0;
height: 88rpx;
background: #1677ff;
color: #fff;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
}
/* 修改输入框的placeholder样式 */
::-webkit-input-placeholder {
color: #999999;
font-size: 28rpx;
}
</style>
+13 -8
View File
@@ -28,7 +28,7 @@
</view>
</tm-sheet>
<tm-button class="mt-1" :block="true" @click="disabled ? '' : handlePay()" :disabled="disabled">支付</tm-button>
<tm-button class="mt-1" :block="true" @click="disabled ? '' : handlePay()" :disabled="disabled">支 付</tm-button>
<numberKeyboard v-model="amount" />
</view>
@@ -38,15 +38,15 @@
<script setup lang="ts">
import { ref, computed, reactive } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { usePayStore } from "@/sheep/stores/pay"
import payApi from "@@/api/pay"
import { storePrice } from "@/sheep/utils/formatMoney"
import { router } from "@/sheep"
import { merchantApi, walletApi } from "@/sheep/api"
import numberKeyboard from "@/sheep/components/numberKeyboard.vue"
import { filePath } from "@/sheep/utils/file"
import { hooks } from "@@/index"
import { router } from "@/sheep"
import { filePath } from "@/sheep/utils/file"
import { storePrice } from "@/sheep/utils/formatMoney"
import { usePayStore } from "@/sheep/stores/pay"
import { isEmpty } from "radash"
import numberKeyboard from "@/sheep/components/numberKeyboard.vue"
const scanCodeData = reactive({
id: "",
@@ -69,8 +69,13 @@ onShow(async () => {
uni.showLoading({ title: "加载中...", mask: true })
if (scanCodeData.id) {
const { data } = await merchantApi.detail({ id: scanCodeData.id })
scanCodeData.name = data.name
disabled.value = false
if (data) {
scanCodeData.name = data.name
disabled.value = false
} else {
disabled.value = true
return uni.showToast({ title: "商户id错误", icon: "none" })
}
} else {
uni.showToast({ title: "参数不存在", icon: "none" })
disabled.value = true
+42 -59
View File
@@ -2,22 +2,22 @@
<view class="bg-gray-100 flex-col min-h-screen">
<view class="bg-white p-4 card mx-3 my-1 mt-2 flex items-center justify-between">
<view class="flex items-center">
<image class="w-64 h-64 mr-2" :src="filePath(transferMoney.sourceWalletType.walletTypeIcon)" />
<view>{{ transferMoney.sourceWalletType.walletTypeName }}</view>
<image class="w-64 h-64 mr-2" :src="filePath(payStore.sourceWalletData.walletTypeIcon)" />
<view>{{ payStore.sourceWalletData.walletTypeName }}</view>
</view>
<view class="flex items-center">
<view class="main-text-28">余额:</view>
<view class="main-text-28 mr-1">{{ hooks.useMoney(transferMoney.sourceWalletType.balance) + "元" }}</view>
<view class="main-text-28 mr-1">{{ hooks.useMoney(payStore.sourceWalletData.balance) + "元" }}</view>
</view>
</view>
<view class="bg-white p-4 card mx-3 my-1 flex items-center justify-between active:bg-[#f0f0f0]" @click="targetWalletSelect">
<view class="bg-white p-4 card mx-3 my-1 flex items-center justify-between active:bg-[#f0f0f0]" @click="show = true">
<view class="flex items-center">
<image class="w-64 h-64 rounded-full mr-2" src="@/static/page/money-cny-circle-fill.svg" />
<view>转入到</view>
</view>
<view class="flex items-center">
<view class="second-text-28 mr-1">{{ state.targetWalletTypeName }}</view>
<view class="second-text-28 mr-1">{{ targetWalletType.name }}</view>
<image class="w-38 h-38 rounded-full c-blueGray" src="@/static/page/arrow-right.svg" />
</view>
</view>
@@ -32,26 +32,24 @@
</view>
<!-- 金额限制提示 -->
<view class="h-30rpx">
<view v-if="storePrice(amount) > transferMoney.sourceWalletType?.balance" class="text-xs c-red px-8">
注:转账金额不能超过钱包余额
</view>
<view v-if="storePrice(amount) > payStore.sourceWalletData?.balance" class="text-xs c-red px-8">注:转账金额不能超过钱包余额</view>
</view>
<view class="p-4 font-bold mt-1">
<tm-button block @click="confirmTransfer" v-if="transferMoney.sourceWalletType.walletTypeId">确定转账</tm-button>
<tm-button block @click="confirmTransfer" v-if="payStore.sourceWalletData.walletTypeId">确定转账</tm-button>
</view>
<number-keyboard v-model="amount" />
<!-- 选择转入方式 -->
<tm-drawer v-model:show="show" title="选择钱包" size="50%" bg-color="#efefef" position="bottom" :show-footer="true" :showClose="true">
<tm-drawer v-model:show="show" title="选择钱包" size="30%" bg-color="#efefef" position="bottom" :show-footer="false" :showClose="true">
<view class="m-4">
<view
v-for="(v, index) in state.walletList"
v-for="(v, index) in wallet.walletList"
:key="index"
class="card mb-2 overflow-hidden relative border-4 border-solid border-white duration-200 flex justify-between items-center p-4"
:class="{ 'border-4 border-solid border-blue!': state.targetWalletTypeId == v.walletTypeId }"
@click="state.targetWalletTypeId = v.walletTypeId"
:class="{ 'border-4 border-solid border-blue!': targetWalletType.id == v.walletTypeId }"
@click="selectTargetWalletType(v)"
>
<view class="flex items-center">
<image class="w-70 h-70 mr-2" :src="filePath(v.walletTypeIcon)" />
@@ -63,12 +61,6 @@
<view class="main-text-32">余额:{{ hooks.useMoney(v.balance) + "元" }}</view>
</view>
</view>
<template #footer>
<view class="font-bold px-5">
<tm-button block @click="confirmTargetWallet">确 定</tm-button>
</view>
</template>
</tm-drawer>
</view>
</template>
@@ -81,46 +73,44 @@ import { payApi, walletApi } from "@/sheep/api"
import { hooks, router } from "@@/index"
import { filePath } from "@/sheep/utils/file"
import { storePrice } from "@/sheep/utils/formatMoney"
import { useTransferMoney } from "@/sheep/stores/transferMoney"
import { usePayStore } from "@/sheep/stores/pay"
const payStore = usePayStore()
const transferMoney = useTransferMoney()
const amount = ref("")
const show = ref(false)
const state = reactive<any>({
sourceWalletTypeId: "",
sourceWalletTypeName: "",
walletList: [],
targetWalletTypeId: "",
targetWalletTypeName: "",
const sourceWalletType = reactive({
id: "",
name: "",
})
const targetWalletType = reactive({
id: "",
name: "",
})
const wallet = reactive<any>({
walletList: [],
})
const curTargetWallet = ref()
onLoad(async () => {
if (!transferMoney.sourceWalletType.walletTypeId) return uni.showToast({ title: "钱包类型id不存在", icon: "none" })
state.sourceWalletTypeId = transferMoney.sourceWalletType.walletTypeId
state.sourceWalletTypeName = transferMoney.sourceWalletType.walletTypeName
uni.setNavigationBarTitle({ title: state.sourceWalletTypeName || "源钱包" })
if (!payStore.sourceWalletData.walletTypeId) return uni.showToast({ title: "钱包类型id不存在", icon: "none" })
sourceWalletType.id = payStore.sourceWalletData.walletTypeId
sourceWalletType.name = payStore.sourceWalletData.walletTypeName
uni.setNavigationBarTitle({ title: sourceWalletType.name || "源钱包" })
const res = await walletApi.all()
if (res.code == 0) {
state.walletList = res.data.records.filter((item: any) => item.walletTypeId != state.sourceWalletTypeId)
wallet.walletList = res.data.records.filter((item: any) => item.walletTypeId != sourceWalletType.id)
}
if (!curTargetWallet.value) {
curTargetWallet.value = state.walletList[0]
state.targetWalletTypeId = state.walletList[0]?.walletTypeId
state.targetWalletTypeName = state.walletList[0]?.walletTypeName
if (wallet.walletList.length) {
targetWalletType.id = wallet.walletList[0]?.walletTypeId
targetWalletType.name = wallet.walletList[0]?.walletTypeName
}
})
// 目标钱包选择
const targetWalletSelect = () => {
// state.sourceWalletTypeId = ""
show.value = true
}
// 确认目标钱包
const confirmTargetWallet = () => {
// state.sourceWalletTypeName = state.walletList.find((item: any) => item.walletTypeId != state.sourceWalletTypeId).walletTypeName
// 选择目标钱包
const selectTargetWalletType = (v: any) => {
targetWalletType.id = v.walletTypeId
targetWalletType.name = v.walletTypeName
show.value = false
}
@@ -153,14 +143,10 @@ const confirmTransfer = async () => {
if (!amount.value || amount.value === "0" || amount.value === "0." || parseFloat(amount.value) === 0) {
return uni.showToast({ title: "金额不能为零", icon: "none" })
}
if (numAmount.value > transferMoney.sourceWalletType.balance) {
if (numAmount.value > payStore.sourceWalletData.balance) {
return uni.showToast({ title: "转账金额不能超过钱包余额", icon: "none" })
}
// console.log("state.sourceWalletTypeId", state.sourceWalletTypeId)
// console.log("state.targetWalletTypeId", state.targetWalletTypeId)
// console.log("金额", numAmount.value)
uni.showModal({
title: "确定转入吗?",
success: async (res) => {
@@ -170,28 +156,25 @@ const confirmTransfer = async () => {
},
})
}
// 二次确认转账
const confirmTransferAgain = async () => {
try {
uni.showLoading({ title: "正在转入...", mask: true })
const res = await payApi.transferMoney({
sourceWalletTypeId: state.sourceWalletTypeId,
targetWalletTypeId: state.targetWalletTypeId,
sourceWalletTypeId: sourceWalletType.id,
targetWalletTypeId: targetWalletType.id,
balance: numAmount.value,
})
if (res.code == 0) {
uni.showToast({ title: "转账成功", icon: "success" })
transferMoney.setSourceWalletType({})
// router.jump("/pages/wallet/list", {}, 2)
payStore.setSourceWalletData({})
router.back()
} else {
uni.showToast({ title: res.msg, icon: "none" })
}
} catch (error: any) {
uni.showToast({
title: error.message,
icon: "none",
})
uni.showToast({ title: error.message, icon: "none" })
} finally {
uni.hideLoading()
}
+9 -6
View File
@@ -57,11 +57,10 @@ import { reactive } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { isEmpty } from "radash"
import { filePath } from "@@/utils/file"
import { getAppInfo } from "@@/utils/getAppInfo"
import { getAppInfo } from "@@/utils/app"
import { hooks, router, constant } from "@@/index"
import { userApi } from "@@/api"
import isDevEnv from "@/sheep/utils/isDevEnv"
// import dd from "gdt-jsapi"
import { isDevEnv } from "@/sheep/utils/app"
const state = reactive<{
wallet: any[]
@@ -83,7 +82,6 @@ const state = reactive<{
// { text: "跨单位就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/stride-list" },
{ text: "人脸管理", icon: "/static/icon/service_5.png", router: "/pages/human-face/list" },
{ text: "清除缓存", icon: "/static/icon/clear-storage.png", router: "" },
// { text: "意见反馈", icon: "/static/icon/service_5.png", router: "/pages/integrated/feedback/index" },
],
appInfo: "",
})
@@ -366,7 +364,7 @@ const scanCode = {
},
}
const onTabLogin = () => (isDevEnv() ? router.jump("/pages/test/login") : login.checkAppInfo())
const onTabLogin = () => (isDevEnv() ? router.jump("/pages/user/login") : login.checkAppInfo())
const onTabMenu = async (item: any, index: number) => {
const isLogin = await hooks.useCheckLogin()
@@ -400,13 +398,18 @@ const initMenuConfig = () => {
state.menuList.push({
text: "测试-登录",
icon: "/static/icon/login.png",
router: "/pages/test/login",
router: "/pages/user/login",
})
state.menuList.push({
text: "测试-角色选择",
icon: "/static/icon/login.png",
router: "/pages/user/userRole",
})
state.menuList.push({
text: "测试-意见反馈",
icon: "/static/icon/service_5.png",
router: "/pages/integrated/feedback",
})
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ import { orderApi } from "@@/api"
import { filePath } from "@@/utils/file"
import zTabs from "@@/components/other/z-tabs/z-tabs.vue"
import getLaunchParam from "@/sheep/utils/getLaunchParam"
import { getLaunchParam } from "@/sheep/utils/app"
const data = {
tabs: [
+1 -1
View File
@@ -23,7 +23,7 @@ onLoad(() => {
app._initTabbar()
})
onShow(() => {
// pageRef.value && pageRef.value.reload(false)
pageRef.value && pageRef.value.reload(false)
})
const toDetail = async (v: any) => {
-1
View File
@@ -20,7 +20,6 @@
<view class="flex-1 flex-col m-l-20rpx">
<view class="sub-text-34">
<text class="mr-2">{{ v.name }}</text>
<!-- <tm-tag v-if="v.supType == '10' && Number(v.mchCount) > 0" size="xs" skin="thin">共{{ v.mchCount }}家</tm-tag> -->
</view>
<view class="m-t-10rpx">
<view class="flex justify-between items-center">
File renamed without changes.
+1 -1
View File
@@ -54,7 +54,7 @@
<script setup lang="ts">
import { ref } from "vue"
import { userApi } from "@/sheep/api"
import getLaunchParam from "@/sheep/utils/getLaunchParam"
import { getLaunchParam } from "@/sheep/utils/app"
const userRole = ref("user")
-23
View File
@@ -1,23 +0,0 @@
<script setup lang="ts">
import { ref, reactive } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { hooks } from "@@/index"
import { foodApi } from "@@/api"
const data = {}
const state = reactive<any>({
pa: {
status: 1,
},
})
onLoad(() => {})
onShow(() => {})
</script>
<template>
<crud-layout title="订单详情" bg="/static/page/blue-bg.png"> </crud-layout>
</template>
<style lang="scss" scoped></style>
-2
View File
@@ -85,5 +85,3 @@ const doTab = (e: any) => {
</z-paging>
</crud-layout>
</template>
<style lang="scss" scoped></style>
+2 -2
View File
@@ -7,7 +7,7 @@ import { hooks, router } from "@@/index"
import { walletApi } from "@@/api"
import { filePath } from "@@/utils/file"
import { useTransferMoney } from "@/sheep/stores/transferMoney"
import { usePayStore } from "@/sheep/stores/pay"
const state = reactive<any>({
showBindDialog: false,
@@ -129,7 +129,7 @@ const doFreeze = async () => {
// 转账
const doTransferMoney = (v: any) => {
if (v.walletTypeId) {
useTransferMoney().setSourceWalletType(v)
usePayStore().setSourceWalletData(v)
router.jump("/pages/pay/transferMoney")
return
}
File renamed without changes.
@@ -7,10 +7,10 @@ const showVerifyDeviceImpModel = (onClose: () => any = () => null) => {
return new Promise((resolve, reject) => {
if (VerifyDeviceImpModelInstance) return
const close = () => {
const close = async () => {
render(null, document.body)
VerifyDeviceImpModelInstance = false
onClose()
await onClose()
}
const vnode = h(VerifyDeviceImpModel, {
-3
View File
@@ -1,3 +0,0 @@
export default {
logo: "/static/constant/logo.png"
}
+2 -4
View File
@@ -1,5 +1,3 @@
export { default as imgConstant } from "./img-constant";
export { default as serviceEnum } from "./service-enum"
export { default as serviceEnum } from "./service-enum";
export { default as storageConstant } from "./storage-constant";
export { default as storageConstant } from "./storage-constant"
+2 -2
View File
@@ -1,6 +1,6 @@
import storageConstant from "../constant/storage-constant"
import { jump } from "../router/index"
import isDevEnv from "../utils/isDevEnv"
import { isDevEnv } from "../utils/app"
export const useToken = () => {
const tokenKey = storageConstant.TOKEN
@@ -41,7 +41,7 @@ export const useCheckLogin = async (): Promise<boolean> => {
confirmText: "去登录",
success: async (res) => {
if (res.confirm) {
jump("/pages/test/login", {}, 3)
jump("/pages/user/login", {}, 3)
return
}
if (!res.confirm) {
-5
View File
@@ -1,8 +1,3 @@
// https://pinia.vuejs.org/
import { createPinia } from "pinia"
// 创建
const pinia = createPinia()
// 导出
export default pinia
+7 -1
View File
@@ -1,14 +1,20 @@
import { defineStore } from "pinia"
export const usePayStore = defineStore("pay", {
state: (): { payData: { id?: string; name?: string; customPrice?: number } } => {
state: (): { payData: { id?: string; name?: string; customPrice?: number }; sourceWalletData: any } => {
return {
payData: {},
sourceWalletData: {},
}
},
actions: {
// 支付
setPayData(data: any) {
this.payData = data
},
// 转账
setSourceWalletData(data: any) {
this.sourceWalletData = data
},
},
})
-14
View File
@@ -1,14 +0,0 @@
import { defineStore } from "pinia"
export const useTransferMoney = defineStore("transferMoney", {
state: (): any => {
return {
sourceWalletType: {},
}
},
actions: {
setSourceWalletType(data: any) {
this.sourceWalletType = data
},
},
})
+26
View File
@@ -0,0 +1,26 @@
// app运行环境
// export default () => import.meta.env.VITE_DEV_ENV === "dev"
export const isDevEnv = (): boolean => process.env.NODE_ENV === "development"
// 获取启动参数
export const getLaunchParam = (): "canteen" | "selfMerchant" | "coopMerchant" | "thirdApp" | null => {
return new URL(window.location.href).searchParams.get("launchParam") as "canteen" | "selfMerchant" | "coopMerchant" | "thirdApp" | null
}
// 检测app信息判断运行环境
export const getAppInfo = async (): Promise<string> => {
try {
const res = await (window as any).CP2.getAppInfo()
// 浙政钉环境
if (res.code === 0) {
console.log("环境:zzd")
return "zzd"
} else {
// 诗画环境
console.log("环境:sh")
return "sh"
}
} catch (error) {
return "unknown"
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
import isDevEnv from "./isDevEnv"
import { isDevEnv } from "./app"
export const filePath = (path?: string) => {
const imageBaseUrl = isDevEnv() ? import.meta.env.VITE_IMAGE_DEV_PATH : import.meta.env.VITE_APP_PROD_IMAGE
-17
View File
@@ -1,17 +0,0 @@
// 检测app信息判断运行环境
export const getAppInfo = async (): Promise<string> => {
try {
const res = await (window as any).CP2.getAppInfo()
// 浙政钉环境
if (res.code === 0) {
console.log("环境:zzd")
return "zzd"
} else {
// 诗画环境
console.log("环境:sh")
return "sh"
}
} catch (error) {
return "unknown"
}
}
-6
View File
@@ -1,6 +0,0 @@
// 获取启动参数
const getLaunchParam = (): "canteen" | "selfMerchant" | "coopMerchant" | "thirdApp" | null => {
return new URL(window.location.href).searchParams.get("launchParam") as "canteen" | "selfMerchant" | "coopMerchant" | "thirdApp" | null
}
export default getLaunchParam
-2
View File
@@ -1,2 +0,0 @@
// export default () => import.meta.env.VITE_DEV_ENV === "dev"
export default () => process.env.NODE_ENV === "development"
+114 -45
View File
@@ -2,12 +2,33 @@ import Request from "luch-request"
import { router, constant } from "@@/index"
import { userApi } from "../api"
import { isEmpty } from "radash"
import isDevEnv from "./isDevEnv"
import { getAppInfo } from "@@/utils/getAppInfo"
import { isDevEnv } from "./app"
import { getAppInfo } from "@@/utils/app"
import showVerifyDeviceImpModel from "../components/verifyDeviceImpModel/verifyDeviceImpModel"
const TOKEN_KEY = constant.storageConstant.TOKEN
const USER_INFO_KEY = constant.storageConstant.USERINFO
const SKIP_AUTH_URLS = ["/thirdparty/thirdPartyOAth2", "/sys/auth/refresh-token", "/auth/oauth2/token"]
let isRefreshing = false
// 队列存储发送失败的请求
let failedQueue: any[] = []
// 存储刷新token的Promise
let refreshTokenPromise: Promise<any> | null = null
// 处理队列中的请求
const processQueue = (error: any, token: any = null) => {
failedQueue.forEach((prom) => {
if (error) {
prom.reject(error)
} else {
prom.resolve(token)
}
})
if (!error) {
failedQueue = []
}
}
// 读取token并处理Authorization请求头
const getAuthHeader = (tokenStorage: any) => {
@@ -20,7 +41,7 @@ const getAuthHeader = (tokenStorage: any) => {
// 浙政钉登录 登录成功返回true,否则返回false
async function handleZzdLogin(): Promise<boolean> {
try {
uni.showLoading({ title: "加载中...", mask: true })
uni.showLoading({ title: "登录中...", mask: true })
const token = new URL(window.location.href).searchParams.get("uabaseToken")
if (!token) {
@@ -57,15 +78,15 @@ async function handleShLogin(): Promise<boolean> {
try {
uni.showLoading({ title: "登录中...", mask: true })
// const userInfo = await (window as any).CP2.getUserInfo()
// if (userInfo.code == 0 || !userInfo.data.accountId || !userInfo.data.sessionId) {
// uni.showToast({ title: "获取诗画用户信息失败", icon: "none" })
// return false
// }
const userInfo = await (window as any).CP2.getUserInfo()
if (userInfo.code == 0 || !userInfo.data.accountId || !userInfo.data.sessionId) {
uni.showToast({ title: "获取诗画用户信息失败", icon: "none" })
return false
}
// const { accountId, sessionId } = userInfo.data
// const response = await userApi.authsh({ accountId, sessionId })
const response = await userApi.authsh({ accountId: "67e790d6a95d5a7b507e53dd", sessionId: "68215b03a95d5a4899d20cb4" }) // 测试
const { accountId, sessionId } = userInfo.data
const response = await userApi.authsh({ accountId, sessionId })
// const response = await userApi.authsh({ accountId: "67e790d6a95d5a7b507e53dd", sessionId: "68215b03a95d5a4899d20cb4" }) // 测试
if (response.code == 0) {
uni.setStorageSync(TOKEN_KEY, { accessToken: response.data.accessToken })
@@ -81,13 +102,9 @@ async function handleShLogin(): Promise<boolean> {
return false
}
// 如果是设备未验证,保存响应数据
if (response.data == -1) {
uni.showToast({ title: "您的设备需要信任验证", icon: "none" })
await showVerifyDeviceImpModel(async () => {
await autoLogin()
uni.reLaunch({ url: window.location.href.split("#")[1] })
})
return false
uni.setStorageSync(TOKEN_KEY, { data: -1 })
}
uni.showToast({ title: response.msg || "登录失败", icon: "none" })
@@ -118,6 +135,56 @@ async function autoLogin(): Promise<boolean> {
}
}
// token刷新
async function refreshToken(): Promise<any> {
if (refreshTokenPromise) {
return refreshTokenPromise
}
if (!isRefreshing) {
isRefreshing = true
refreshTokenPromise = (async () => {
try {
const loginResult = await autoLogin()
if (loginResult) {
const newTokenStorage = uni.getStorageSync(TOKEN_KEY)
processQueue(null, newTokenStorage)
return newTokenStorage
} else {
// 检查是否需要设备验证
const tokenStorage = uni.getStorageSync(TOKEN_KEY)
if (tokenStorage?.data == -1) {
// 显示设备验证弹窗
await showVerifyDeviceImpModel(async () => {
// 验证完成后重新登录
const newLoginResult = await autoLogin()
if (newLoginResult) {
const newTokenStorage = uni.getStorageSync(TOKEN_KEY)
processQueue(null, newTokenStorage)
return newTokenStorage
} else {
processQueue(new Error("登录失败"), null)
throw new Error("登录失败")
}
})
} else {
processQueue(new Error("登录失败"), null)
throw new Error("登录失败")
}
}
} catch (error) {
processQueue(error, null)
throw error
} finally {
isRefreshing = false
refreshTokenPromise = null
}
})()
}
return refreshTokenPromise
}
// token过期刷新检查
async function checkAndRefreshToken(tokenStorage: any) {
const { accessToken } = tokenStorage
@@ -125,13 +192,9 @@ async function checkAndRefreshToken(tokenStorage: any) {
const expiresDate = new Date(accessToken.expiresAt).getTime()
// console.log("过期时间", expiresDate)
// console.log("现在时间", new Date().getTime())
// console.log("现在时间大于过期时间吗", new Date().getTime() >= expiresDate)
// token已过期,尝试刷新
if (new Date().getTime() >= expiresDate) {
await autoLogin()
return await refreshToken()
}
}
@@ -157,8 +220,6 @@ const http = new Request({
// 请求拦截器
http.interceptors.request.use(async (config) => {
// console.log("config", config)
const tokenStorage = uni.getStorageSync(TOKEN_KEY)
const config_header: any = config.header || {}
@@ -166,25 +227,25 @@ http.interceptors.request.use(async (config) => {
config_header["TENANT-ID"] = "1"
if (import.meta.env.VITE_IS_TOC === "true") config_header["CLIENT-TOC"] = "Y"
// 不需要设置认证头的接口路径
const skipAuthUrls = ["/thirdparty/thirdPartyOAth2", "/sys/auth/refresh-token", "/auth/oauth2/token"]
// 判断是否需要设置认证头
if (!skipAuthUrls.some((url) => config.url?.startsWith(url))) {
if (!SKIP_AUTH_URLS.some((url) => config.url?.startsWith(url))) {
if (!isEmpty(tokenStorage)) {
// token存在,token过期刷新检查
await checkAndRefreshToken(tokenStorage)
const authHeader = getAuthHeader(tokenStorage)
if (authHeader) config_header.Authorization = authHeader
const newTokenStorage = await checkAndRefreshToken(tokenStorage)
if (newTokenStorage) {
const authHeader = getAuthHeader(newTokenStorage)
if (authHeader) config_header.Authorization = authHeader
} else {
const authHeader = getAuthHeader(tokenStorage)
if (authHeader) config_header.Authorization = authHeader
}
} else {
// token不存在,则发起登录
await autoLogin()
// 登录后重新获取token
const newTokenStorage = uni.getStorageSync(TOKEN_KEY)
const authHeader = getAuthHeader(newTokenStorage)
if (authHeader) config_header.Authorization = authHeader
// token不存在,刷新token
const newTokenStorage = await refreshToken()
if (newTokenStorage) {
const authHeader = getAuthHeader(newTokenStorage)
if (authHeader) config_header.Authorization = authHeader
}
}
}
@@ -194,8 +255,6 @@ http.interceptors.request.use(async (config) => {
// 响应拦截器
http.interceptors.response.use(
async (response) => {
// console.log("response", response)
// 账号密码登录响应结果特殊处理
if (response.config.url === "/auth/oauth2/token") {
return Promise.resolve({
@@ -208,8 +267,6 @@ http.interceptors.response.use(
return Promise.resolve(response.data)
},
async (error) => {
// console.log("error", error)
// 处理 424 错误(token过期)
if (error.statusCode === 424) {
if (isDevEnv()) {
@@ -217,9 +274,21 @@ http.interceptors.response.use(
uni.removeStorageSync(TOKEN_KEY)
uni.removeStorageSync(USER_INFO_KEY)
uni.showToast({ title: "登录已过期,请重新登录", icon: "none" })
router.jump("/pages/test/login")
router.jump("/pages/user/login")
} else {
await autoLogin()
try {
const newTokenStorage = await refreshToken()
if (newTokenStorage) {
// 重新发起失败的请求
const config = error.config
const authHeader = getAuthHeader(newTokenStorage)
if (authHeader && config.header) config.header.Authorization = authHeader
return http.request(config)
}
} catch (err) {
// 错误已在refreshToken中处理
return Promise.reject(err)
}
}
}