第一次上传
1
.svn/entries
Normal file
@@ -0,0 +1 @@
|
||||
12
|
||||
1
.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
12
|
||||
@@ -0,0 +1,143 @@
|
||||
{
|
||||
"name" : "科讯代购",
|
||||
"appid" : "__UNI__06C2D6A",
|
||||
"description" : "",
|
||||
"versionName" : "1.0.8",
|
||||
"versionCode" : 108,
|
||||
"transformPx" : false,
|
||||
/* 5+App特有相关 */
|
||||
"app-plus" : {
|
||||
"compatible" : {
|
||||
"ignoreVersion" : true //true表示忽略版本检查提示框,HBuilderX1.9.0及以上版本支持
|
||||
},
|
||||
"usingComponents" : true,
|
||||
"nvueStyleCompiler" : "uni-app",
|
||||
"compilerVersion" : 3,
|
||||
"splashscreen" : {
|
||||
"alwaysShowBeforeRender" : false,
|
||||
"waiting" : false,
|
||||
"autoclose" : true,
|
||||
"delay" : 0
|
||||
},
|
||||
/* 模块配置 */
|
||||
"modules" : {
|
||||
"Barcode" : {},
|
||||
"Maps" : {},
|
||||
"Geolocation" : {}
|
||||
},
|
||||
/* 应用发布信息 */
|
||||
"distribute" : {
|
||||
/* android打包配置 */
|
||||
"android" : {
|
||||
"permissions" : [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
],
|
||||
"minSdkVersion" : 25,
|
||||
"targetSdkVersion" : 25,
|
||||
"abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ]
|
||||
},
|
||||
/* ios打包配置 */
|
||||
"ios" : {
|
||||
"idfa" : false,
|
||||
"dSYMs" : false
|
||||
},
|
||||
/* SDK配置 */
|
||||
"sdkConfigs" : {
|
||||
"ad" : {},
|
||||
"maps" : {
|
||||
"amap" : {
|
||||
"name" : "amapZAvZjTHj",
|
||||
"appkey_ios" : "3caf9e6f01b0085be1e75e0d0e281fe7",
|
||||
"appkey_android" : "3caf9e6f01b0085be1e75e0d0e281fe7"
|
||||
}
|
||||
},
|
||||
"geolocation" : {
|
||||
"amap" : {
|
||||
"name" : "amapZAvZjTHj",
|
||||
"__platform__" : [ "android" ],
|
||||
"appkey_ios" : "",
|
||||
"appkey_android" : "3caf9e6f01b0085be1e75e0d0e281fe7"
|
||||
}
|
||||
}
|
||||
},
|
||||
"icons" : {
|
||||
"android" : {
|
||||
"hdpi" : "unpackage/res/icons/72x72.png",
|
||||
"xhdpi" : "unpackage/res/icons/96x96.png",
|
||||
"xxhdpi" : "unpackage/res/icons/144x144.png",
|
||||
"xxxhdpi" : "unpackage/res/icons/192x192.png"
|
||||
},
|
||||
"ios" : {
|
||||
"appstore" : "unpackage/res/icons/1024x1024.png",
|
||||
"ipad" : {
|
||||
"app" : "unpackage/res/icons/76x76.png",
|
||||
"app@2x" : "unpackage/res/icons/152x152.png",
|
||||
"notification" : "unpackage/res/icons/20x20.png",
|
||||
"notification@2x" : "unpackage/res/icons/40x40.png",
|
||||
"proapp@2x" : "unpackage/res/icons/167x167.png",
|
||||
"settings" : "unpackage/res/icons/29x29.png",
|
||||
"settings@2x" : "unpackage/res/icons/58x58.png",
|
||||
"spotlight" : "unpackage/res/icons/40x40.png",
|
||||
"spotlight@2x" : "unpackage/res/icons/80x80.png"
|
||||
},
|
||||
"iphone" : {
|
||||
"app@2x" : "unpackage/res/icons/120x120.png",
|
||||
"app@3x" : "unpackage/res/icons/180x180.png",
|
||||
"notification@2x" : "unpackage/res/icons/40x40.png",
|
||||
"notification@3x" : "unpackage/res/icons/60x60.png",
|
||||
"settings@2x" : "unpackage/res/icons/58x58.png",
|
||||
"settings@3x" : "unpackage/res/icons/87x87.png",
|
||||
"spotlight@2x" : "unpackage/res/icons/80x80.png",
|
||||
"spotlight@3x" : "unpackage/res/icons/120x120.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/* 快应用特有相关 */
|
||||
"quickapp" : {},
|
||||
/* 小程序特有相关 */
|
||||
"mp-weixin" : {
|
||||
"appid" : "wx48366fb1ba81d1ea",
|
||||
"setting" : {
|
||||
"urlCheck" : false
|
||||
},
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-alipay" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-baidu" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-toutiao" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"uniStatistics" : {
|
||||
"enable" : false
|
||||
},
|
||||
"vueVersion" : "3",
|
||||
"h5" : {
|
||||
"sdkConfigs" : {
|
||||
"maps" : {
|
||||
"qqmap" : {
|
||||
"key" : "7DIBZ-K4HCJ-ZR2FE-FOOOP-SALFT-RLFYW"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script>
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||
CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
</script>
|
||||
<title></title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<view style="padding: 20rpx;">
|
||||
<view class="" v-for="(item,index) in noticeList " :key="index" @click="Service.GoPage('/pages/article/articleCom?noticeId='+item.noticeId)"
|
||||
style=" padding: 20rpx; margin-top: 20rpx; border-radius: 20rpx; box-shadow: 0 0 10rpx 4rpx #e2e2e2; ">
|
||||
<view class="" style="display: flex; align-items: center; ">
|
||||
<view class="tag" v-if="index==0"
|
||||
style=" color: #fff; border-radius: 12rpx; background-color: #FF6B35; padding: 4rpx 20rpx; ">
|
||||
<img :src="Service.GetIconImg('/static/index/community/top.png')"
|
||||
style="width: 30rpx; height: 30rpx; " alt="" />
|
||||
<text style="margin-left: 10rpx; font-size: 24rpx;">置顶</text>
|
||||
</view>
|
||||
<view class="tag" v-else
|
||||
style=" color: #fff; border-radius: 12rpx; background-color: #FF6B35; padding: 4rpx 20rpx; ">
|
||||
<img :src="Service.GetIconImg('/static/index/community/top.png')"
|
||||
style="width: 30rpx; height: 30rpx; " alt="" />
|
||||
<text style="margin-left: 10rpx; font-size: 24rpx;">{{item.sign}}</text>
|
||||
</view>
|
||||
<text style="font-size: 32rpx; font-weight: 600; margin-left: 15rpx; ">{{item.title}}</text>
|
||||
</view>
|
||||
<view class=""
|
||||
style=" margin: 16rpx 0; color: #666666; font-size: 26rpx; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; ">
|
||||
{{item.remark}}
|
||||
</view>
|
||||
<view class="" style=" font-size: 22rpx; color: #999999; display: flex; align-items: center;">
|
||||
<up-icon name="clock" size="12" color='#999999' ></up-icon>
|
||||
<text style="margin-left: 10rpx;" >{{Service.formatDate(item.addTime,2)}}</text>
|
||||
<!-- <text style="margin: 0 15rpx;">·</text>
|
||||
<view class="" style="display: flex;align-items: center;">
|
||||
<img :src="Service.GetIconImg('/static/index/community/see.png')"
|
||||
style="width: 30rpx; height: 30rpx; " alt="" />
|
||||
<text style="margin-left: 10rpx;">1200人阅读</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<up-loadmore :status="status" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad, onReachBottom } from "@dcloudio/uni-app";
|
||||
import { Service } from "@/Service/Service"
|
||||
import { ref } from "vue";
|
||||
import { vpCommunityService } from '@/Service/vp/vpCommunityService'
|
||||
|
||||
interface notice{
|
||||
addTime:string
|
||||
noticeId:string
|
||||
remark:string
|
||||
sign:string
|
||||
title:string
|
||||
}
|
||||
|
||||
let status = ref('nomore')
|
||||
let page=ref(1)
|
||||
let noticeList=ref<notice[]>([])
|
||||
let comId=ref()
|
||||
onLoad((data:any) => {
|
||||
comId.value=data.comId
|
||||
getData()
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
|
||||
onReachBottom(()=>{
|
||||
getList()
|
||||
})
|
||||
|
||||
const getData=()=>{
|
||||
status.value='loadmore'
|
||||
page.value=1
|
||||
noticeList.value=[]
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList=()=>{
|
||||
if (status.value !== 'loadmore') {
|
||||
return
|
||||
}
|
||||
status.value = 'loading'
|
||||
vpCommunityService.GetCommunityNoticeList(comId.value,page.value).then(res=>{
|
||||
noticeList.value=[...noticeList.value,...res.data.noticeList]
|
||||
status.value = res.data.noticeList.length == 10 ? 'loadmore' : 'nomore'
|
||||
page.value++
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: fit-content;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {parent, children} from '../common/relation';
|
||||
export default {
|
||||
name: 'lime-painter-image',
|
||||
mixins:[children('painter')],
|
||||
props: {
|
||||
id: String,
|
||||
css: [String, Object],
|
||||
src: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
type: 'image',
|
||||
el: {
|
||||
css: {},
|
||||
src: null
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,442 @@
|
||||
<template>
|
||||
<view class="member-code-page">
|
||||
<!-- 沉浸式状态栏 -->
|
||||
<view class="status-bar"></view>
|
||||
|
||||
<!-- 顶部导航 -->
|
||||
<view class="nav-bar">
|
||||
<image class="back-icon" src="/static/icons/back.svg" @click="Service.GoPageBack()" mode="aspectFit" />
|
||||
<text class="nav-title">会员码</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<view class="content">
|
||||
<!-- 会员卡片 -->
|
||||
<view class="member-card">
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-section">
|
||||
<image class="user-avatar" :src="Service.GetMateUrlByImg(userData.headImg)" mode="aspectFill" />
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{ userData.nick }}</text>
|
||||
<!-- ID和会员等级标签在一排 -->
|
||||
<view class="tags-row">
|
||||
<view class="user-id-tag" @click="copyMemberId">
|
||||
<text class="ri-vip-crown-2-fill id-icon"></text>
|
||||
<text class="id-text">ID: {{ userData.userNo }}</text>
|
||||
<text class="ri-file-copy-line id-copy"></text>
|
||||
</view>
|
||||
<!-- <view class="user-member-tag">
|
||||
<text class="ri-vip-crown-fill member-icon"></text>
|
||||
<text class="member-text">黄金会员</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<view class="divider"></view>
|
||||
|
||||
<!-- 条形码区域 -->
|
||||
<view class="barcode-section">
|
||||
<u-barcode :value="code" :displayValue='false' :width="300" :height="100" />
|
||||
<text class="barcode-number">{{ code }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 二维码区域 -->
|
||||
<view class="qrcode-section">
|
||||
<view class="qrcode-wrapper" >
|
||||
<view class="qrcode-placeholder">
|
||||
<up-qrcode cid="ex1" :size="180" :val="erCode"></up-qrcode>
|
||||
</view>
|
||||
</view>
|
||||
<text class="code-tip">向商家出示会员码,扫码享受积分优惠</text>
|
||||
</view>
|
||||
|
||||
<!-- 刷新提示 -->
|
||||
<view class="refresh-section">
|
||||
<text class="ri-time-line refresh-icon"></text>
|
||||
<text class="refresh-text">{{ refreshCountdown }}秒后自动刷新</text>
|
||||
<view class="refresh-btn" @click="refreshCode">
|
||||
<text class="ri-refresh-line btn-icon"></text>
|
||||
<text class="btn-text">刷新</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 温馨提示 -->
|
||||
<view class="tips-section">
|
||||
<view class="tips-title">
|
||||
<text class="ri-lightbulb-line tips-icon"></text>
|
||||
<text class="title-text">温馨提示</text>
|
||||
</view>
|
||||
<view class="tips-list">
|
||||
<text class="tips-item">• 会员码每分钟自动更新,确保使用安全</text>
|
||||
<text class="tips-item">• 结账时请向商家出示此码</text>
|
||||
<text class="tips-item">• 消费可获得积分,积分可抵扣现金</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { vpUserService, Service } from '@/Service/vp/vpUserService'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
// 用户数据
|
||||
const user = ref({
|
||||
nickname: '美食达人',
|
||||
avatar: 'https://picsum.photos/200/200?random=100',
|
||||
points: 2580,
|
||||
memberLevel: 'gold',
|
||||
memberId: '8888888',
|
||||
memberCode: 'VIP8888888888888'
|
||||
})
|
||||
let code = ref('')
|
||||
let erCode = ref('')
|
||||
|
||||
let userData = ref<any>({})
|
||||
|
||||
// 刷新倒计时(秒)
|
||||
const refreshCountdown = ref(60)
|
||||
let refreshTimer = ref(0)
|
||||
let countdownTimer = ref(0)
|
||||
|
||||
|
||||
onLoad(() => {
|
||||
fetchUserInfo()
|
||||
getCode()
|
||||
startAutoRefresh()
|
||||
})
|
||||
|
||||
// 获取用户信息
|
||||
const fetchUserInfo = () => {
|
||||
vpUserService.GetUserInfo().then(res => {
|
||||
if (res.code == 0) {
|
||||
userData.value = res.data.userInfo
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const getCode=()=>{
|
||||
vpUserService.GetUserCode().then(res => {
|
||||
if (res.code==0){
|
||||
code.value=res.data.code
|
||||
erCode.value=res.data.erCode
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 刷新会员码
|
||||
const refreshCode = () => {
|
||||
getCode()
|
||||
// 重置倒计时
|
||||
refreshCountdown.value = 60
|
||||
Service.Msg('会员码已刷新')
|
||||
}
|
||||
|
||||
|
||||
// 复制会员ID
|
||||
const copyMemberId = () => {
|
||||
uni.setClipboardData({
|
||||
data: userData.value.userNo,
|
||||
success: () => {
|
||||
Service.Msg('会员ID已复制')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 开始自动刷新
|
||||
const startAutoRefresh = () => {
|
||||
// 清除之前的定时器
|
||||
if (refreshTimer.value) {
|
||||
clearInterval(refreshTimer.value)
|
||||
}
|
||||
if (countdownTimer.value) {
|
||||
clearInterval(countdownTimer.value)
|
||||
}
|
||||
|
||||
// 倒计时定时器(每秒更新)
|
||||
countdownTimer.value = setInterval(() => {
|
||||
refreshCountdown.value--
|
||||
if (refreshCountdown.value <= 0) {
|
||||
refreshCountdown.value = 60
|
||||
getCode()
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 清理定时器
|
||||
onUnmounted(() => {
|
||||
if (refreshTimer) {
|
||||
clearInterval(refreshTimer.value)
|
||||
}
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer.value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 引入全局标签样式 */
|
||||
@import '@/styles/member-tags.scss';
|
||||
|
||||
/* 现代化会员码页面 */
|
||||
.member-code-page {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #FFF4E6 0%, #F5F5F5 100%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 状态栏 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 24rpx;
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.nav-placeholder {
|
||||
width: 40rpx;
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 32rpx 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 会员卡片 */
|
||||
.member-card {
|
||||
width: 100%;
|
||||
background: #FFFFFF;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(255, 107, 0, 0.12);
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
/* 用户信息区域 */
|
||||
.user-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 48rpx;
|
||||
margin-right: 20rpx;
|
||||
border: 3rpx solid #FF6B00;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
/* 分割线 */
|
||||
.divider {
|
||||
height: 1rpx;
|
||||
background: linear-gradient(90deg, transparent, #E0E0E0, transparent);
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
/* 条形码区域 */
|
||||
.barcode-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.barcode-wrapper {
|
||||
width: 480rpx;
|
||||
background: #F8F8F8;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx 32rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.barcode-placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.barcode-lines {
|
||||
font-size: 40rpx;
|
||||
color: #000000;
|
||||
letter-spacing: 2rpx;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.barcode-number {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
letter-spacing: 4rpx;
|
||||
}
|
||||
|
||||
/* 二维码区域 */
|
||||
.qrcode-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.qrcode-wrapper {
|
||||
width: 360rpx;
|
||||
height: 360rpx;
|
||||
background: #F8F8F8;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.qrcode-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #000000;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.qrcode-icon {
|
||||
font-size: 200rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.code-tip {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
text-align: center;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
/* 刷新区域 */
|
||||
.refresh-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.refresh-icon {
|
||||
font-size: 24rpx;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
.refresh-text {
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4rpx;
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
padding: 10rpx 20rpx;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
font-size: 20rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 24rpx;
|
||||
color: #FFFFFF;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 温馨提示 */
|
||||
.tips-section {
|
||||
width: 100%;
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.tips-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.tips-icon {
|
||||
font-size: 28rpx;
|
||||
color: #FFA500;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.tips-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.tips-item {
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,361 @@
|
||||
<template>
|
||||
<view style="display: flex; flex-direction: column; height: 100vh; ">
|
||||
<view class="merchant-info">
|
||||
<view class="">
|
||||
<text class="section-title">付款给商户</text>
|
||||
<text class="merchant-name">{{ storeInfo.name }}</text>
|
||||
</view>
|
||||
<image :src="Service.GetMateUrlByImg(storeInfo.logo)" mode="aspectFill" class="merchant-icon">
|
||||
</image>
|
||||
</view>
|
||||
|
||||
<view class=""
|
||||
style=" padding: 30rpx 40rpx; flex: 1; background-color: #fff; width: 100%; border-top-right-radius: 30rpx; border-top-left-radius: 30rpx; ">
|
||||
<view class="" style="font-size: 24rpx; font-weight: 600;">
|
||||
余额
|
||||
</view>
|
||||
<view class="" style="margin: 20rpx 0; padding: 20rpx 0; border-bottom: 1rpx solid #e2e2e2; ">
|
||||
<!-- <up-input prefixIcon='rmb' :prefixIconStyle="{ 'color':'#000','font-weight': 600,'font-size':'60rpx' }"
|
||||
fontSize='50rpx'
|
||||
auto-blur="false"
|
||||
@focus="focusFunc"
|
||||
:customStyle="{'color':'#000', height: '90rpx', 'padding-left': 0, 'font-weight': 600,'padding-bottom':'20rpx' }"
|
||||
border="bottom" v-model="account"></up-input> -->
|
||||
<view class="" style="display: flex; align-items: center; width: 100%; ">
|
||||
<view class="" style="height: 70rpx; display: flex; align-items: center; ">
|
||||
<up-icon name="rmb" :bold='true' size='50rpx' color="#000"></up-icon>
|
||||
</view>
|
||||
<view class="" style=" height: 70rpx; line-height: 70rpx; font-weight: 600;font-size: 70rpx; ">
|
||||
{{account}}
|
||||
</view>
|
||||
<view class="" v-if="isShow"
|
||||
style="margin: 0 10rpx; width: 4rpx; height: 70rpx; background-color: var(--nav-mian); ">
|
||||
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
</view>
|
||||
<view v-if="storeInfo.code=='Discounts' && userInfo.integral>0 " class=""
|
||||
style="font-size: 24rpx; color: #6B6B6B; ">
|
||||
<text>当前拥有{{ userInfo.integral }}积分</text>
|
||||
<text style="margin-left: 10rpx;">可抵扣 ¥{{ userInfo.integral }}元</text>
|
||||
</view>
|
||||
<view v-else class="">
|
||||
<view v-if="account>0 " class="" style="font-size: 24rpx; color: #6B6B6B; ">
|
||||
本次消费可得 {{ computePoints( account ) }} 积分
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="font-size: 24rpx; color: #6B6B6B; ">
|
||||
当前可用优惠券3张 <text style="color: blue;">点击查看</text>
|
||||
</view>
|
||||
<!-- <view class="" style="font-size: 28rpx; margin-top: 10rpx; ">
|
||||
<text :style="{'margin-right':!des?'':'15rpx'}">{{des}}</text>
|
||||
<text @click="showDes=true" style="font-weight: 600; color: #586B95;">添加备注</text>
|
||||
</view> -->
|
||||
<view class=""
|
||||
style="background-color: #f5f5f5; padding: 20rpx; position: fixed; bottom: 0; left: 0; width: 100%; padding-top: 25rpx; padding-bottom: 20rpx; ">
|
||||
<view class="" style="display: grid; grid-template-columns: repeat(4,1fr); ">
|
||||
<view class="button" @click="input(item)" v-for="(item,index) in 3" :key="index">
|
||||
{{item}}
|
||||
</view>
|
||||
<view @click="deleInput()" class="button">
|
||||
<up-icon name="backspace" :bold='true' size="26"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="display: grid; grid-template-columns: 3fr 1fr; ">
|
||||
<view class="">
|
||||
<view class="" style="display: grid; grid-template-columns: repeat(3,1fr); ">
|
||||
<view class="button" @click="input(item+3)" v-for="(item,index) in 3" :key="index">
|
||||
{{item+3}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="display: grid; grid-template-columns: repeat(3,1fr); ">
|
||||
<view class="button" @click="input(item+6)" v-for="(item,index) in 3" :key="index">
|
||||
{{item+6}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="display: grid; grid-template-columns: 2fr 1fr; ">
|
||||
<view @click="input(0)" class="button">
|
||||
0
|
||||
</view>
|
||||
<view @click="input('.')" class="button">
|
||||
.
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view @click="save()" class="button" style="background-color: var(--nav-mian); color: #fff; ">
|
||||
付款
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- <up-popup :show="showDes">
|
||||
<view style="width: 100%; padding: 50rpx 30rpx; ">
|
||||
<view class="">
|
||||
<text style="font-size: 28rpx; font-weight: 600;">添加备注</text>
|
||||
</view>
|
||||
<view class=""
|
||||
style=" margin-top: 30rpx; padding: 20rpx 0; border-bottom: 1rpx solid #e2e2e2; border-top: 1rpx solid #e2e2e2; ">
|
||||
<up-input placeholder="请输入内容" border="none" v-model="des"></up-input>
|
||||
</view>
|
||||
|
||||
<view class=""
|
||||
style=" margin: 0 110rpx; margin-top: 50rpx; display: flex; align-items: center; justify-content: space-between; ">
|
||||
<view class="" @click="showDes=false,des=''"
|
||||
style=" background-color: #f2f2f2; color: #000; padding: 20rpx 80rpx;border-radius: 20rpx; display: flex; align-items: center; justify-content: center; ">
|
||||
取消
|
||||
</view>
|
||||
<view class="" @click="showDes=false"
|
||||
style=" background-color: #07c160; color: #fff; padding: 20rpx 80rpx;border-radius: 20rpx; display: flex; align-items: center; justify-content: center; ">
|
||||
确定
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</up-popup> -->
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { Service } from "@/Service/Service"
|
||||
import { onUnmounted, ref } from "vue";
|
||||
import { vpMerchService } from "@/Service/vp/vpMerchService";
|
||||
import { vpUserService } from "@/Service/vp/vpUserService";
|
||||
import { vpLoginService } from "@/Service/vp/vpLoginService";
|
||||
|
||||
let account = ref('')
|
||||
// let showDes = ref(false)
|
||||
let des = ref()
|
||||
let isShow = ref(false)
|
||||
let timeOut = ref()
|
||||
let payway = ref('')
|
||||
let par = ref('')
|
||||
let openId = ref('')
|
||||
let storeInfo = ref<any>({})
|
||||
let userInfo = ref<any>({})
|
||||
let radio = ref(0)
|
||||
let points = ref('')
|
||||
|
||||
|
||||
let showCoupon = ref(false)
|
||||
|
||||
onLoad((data : any) => {
|
||||
focusFunc()
|
||||
getOpid()
|
||||
// 支付宝
|
||||
// #ifdef MP-ALIPAY
|
||||
let querdata = Service.GetStorageCache('quer')
|
||||
payway.value = 'zfb'
|
||||
par.value = querdata.query.par
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
payway.value = 'wx'
|
||||
if (data.q) {
|
||||
par.value = decodeURIComponent(data.q).split('?')[1].split('=')[1]
|
||||
}
|
||||
// #endif
|
||||
if (!Service.GetUserIsLogin()) {
|
||||
login()
|
||||
return
|
||||
}
|
||||
getData()
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
clearInterval(timeOut.value)
|
||||
})
|
||||
|
||||
const getData = () => {
|
||||
vpMerchService.GetUnitMerchInfo(par.value).then(res => {
|
||||
if (res.code == 0) {
|
||||
storeInfo.value = res.data.merchInfo
|
||||
userInfo.value = res.data.accInfo
|
||||
radio.value = res.data.radio
|
||||
} else {
|
||||
Service.Msg('商家获取失败,请重新扫码')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const input = (val : any) => {
|
||||
if (account.value.split('').length > 8) {
|
||||
return
|
||||
}
|
||||
|
||||
if (val == '.') {
|
||||
let arr = account.value.split('').filter((item => item == val))
|
||||
if (arr.length > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!account.value) {
|
||||
account.value = '0.'
|
||||
return
|
||||
}
|
||||
}
|
||||
account.value = account.value + val
|
||||
}
|
||||
|
||||
|
||||
const computePoints = (e : any) => {
|
||||
if (e <= 0.1) {
|
||||
return 0
|
||||
}
|
||||
if (storeInfo.value.code == 'Discounts') {
|
||||
return Number(e * radio.value).toFixed(2)
|
||||
} else {
|
||||
return Number(e * radio.value - e * 0.003).toFixed(2)
|
||||
}
|
||||
}
|
||||
|
||||
const deleInput = () => {
|
||||
let arr = account.value.split('')
|
||||
arr.pop()
|
||||
account.value = arr.join('')
|
||||
}
|
||||
|
||||
const save = () => {
|
||||
if (!account.value) {
|
||||
Service.Msg('请输入金额')
|
||||
return
|
||||
}
|
||||
Service.LoadIng('支付中')
|
||||
vpUserService.PayMerch(storeInfo.value.merchId, Number(account.value), payway.value, openId.value).then(res => {
|
||||
if (res.code == 0) {
|
||||
wx.requestPayment({
|
||||
timeStamp: res.data.resdata.timeStamp,
|
||||
nonceStr: res.data.resdata.nonceStr,
|
||||
package: res.data.resdata.package,
|
||||
signType: res.data.resdata.signType,
|
||||
paySign: res.data.resdata.paySign,
|
||||
success(payRes) {
|
||||
Service.LoadClose()
|
||||
//支付完成处理逻辑
|
||||
Service.Msg("支付成功");
|
||||
},
|
||||
fail(err) {
|
||||
Service.Msg("支付失败");
|
||||
console.error('pay fail', err)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
const focusFunc = () => {
|
||||
timeOut.value = setInterval(() => {
|
||||
isShow.value = !isShow.value
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const getOpid = () => {
|
||||
uni.login({
|
||||
onlyAuthorize: true,
|
||||
provider: 'weixin',
|
||||
success: function (loginRes) {
|
||||
vpLoginService.GetOpenIdByWeixin(loginRes.code, 1).then(res => {
|
||||
if (res.code == 0) {
|
||||
openId.value = res.data
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const login = () => {
|
||||
uni.getProvider({
|
||||
service: 'oauth',
|
||||
success: function (res : any) {
|
||||
uni.login({
|
||||
onlyAuthorize: true,
|
||||
provider: res.provider,
|
||||
success: function (loginRes) {
|
||||
vpLoginService.WxLogin(loginRes.code, res.provider == 'weixin' ? 1 : 2, 0, 0, '').then(content => {
|
||||
if (content.code == 0) {
|
||||
Service.SetUserToken(content.data.accToken)
|
||||
getData()
|
||||
} else {
|
||||
Service.Msg(content.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
// 按键
|
||||
.button {
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 18rpx 0;
|
||||
border-radius: 10rpx;
|
||||
font-weight: 700;
|
||||
margin: 8rpx;
|
||||
font-size: 38rpx;
|
||||
}
|
||||
|
||||
.button:active {
|
||||
background-color: #ababab;
|
||||
}
|
||||
|
||||
/* 商户信息 */
|
||||
.merchant-info {
|
||||
padding: 30rpx 40rpx;
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.merchant-name {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
display: block;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
|
||||
.merchant-icon {
|
||||
width: 95rpx;
|
||||
height: 95rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
</style>
|
||||
|
After Width: | Height: | Size: 966 B |
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Remix Icon Minimal Version - Only Used Icons
|
||||
* Based on Remix Icon v4.8.0
|
||||
* https://remixicon.com
|
||||
* Custom minimal build for VpComUni project
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-family: "remixicon";
|
||||
src: url("remixicon.woff2?t=1766743011500") format("woff2");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
[class^="ri-"], [class*=" ri-"] {
|
||||
font-family: 'remixicon' !important;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* Size utilities */
|
||||
.ri-lg { font-size: 1.3333em; line-height: 0.75em; vertical-align: -.0667em; }
|
||||
.ri-xl { font-size: 1.5em; line-height: 0.6666em; vertical-align: -.075em; }
|
||||
.ri-xxs { font-size: .5em; }
|
||||
.ri-xs { font-size: .75em; }
|
||||
.ri-sm { font-size: .875em }
|
||||
.ri-1x { font-size: 1em; }
|
||||
.ri-2x { font-size: 2em; }
|
||||
.ri-3x { font-size: 3em; }
|
||||
.ri-4x { font-size: 4em; }
|
||||
.ri-5x { font-size: 5em; }
|
||||
.ri-6x { font-size: 6em; }
|
||||
.ri-7x { font-size: 7em; }
|
||||
.ri-8x { font-size: 8em; }
|
||||
.ri-9x { font-size: 9em; }
|
||||
.ri-10x { font-size: 10em; }
|
||||
.ri-fw { text-align: center; width: 1.25em; }
|
||||
|
||||
/* Only the icons actually used in the project */
|
||||
.ri-arrow-right-s-line:before { content: "\ea5e"; }
|
||||
.ri-bank-card-line:before { content: "\ea92"; }
|
||||
.ri-calendar-check-line:before { content: "\eb23"; }
|
||||
.ri-checkbox-circle-fill:before { content: "\eb80"; }
|
||||
.ri-close-line:before { content: "\eb99"; }
|
||||
.ri-coin-line:before { content: "\ebb2"; }
|
||||
.ri-coupon-3-line:before { content: "\ebe6"; }
|
||||
.ri-customer-service-2-line:before { content: "\ec0c"; }
|
||||
.ri-edit-line:before { content: "\ec86"; }
|
||||
.ri-error-warning-line:before { content: "\ec94"; }
|
||||
.ri-feedback-line:before { content: "\ec9c"; }
|
||||
.ri-file-copy-line:before { content: "\ea86"; }
|
||||
.ri-file-list-3-line:before { content: "\ead3"; }
|
||||
.ri-gift-line:before { content: "\ebd0"; }
|
||||
.ri-heart-line:before { content: "\ebd6"; }
|
||||
.ri-information-line:before { content: "\ec9e"; }
|
||||
.ri-lightbulb-line:before { content: "\ebd7"; }
|
||||
.ri-map-pin-line:before { content: "\ebda"; }
|
||||
.ri-medal-line:before { content: "\ebdb"; }
|
||||
.ri-message-2-line:before { content: "\ebdd"; }
|
||||
.ri-percent-line:before { content: "\ebe0"; }
|
||||
.ri-qr-code-line:before { content: "\ebe3"; }
|
||||
.ri-refresh-line:before { content: "\eb95"; }
|
||||
.ri-settings-4-line:before { content: "\ebe5"; }
|
||||
.ri-smartphone-line:before { content: "\ebe7"; }
|
||||
.ri-sparkling-fill:before { content: "\ebe9"; }
|
||||
.ri-team-line:before { content: "\ebed"; }
|
||||
.ri-time-line:before { content: "\ebf1"; }
|
||||
.ri-user-add-line:before { content: "\ebf3"; }
|
||||
.ri-vip-crown-2-fill:before { content: "\ebf5"; }
|
||||
.ri-vip-crown-fill:before { content: "\ebf6"; }
|
||||
.ri-vip-diamond-fill:before { content: "\ebf7"; }
|
||||
.ri-wechat-fill:before { content: "\ebf9"; }
|
||||
|
After Width: | Height: | Size: 680 B |
@@ -0,0 +1,230 @@
|
||||
<template>
|
||||
<view style="margin: 20rpx 30rpx;">
|
||||
<view class="" style="border-radius: 20rpx; background-color: #fff; padding: 20rpx; ">
|
||||
<view class="" style="font-weight: 600; font-size: 30rpx;">
|
||||
商品封面
|
||||
</view>
|
||||
<view class="" @click="uploadFImg()"
|
||||
style=" margin-top: 20rpx; width: 100%; height: 320rpx; border: 4rpx dashed #e2e2e2; border-radius: 20rpx; overflow: hidden; ">
|
||||
<view class=""
|
||||
style="width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; ">
|
||||
<img :src="Service.GetIconImg('/static/goods/photo.png')" style="width: 80rpx; height: 80rpx;"
|
||||
alt="" />
|
||||
<view class="" style=" color: #666666; font-size: 28rpx;">
|
||||
上传图片
|
||||
</view>
|
||||
</view>
|
||||
<!-- <image :src="Service.GetMateUrlByImg('/static/dele/dele4.jpg')" mode="aspectFill" style="width: 100%; height: 100%; " alt="" /> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
<view class="" style=" margin-top: 20rpx; border-radius: 20rpx; background-color: #fff; padding: 20rpx; ">
|
||||
<up-form labelWidth='90' :labelStyle="{'font-weight': 600 }" labelPosition="top" :model="goodsInfo"
|
||||
ref="form1">
|
||||
<up-form-item label="商品名称" prop="goodsInfo.name" :borderBottom="false" ref="item1">
|
||||
<up-input :customStyle="{'border-radius': '15rpx' }" placeholder="请输入商品名称" border='surround'
|
||||
v-model="goodsInfo.name"></up-input>
|
||||
</up-form-item>
|
||||
<up-form-item label="价格" prop="goodsInfo.sex" :borderBottom="false">
|
||||
<up-input prefixIcon='rmb' :prefixIconStyle="{ 'color':'#000','font-weight': 600 }"
|
||||
:customStyle="{'border-radius': '15rpx' }" placeholder="请输入价格" v-model="goodsInfo.prince"
|
||||
border="surround"></up-input>
|
||||
</up-form-item>
|
||||
<up-form-item label="描述" prop="goodsInfo.sex" :borderBottom="false">
|
||||
<up-textarea v-model="goodsInfo.des" placeholder="请输入商品描述"></up-textarea>
|
||||
</up-form-item>
|
||||
<up-form-item label="分类" prop="goodsInfo.sex" :borderBottom="false">
|
||||
<view class=""
|
||||
style="padding: 12rpx 18rpx; border-radius: 7px; border: 1rpx solid #e2e2e2; width: 100%; ">
|
||||
<view class="" @click="showClass=true"
|
||||
style="display: flex; height: 48rpx; align-items: center; justify-content: space-between;">
|
||||
<view class="" :style="{ 'color':goodsInfo.class? '#000':'#c0c4cc' } ">
|
||||
{{ goodsInfo.class?goodsInfo.class:'请选择分类'}}
|
||||
</view>
|
||||
<view class="">
|
||||
<up-icon name="arrow-right" size="14" color='#666666' :bold='true'></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</up-form-item>
|
||||
<up-form-item label="状态" prop="goodsInfo.sex" :borderBottom="false">
|
||||
<up-radio-group v-model="goodsInfo.status" placement="row">
|
||||
<up-radio :customStyle="{marginBottom: '8px'}" v-for="(item, index) in radiolist" :key="index"
|
||||
:label="item.name" :name="item.value" @change="radioChange">
|
||||
</up-radio>
|
||||
</up-radio-group>
|
||||
</up-form-item>
|
||||
<up-form-item label="标签" :borderBottom="false">
|
||||
<view v-for="(item, index) in tabsList" @click="clickTab(index)" :key="index"
|
||||
:class="{active:!goodsInfo.tabs.includes(index),actived:goodsInfo.tabs.includes(index)}">
|
||||
{{item}}
|
||||
</view>
|
||||
</up-form-item>
|
||||
</up-form>
|
||||
</view>
|
||||
|
||||
<view class="" style="width: 100%; height: 200rpx;">
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<view class="action-buttons">
|
||||
<u-button type="primary" @click="Service.GoPageBack()" :custom-style="ButtonStyle">取消</u-button>
|
||||
<u-button type="primary" @click="save()" :custom-style="ButtonStyle">保存</u-button>
|
||||
</view>
|
||||
|
||||
|
||||
<up-picker :show="showClass" @cancel="showClass=!showClass" @confirm="confirmClass"
|
||||
:columns="columns"></up-picker>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { Service } from "@/Service/Service"
|
||||
import { ref } from "vue";
|
||||
|
||||
|
||||
let goodsInfo = ref({
|
||||
name: "",
|
||||
prince: '',
|
||||
des: "",
|
||||
class: '',
|
||||
status: '',
|
||||
tabs: []
|
||||
})
|
||||
|
||||
let showClass = ref(false)
|
||||
const columns = ref([
|
||||
['中国', '美国', '日本']
|
||||
]);
|
||||
|
||||
const radiolist = ref([
|
||||
{
|
||||
name: '上架中',
|
||||
value: '0'
|
||||
|
||||
},
|
||||
{
|
||||
name: '已下架',
|
||||
value: '1'
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
let tabsList = ref([
|
||||
'新品',
|
||||
'热销',
|
||||
'推荐',
|
||||
'折扣',
|
||||
'特惠'
|
||||
])
|
||||
|
||||
|
||||
const ButtonStyle = ref({
|
||||
backgroundColor: '#FF6600',
|
||||
borderColor: '#FF6600',
|
||||
color: '#FFFFFF',
|
||||
fontSize: '28rpx',
|
||||
height: '75rpx',
|
||||
borderRadius: '45rpx',
|
||||
marginLeft: '20rpx'
|
||||
});
|
||||
|
||||
onLoad(() => {
|
||||
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
|
||||
|
||||
const confirmClass = (e) => {
|
||||
goodsInfo.value.class = e.value[0]
|
||||
showClass.value = !showClass.value
|
||||
}
|
||||
|
||||
|
||||
const radioChange = (e) => {
|
||||
goodsInfo.value.status = e
|
||||
}
|
||||
|
||||
const clickTab = (e : any) => {
|
||||
if (goodsInfo.value.tabs.includes(e)) {
|
||||
let a = goodsInfo.value.tabs.findIndex((x) => {
|
||||
return x == e
|
||||
})
|
||||
goodsInfo.value.tabs.splice(a, 1)
|
||||
return
|
||||
}
|
||||
|
||||
goodsInfo.value.tabs.push(e)
|
||||
}
|
||||
|
||||
const save=()=>{
|
||||
Service.GoPageBack()
|
||||
}
|
||||
|
||||
|
||||
|
||||
const uploadFImg = () => {
|
||||
uni.chooseImage({
|
||||
count: 1, // 最多选择3张图片
|
||||
sizeType: ['original', 'compressed'], // 支持原图和压缩图
|
||||
sourceType: ['album', 'camera'], // 可从相册选择或使用相机拍照
|
||||
success: function (res) {
|
||||
let path = res.tempFiles[0].path
|
||||
// Service.uploadH5(path, 'Avatar', data => {
|
||||
// userInfo.value.headImg = data.split(',')[2].split(':')[1].split('"')[1]
|
||||
// })
|
||||
|
||||
},
|
||||
fail: function (err) {
|
||||
console.error('选择失败:', err.errMsg);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: #b5b5b5;
|
||||
border: 1rpx solid #b5b5b5;
|
||||
margin-right: 15rpx;
|
||||
border-radius: 23rpx;
|
||||
padding: 5rpx 16rpx;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.actived {
|
||||
color: var(--nav-mian);
|
||||
border: 1rpx solid var(--nav-mian);
|
||||
margin-right: 15rpx;
|
||||
border-radius: 23rpx;
|
||||
padding: 5rpx 16rpx;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
/* 底部操作按钮 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
padding: 30rpx 30rpx;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #ffffff;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,9 @@
|
||||
export class BaseConfig {
|
||||
// protected static servesUrl: string = "http://192.168.0.142:5002";//线下
|
||||
|
||||
protected static servesUrl: string = "https://vp.xypays.cn";
|
||||
protected static imgUrl : string = "https://vp.clouds.xypays.cn";
|
||||
protected static mediaUrl: string = "http://byc1.xypays.cn/";
|
||||
protected static uploadUrl: string = "/TencentCos/GetUpLoadInfo";
|
||||
protected static payuploadUrl: string = "https://vp.xypays.cn";
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"id": "t-cropper",
|
||||
"displayName": "t-cropper图片裁剪插件",
|
||||
"version": "1.0.8",
|
||||
"description": "vue3图片裁剪插件,头像裁剪、支持自定义尺寸、等比例缩放、拖动、图片翻转、剪切圆形/圆角图片,高性能裁剪图片插件",
|
||||
"keywords": [
|
||||
"图片裁剪",
|
||||
",头像裁剪,缩放,旋转,拖动",
|
||||
""
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y",
|
||||
"alipay": "n"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "n",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "u",
|
||||
"app-uvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "u",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 292 B |
@@ -0,0 +1,727 @@
|
||||
/**
|
||||
* 图片编辑器-手势监听
|
||||
* 1. wxs 暂不支持 es6 语法
|
||||
* 2. 支持编译到微信小程序、QQ小程序、app-vue、H5上(uni-app 2.2.5及以上版本)
|
||||
*/
|
||||
/** 图片偏移量 */
|
||||
var offset = { x: 0, y: 0 };
|
||||
/** 图片缩放比例 */
|
||||
var scale = 1;
|
||||
/** 图片最小缩放比例 */
|
||||
var minScale = 1;
|
||||
/** 图片旋转角度 */
|
||||
var rotate = 0;
|
||||
/** 触摸点 */
|
||||
var touches = [];
|
||||
/** 图片布局信息 */
|
||||
var img = {};
|
||||
/** 系统信息 */
|
||||
var sys = {};
|
||||
/** 裁剪区域布局信息 */
|
||||
var area = {};
|
||||
/** 触摸行为类型 */
|
||||
var touchType = '';
|
||||
/** 操作角的位置 */
|
||||
var activeAngle = 0;
|
||||
/** 裁剪区域布局信息偏移量 */
|
||||
var areaOffset = { left: 0, right: 0, top: 0, bottom: 0 };
|
||||
/** 容错值 */
|
||||
var fault = 0.000001;
|
||||
/**
|
||||
* 获取a、b两数中的最小正数
|
||||
* @param a
|
||||
* @param b
|
||||
*/
|
||||
function minimum(a, b) {
|
||||
if (a > 0 && b < 0) return a;
|
||||
if (a < 0 && b > 0) return b;
|
||||
if (a > 0 && b > 0) return Math.min(a, b);
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* 在容错访问内获取n近似值
|
||||
* @param n
|
||||
*/
|
||||
function num(n) {
|
||||
var m = parseFloat((n).toFixed(6));
|
||||
return m === fault || m === -fault ? 0 : m;
|
||||
}
|
||||
/**
|
||||
* 比较a值在容错值范围内是否等于b值
|
||||
* @param a
|
||||
* @param b
|
||||
*/
|
||||
function equalsByFault(a, b) {
|
||||
return Math.abs(a - b) <= fault;
|
||||
}
|
||||
/**
|
||||
* 比较a值在容错值范围内是否小于b值
|
||||
* @param a
|
||||
* @param b
|
||||
*/
|
||||
function lessThanByFault(a, b) {
|
||||
var c = a - b;
|
||||
return c < 0 ? c < -fault : c < fault;
|
||||
}
|
||||
/**
|
||||
* 验证并获取有效最大值
|
||||
* @param v
|
||||
* @param max
|
||||
* @param isInclude
|
||||
* @param x
|
||||
* @param y
|
||||
* @param rate
|
||||
* @returns
|
||||
*/
|
||||
function validMax(v, max, isInclude, x, y, rate) {
|
||||
if(typeof max === 'number') {
|
||||
if(isInclude && equalsByFault(max, y)) { // 宽高不等时,x轴用y轴值要做等比例转换
|
||||
var n = num(max * rate);
|
||||
if (n <= x) return n; // 转化后值在x轴最大值范围内
|
||||
return x; // 转化后值超出x轴最大值范围则用最大值
|
||||
}
|
||||
return max;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
/**
|
||||
* 计算两点间距
|
||||
* @param {Object} touches 触摸点信息
|
||||
*/
|
||||
function getDistanceByTouches(touches) {
|
||||
// 根据勾股定理求两点间距离
|
||||
var a = touches[1].pageX - touches[0].pageX;
|
||||
var b = touches[1].pageY - touches[0].pageY;
|
||||
var c = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
|
||||
// 求两点间的中点坐标
|
||||
// 1. a、b可能为负值
|
||||
// 2. 在求a、b时,如用touches[1]减touches[0],则求中点坐标也得用touches[1]减a/2、b/2
|
||||
// 3. 同理,在求a、b时,也可用touches[0]减touches[1],则求中点坐标也得用touches[0]减a/2、b/2
|
||||
var x = touches[1].pageX - a / 2;
|
||||
var y = touches[1].pageY - b / 2;
|
||||
return { c, x, y };
|
||||
};
|
||||
/**
|
||||
* 修正取值
|
||||
* @param {Object} a
|
||||
* @param {Object} b
|
||||
* @param {Object} c
|
||||
* @param {Object} reverse 是否反向
|
||||
*/
|
||||
function correctValue(a, b, c, reverse) {
|
||||
return num(reverse ? Math.max(Math.min(a, b), c) : Math.min(Math.max(a, b), c));
|
||||
}
|
||||
|
||||
/**
|
||||
* 旋转90°或270°时检查边界:限制 x、y 拖动范围,禁止滑出边界
|
||||
* @param {Object} e 点坐标
|
||||
* @param {Object} xReverse x是否反向
|
||||
* @param {Object} yReverse y是否反向
|
||||
*/
|
||||
function checkRotateRange(e, xReverse, yReverse) {
|
||||
var o = num((img.height - img.width) / 2); // 宽高差值一半
|
||||
return {
|
||||
x: correctValue(e.x, -img.height + o + area.width + area.left, area.left + o, xReverse),
|
||||
y: correctValue(e.y, -img.width - o + area.height + area.top, area.top - o, yReverse)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查边界:限制 x、y 拖动范围,禁止滑出边界
|
||||
* @param {Object} e 点坐标
|
||||
*/
|
||||
function checkRange(e) {
|
||||
var r = rotate / 90 % 2;
|
||||
if(r === 1) { // 因图片宽高可能不等,翻转 90° 或 270° 后图片宽高需反着计算,且左右和上下边界要根据差值做偏移
|
||||
if (area.width === area.height) {
|
||||
return checkRotateRange(e, img.height < area.height, img.width < area.width);
|
||||
}
|
||||
var isInclude = img.height < area.width && img.width < area.height; // 图片是否包含在裁剪区域内
|
||||
if (img.width < area.height || img.height < area.width) {
|
||||
if (area.width < area.height && img.width < img.height) {
|
||||
return isInclude
|
||||
? checkRotateRange(e, area.width < area.height, area.width < area.height)
|
||||
: checkRotateRange(e, false, true);
|
||||
}
|
||||
if (area.height < area.width && img.height < img.width) {
|
||||
return isInclude
|
||||
? checkRotateRange(e, area.height < area.width, area.height < area.width)
|
||||
: checkRotateRange(e, true, false);
|
||||
}
|
||||
}
|
||||
if (img.height >= area.width && img.width >= area.height) {
|
||||
return checkRotateRange(e, false, false);
|
||||
}
|
||||
if (isInclude) {
|
||||
return area.height < area.width
|
||||
? checkRotateRange(e, true, true)
|
||||
: checkRotateRange(e, area.width < area.height, area.width < area.height);
|
||||
}
|
||||
if (img.height < area.width && !img.width < area.height) {
|
||||
return checkRotateRange(e, true, false);
|
||||
}
|
||||
if (!img.height < area.width && img.width < area.height) {
|
||||
return checkRotateRange(e, false, true);
|
||||
}
|
||||
return checkRotateRange(e, img.height < area.height, img.width < area.width);
|
||||
}
|
||||
return {
|
||||
x: correctValue(e.x, -img.width + area.width + area.left, area.left, img.width < area.width),
|
||||
y: correctValue(e.y, -img.height + area.height + area.top, area.top, img.height < area.height)
|
||||
};
|
||||
};
|
||||
/**
|
||||
* 变更图片布局信息
|
||||
* @param {Object} e 布局信息
|
||||
*/
|
||||
function changeImageRect(e) {
|
||||
offset.x += e.x || 0;
|
||||
offset.y += e.y || 0;
|
||||
var image = e.instance.selectComponent('.crop-image');
|
||||
if(e.check && area.checkRange) { // 检查边界
|
||||
var point = checkRange(offset);
|
||||
if(offset.x !== point.x || offset.y !== point.y) {
|
||||
offset = point;
|
||||
}
|
||||
}
|
||||
// image.setStyle({
|
||||
// width: img.width + 'px',
|
||||
// height: img.height + 'px',
|
||||
// transform: 'translate(' + offset.x + 'px, ' + offset.y + 'px) rotate(' + rotate +'deg)'
|
||||
// });
|
||||
var ox = (img.width - img.oldWidth) / 2;
|
||||
var oy = (img.height - img.oldHeight) / 2;
|
||||
image.setStyle({
|
||||
width: img.oldWidth + 'px',
|
||||
height: img.oldHeight + 'px',
|
||||
transform: (img.gpu ? 'translateZ(0) ' : '') + 'translate(' + (offset.x + ox) + 'px, ' + (offset.y + oy) + 'px) rotate(' + rotate +'deg) scale(' + scale + ')'
|
||||
});
|
||||
|
||||
e.instance.callMethod('dataChange', {
|
||||
width: img.width,
|
||||
height: img.height,
|
||||
x: offset.x,
|
||||
y: offset.y,
|
||||
rotate: rotate
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 变更裁剪区域布局信息
|
||||
* @param {Object} e 布局信息
|
||||
*/
|
||||
function changeAreaRect(e) {
|
||||
// 变更蒙版样式
|
||||
var masks = e.instance.selectAllComponents('.crop-mask-block');
|
||||
var maskStyles = [
|
||||
{
|
||||
left: 0,
|
||||
width: (area.left + areaOffset.left) + 'px',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
'z-index': area.zIndex + 2
|
||||
},
|
||||
{
|
||||
left: (area.right + areaOffset.right) + 'px',
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
'z-index': area.zIndex + 2
|
||||
},
|
||||
{
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
top: 0,
|
||||
height: (area.top + areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 2
|
||||
},
|
||||
{
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
top: (area.bottom + areaOffset.bottom) + 'px',
|
||||
// height: (area.top - areaOffset.bottom + sys.offsetBottom) + 'px',
|
||||
bottom: 0,
|
||||
'z-index': area.zIndex + 2
|
||||
}
|
||||
];
|
||||
var len = masks.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
masks[i].setStyle(maskStyles[i]);
|
||||
}
|
||||
|
||||
// 变更边框样式
|
||||
if(area.showBorder) {
|
||||
var border = e.instance.selectComponent('.crop-border');
|
||||
border.setStyle({
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
top: (area.top + areaOffset.top) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
});
|
||||
}
|
||||
|
||||
// 变更参考线样式
|
||||
if(area.showGrid) {
|
||||
var grids = e.instance.selectAllComponents('.crop-grid');
|
||||
var gridStyles = [
|
||||
{
|
||||
'border-width': '1px 0 0 0',
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
right: (area.right + areaOffset.right) + 'px',
|
||||
top: (area.top + areaOffset.top + (area.height + areaOffset.bottom - areaOffset.top) / 3 - 0.5) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '1px 0 0 0',
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
right: (area.right + areaOffset.right) + 'px',
|
||||
top: (area.top + areaOffset.top + (area.height + areaOffset.bottom - areaOffset.top) * 2 / 3 - 0.5) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '0 1px 0 0',
|
||||
top: (area.top + areaOffset.top) + 'px',
|
||||
bottom: (area.bottom + areaOffset.bottom) + 'px',
|
||||
left: (area.left + areaOffset.left + (area.width + areaOffset.right - areaOffset.left) / 3 - 0.5) + 'px',
|
||||
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '0 1px 0 0',
|
||||
top: (area.top + areaOffset.top) + 'px',
|
||||
bottom: (area.bottom + areaOffset.bottom) + 'px',
|
||||
left: (area.left + areaOffset.left + (area.width + areaOffset.right - areaOffset.left) * 2 / 3 - 0.5) + 'px',
|
||||
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
}
|
||||
];
|
||||
var len = grids.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
grids[i].setStyle(gridStyles[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// 变更四个伸缩角样式
|
||||
if(area.showAngle) {
|
||||
var angles = e.instance.selectAllComponents('.crop-angle');
|
||||
var angleStyles = [
|
||||
{
|
||||
'border-width': area.angleBorderWidth + 'px 0 0 ' + area.angleBorderWidth + 'px',
|
||||
left: (area.left + areaOffset.left - area.angleBorderWidth) + 'px',
|
||||
top: (area.top + areaOffset.top - area.angleBorderWidth) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': area.angleBorderWidth + 'px ' + area.angleBorderWidth + 'px 0 0',
|
||||
left: (area.right + areaOffset.right - area.angleSize) + 'px',
|
||||
top: (area.top + areaOffset.top - area.angleBorderWidth) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '0 0 ' + area.angleBorderWidth + 'px ' + area.angleBorderWidth + 'px',
|
||||
left: (area.left + areaOffset.left - area.angleBorderWidth) + 'px',
|
||||
top: (area.bottom + areaOffset.bottom - area.angleSize) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
},
|
||||
{
|
||||
'border-width': '0 ' + area.angleBorderWidth + 'px ' + area.angleBorderWidth + 'px 0',
|
||||
left: (area.right + areaOffset.right - area.angleSize) + 'px',
|
||||
top: (area.bottom + areaOffset.bottom - area.angleSize) + 'px',
|
||||
'z-index': area.zIndex + 3
|
||||
}
|
||||
];
|
||||
var len = angles.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
angles[i].setStyle(angleStyles[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// 变更圆角样式
|
||||
if(area.radius > 0) {
|
||||
var circleBox = e.instance.selectComponent('.crop-circle-box');
|
||||
var circle = e.instance.selectComponent('.crop-circle');
|
||||
var radius = area.radius;
|
||||
if(area.width === area.height && area.radius >= area.width / 2) { // 圆形
|
||||
radius = (area.width / 2);
|
||||
} else { // 圆角矩形
|
||||
if(area.width !== area.height) { // 限制圆角半径不能超过短边的一半
|
||||
radius = Math.min(area.width / 2, area.height / 2, radius);
|
||||
}
|
||||
}
|
||||
circleBox.setStyle({
|
||||
left: (area.left + areaOffset.left) + 'px',
|
||||
top: (area.top + areaOffset.top) + 'px',
|
||||
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
|
||||
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
|
||||
'z-index': area.zIndex + 2
|
||||
});
|
||||
circle.setStyle({
|
||||
'box-shadow': '0 0 0 ' + Math.max(area.width, area.height) + 'px rgba(51, 51, 51, 0.8)',
|
||||
'border-radius': radius + 'px'
|
||||
});
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 缩放图片
|
||||
* @param {Object} e 布局信息
|
||||
*/
|
||||
function scaleImage(e) {
|
||||
var last = scale;
|
||||
scale = Math.min(Math.max(e.scale + scale, minScale), img.maxScale);
|
||||
if(last !== scale) {
|
||||
img.width = num(img.oldWidth * scale);
|
||||
img.height = num(img.oldHeight * scale);
|
||||
// 参考问题:有一个长4000px、宽4000px的四方形ABCD,A点的坐标固定在(-2000,-2000),
|
||||
// 该四边形上有一个点E,坐标为(-100,-300),将该四方形复制一份并缩小到90%后,
|
||||
// 新四边形的A点坐标为多少时可使新四边形的E点与原四边形的E点重合?
|
||||
// 预期效果:从图中选取某点(参照物)为中心点进行缩放,缩放时无论图像怎么变化,该点位置始终固定不变
|
||||
// 计算方法:以相同起点先计算缩放前后两点间的距离,再加上原图像偏移量即可
|
||||
e.x = num((e.x - offset.x) * (1 - scale / last));
|
||||
e.y = num((e.y - offset.y) * (1 - scale / last));
|
||||
changeImageRect(e);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
/**
|
||||
* 获取触摸点在哪个角
|
||||
* @param {number} x 触摸点x轴坐标
|
||||
* @param {number} y 触摸点y轴坐标
|
||||
* @return {number} 角的位置:0=无;1=左上;2=右上;3=左下;4=右下;
|
||||
*/
|
||||
function getToucheAngle(x, y) {
|
||||
// console.log('getToucheAngle', x, y, JSON.stringify(area))
|
||||
var o = area.angleBorderWidth; // 需扩大触发范围则把 o 值加大即可
|
||||
if(y >= area.top - o && y <= area.top + area.angleSize + o) {
|
||||
if(x >= area.left - o && x <= area.left + area.angleSize + o) {
|
||||
return 1; // 左上角
|
||||
} else if(x >= area.right - area.angleSize - o && x <= area.right + o) {
|
||||
return 2; // 右上角
|
||||
}
|
||||
} else if(y >= area.bottom - area.angleSize - o && y <= area.bottom + o) {
|
||||
if(x >= area.left - o && x <= area.left + area.angleSize + o) {
|
||||
return 3; // 左下角
|
||||
} else if(x >= area.right - area.angleSize - o && x <= area.right + o) {
|
||||
return 4; // 右下角
|
||||
}
|
||||
}
|
||||
return 0; // 无触摸到角
|
||||
};
|
||||
/**
|
||||
* 重置数据
|
||||
*/
|
||||
function resetData() {
|
||||
offset = { x: 0, y: 0 };
|
||||
scale = 1;
|
||||
minScale = img.minScale;
|
||||
rotate = 0;
|
||||
};
|
||||
/**
|
||||
* 顺时针翻转图片90°
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
function rotateImage(e, o, r) {
|
||||
rotate = (rotate + r) % 360;
|
||||
if(img.minScale >= 1 && area.checkRange) {
|
||||
// 因图片宽高可能不等,翻转后图片宽高需足够填满裁剪区域
|
||||
minScale = 1;
|
||||
if(img.width < area.height) {
|
||||
minScale = area.height / img.oldWidth;
|
||||
} else if(img.height < area.width) {
|
||||
minScale = area.width / img.oldHeight;
|
||||
}
|
||||
if(minScale !== 1) {
|
||||
scaleImage({
|
||||
instance: o,
|
||||
scale: minScale - scale,
|
||||
x: sys.windowWidth / 2,
|
||||
y: (sys.windowHeight - sys.offsetBottom) / 2
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 由于拖动画布后会导致图片位置偏移,翻转时的旋转中心点需是图片区域+偏移区域的中心点
|
||||
// 翻转x轴中心点 = (超出裁剪区域右侧的图片宽度 - 超出裁剪区域左侧的图片宽度) / 2
|
||||
// 翻转y轴中心点 = (超出裁剪区域下方的图片宽度 - 超出裁剪区域上方的图片宽度) / 2
|
||||
var ox = ((offset.x + img.width - area.right) - (area.left - offset.x)) / 2;
|
||||
var oy = ((offset.y + img.height - area.bottom) - (area.top - offset.y)) / 2;
|
||||
changeImageRect({
|
||||
instance: o,
|
||||
check: true,
|
||||
x: -ox - oy,
|
||||
y: -oy + ox
|
||||
});
|
||||
};
|
||||
module.exports = {
|
||||
/**
|
||||
* 初始化:观察数据变更
|
||||
* @param {Object} newVal 新数据
|
||||
* @param {Object} oldVal 旧数据
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
initObserver: function(newVal, oldVal, o, i) {
|
||||
if(newVal) {
|
||||
img = newVal.img;
|
||||
sys = newVal.sys;
|
||||
area = newVal.area;
|
||||
minScale = img.minScale;
|
||||
resetData();
|
||||
img.src && changeImageRect({
|
||||
instance: o,
|
||||
x: (sys.windowWidth - img.width) / 2,
|
||||
y: (sys.windowHeight - sys.offsetBottom - img.height) / 2
|
||||
});
|
||||
changeAreaRect({
|
||||
instance: o
|
||||
});
|
||||
// console.log('initRect', JSON.stringify(newVal))
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 鼠标滚轮滚动
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
mousewheel: function(e, o) {
|
||||
if(!img.src) return;
|
||||
scaleImage({
|
||||
instance: o,
|
||||
check: true,
|
||||
// 鼠标向上滚动时,deltaY 固定 -100,鼠标向下滚动时,deltaY 固定 100
|
||||
scale: e.detail.deltaY > 0 ? -0.05 : 0.05,
|
||||
x: e.touches[0].pageX,
|
||||
y: e.touches[0].pageY
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 触摸开始
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
touchstart: function(e, o) {
|
||||
if(!img.src) return;
|
||||
touches = e.touches;
|
||||
activeAngle = area.showAngle ? getToucheAngle(touches[0].pageX, touches[0].pageY) : 0;
|
||||
if(touches.length === 1 && activeAngle !== 0) {
|
||||
touchType = 'stretch'; // 伸缩裁剪区域
|
||||
} else {
|
||||
touchType = '';
|
||||
}
|
||||
// console.log('touchstart', JSON.stringify(e), activeAngle)
|
||||
},
|
||||
/**
|
||||
* 触摸移动
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
touchmove: function(e, o) {
|
||||
if(!img.src) return;
|
||||
// console.log('touchmove', JSON.stringify(e), JSON.stringify(o))
|
||||
if(touchType === 'stretch') { // 触摸四个角进行拉伸
|
||||
var point = e.touches[0];
|
||||
var start = touches[0];
|
||||
var x = point.pageX - start.pageX;
|
||||
var y = point.pageY - start.pageY;
|
||||
if(x !== 0 || y !== 0) {
|
||||
var maxX = num(area.width * (1 - area.minScale));
|
||||
var maxY = num(area.height * (1 - area.minScale));
|
||||
// console.log(x, y, maxX, maxY, offset, area)
|
||||
touches[0] = point;
|
||||
var r = rotate / 90 % 2;
|
||||
var m = r === 1 ? num((img.height - img.width) / 2) : 0; // 宽高差值一半
|
||||
var xCompare = r === 1 ? lessThanByFault(img.height, area.width) : lessThanByFault(img.width, area.width);
|
||||
var yCompare = r === 1 ? lessThanByFault(img.width, area.height) : lessThanByFault(img.height, area.height)
|
||||
var isInclude = xCompare && yCompare;
|
||||
var isIntersect = area.checkRange && (xCompare || yCompare); // 图片是否包含在裁剪区域内
|
||||
var isReverse = !isInclude || num((offset.x - area.left) / area.width) <= num((offset.y - area.top) / area.height) || (area.width > area.height && img.width < img.height && r === 1);
|
||||
switch(activeAngle) {
|
||||
case 1: // 左上角
|
||||
x = num(x + areaOffset.left);
|
||||
y = num(y + areaOffset.top);
|
||||
if(x >= 0 && y >= 0) { // 有效滑动
|
||||
var t = num(offset.y + m - area.top);
|
||||
var l = num(offset.x - m - area.left);
|
||||
// && (offset.x + img.width < area.right || offset.y + img.height < area.bottom)
|
||||
var max = isIntersect && ((l >= 0) || (t >= 0))
|
||||
? minimum(t, l)
|
||||
: false;
|
||||
if(x > y && isReverse) { // 以x轴滑动距离为缩放基准
|
||||
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
|
||||
if(x > maxX) x = maxX;
|
||||
y = num(x * area.height / area.width);
|
||||
} else { // 以y轴滑动距离为缩放基准
|
||||
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
|
||||
if(y > maxY) y = maxY;
|
||||
x = num(y * area.width / area.height);
|
||||
}
|
||||
areaOffset.left = x;
|
||||
areaOffset.top = y;
|
||||
}
|
||||
break;
|
||||
case 2: // 右上角
|
||||
x = num(x + areaOffset.right);
|
||||
y = num(y + areaOffset.top);
|
||||
if(x <= 0 && y >= 0) { // 有效滑动
|
||||
var w = (r === 1 ? img.height : img.width);
|
||||
var t = num(offset.y + m - area.top);
|
||||
var l = num(area.right + m - offset.x - w);
|
||||
var max = isIntersect && ((t >= 0) || (l >= 0))
|
||||
? minimum(t, l)
|
||||
: false;
|
||||
// var max = isInclude && ((offset.x > 0 && offset.x + img.width <= area.right) || (offset.y > 0 && offset.y >= area.top))
|
||||
// ? minimum(offset.y - area.top, area.right - offset.x - img.width)
|
||||
// : false;
|
||||
// console.log(offset.x, offset.y, img.width, img.height, area.top, area.right, m, max)
|
||||
// console.log(offset.y + m - area.top, area.right + m - offset.x - w)
|
||||
if(-x > y && isReverse) { // 以x轴滑动距离为缩放基准
|
||||
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
|
||||
if(-x > maxX) x = -maxX;
|
||||
y = num(-x * area.height / area.width);
|
||||
} else { // 以y轴滑动距离为缩放基准
|
||||
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
|
||||
if(y > maxY) y = maxY;
|
||||
x = num(-y * area.width / area.height);
|
||||
}
|
||||
areaOffset.right = x;
|
||||
areaOffset.top = y;
|
||||
}
|
||||
break;
|
||||
case 3: // 左下角
|
||||
x += num(x + areaOffset.left);
|
||||
y += num(y + areaOffset.bottom);
|
||||
if(x >= 0 && y <= 0) { // 有效滑动
|
||||
var w = (r === 1 ? img.width : img.height);
|
||||
var t = num(area.bottom - m - offset.y - w);
|
||||
var l = num(offset.x - m - area.left);
|
||||
var max = isIntersect && ((l >= 0) || (t >= 0))
|
||||
? minimum(t, l)
|
||||
: false;
|
||||
if(x > -y && isReverse) { // 以x轴滑动距离为缩放基准
|
||||
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
|
||||
if(x > maxX) x = maxX;
|
||||
y = num(-x * area.height / area.width);
|
||||
} else { // 以y轴滑动距离为缩放基准
|
||||
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
|
||||
if(-y > maxY) y = -maxY;
|
||||
x = num(-y * area.width / area.height);
|
||||
}
|
||||
areaOffset.left = x;
|
||||
areaOffset.bottom = y;
|
||||
}
|
||||
break;
|
||||
case 4: // 右下角
|
||||
x = num(x + areaOffset.right);
|
||||
y = num(y + areaOffset.bottom);
|
||||
if(x <= 0 && y <= 0) { // 有效滑动
|
||||
var w = (r === 1 ? img.height : img.width);
|
||||
var h = (r === 1 ? img.width : img.height);
|
||||
var t = num(area.bottom - offset.y - h - m);
|
||||
var l = num(area.right + m - offset.x - w);
|
||||
var max = isIntersect && ((l >= 0) || (t >= 0))
|
||||
? minimum(t, l)
|
||||
: false;
|
||||
if(-x > -y && isReverse) { // 以x轴滑动距离为缩放基准
|
||||
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
|
||||
if(-x > maxX) x = -maxX;
|
||||
y = num(x * area.height / area.width);
|
||||
} else { // 以y轴滑动距离为缩放基准
|
||||
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
|
||||
if(-y > maxY) y = -maxY;
|
||||
x = num(y * area.width / area.height);
|
||||
}
|
||||
areaOffset.right = x;
|
||||
areaOffset.bottom = y;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// console.log(x, y, JSON.stringify(areaOffset))
|
||||
changeAreaRect({
|
||||
instance: o,
|
||||
});
|
||||
// this.draw();
|
||||
}
|
||||
} else if (e.touches.length == 2) { // 双点触摸缩放
|
||||
var start = getDistanceByTouches(touches);
|
||||
var end = getDistanceByTouches(e.touches);
|
||||
scaleImage({
|
||||
instance: o,
|
||||
check: !area.bounce,
|
||||
scale: (end.c - start.c) / 100,
|
||||
x: end.x,
|
||||
y: end.y
|
||||
});
|
||||
touchType = 'scale';
|
||||
} else if(touchType === 'scale') {// 从双点触摸变成单点触摸 / 从缩放变成拖动
|
||||
touchType = 'move';
|
||||
} else {
|
||||
changeImageRect({
|
||||
instance: o,
|
||||
check: !area.bounce,
|
||||
x: e.touches[0].pageX - touches[0].pageX,
|
||||
y: e.touches[0].pageY - touches[0].pageY
|
||||
});
|
||||
touchType = 'move';
|
||||
}
|
||||
touches = e.touches;
|
||||
},
|
||||
/**
|
||||
* 触摸结束
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
touchend: function(e, o) {
|
||||
if(!img.src) return;
|
||||
if(touchType === 'stretch') { // 拉伸裁剪区域的四个角缩放
|
||||
// 裁剪区域宽度被缩放到多少
|
||||
var left = areaOffset.left;
|
||||
var right = areaOffset.right;
|
||||
var top = areaOffset.top;
|
||||
var bottom = areaOffset.bottom;
|
||||
var w = area.width + right - left;
|
||||
var h = area.height + bottom - top;
|
||||
// 图像放大倍数
|
||||
var p = scale * (area.width / w) - scale;
|
||||
// 复原裁剪区域
|
||||
areaOffset = { left: 0, right: 0, top: 0, bottom: 0 };
|
||||
changeAreaRect({
|
||||
instance: o,
|
||||
});
|
||||
scaleImage({
|
||||
instance: o,
|
||||
scale: p,
|
||||
x: area.left + left + (1 === activeAngle || 3 === activeAngle ? w : 0),
|
||||
y: area.top + top + (1 === activeAngle || 2 === activeAngle ? h : 0)
|
||||
});
|
||||
} else if (area.bounce) { // 检查边界并矫正,实现拖动到边界时有回弹效果
|
||||
changeImageRect({
|
||||
instance: o,
|
||||
check: true
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 顺时针翻转图片90°
|
||||
* @param {Object} e 事件对象
|
||||
* @param {Object} o 组件实例对象
|
||||
*/
|
||||
rotateImage: function(e, o) {
|
||||
rotateImage(e, o, 90);
|
||||
},
|
||||
rotateImage90: function(e, o) {
|
||||
rotateImage(e, o, 90)
|
||||
},
|
||||
rotateImage270: function(e, o) {
|
||||
rotateImage(e, o, 270)
|
||||
},
|
||||
// 此处只用于对齐其他平台端的样式参数,防止异常,无作用
|
||||
imageStyles: '',
|
||||
maskStylesList: ['', '', '', ''],
|
||||
borderStyles: '',
|
||||
gridStylesList: ['', '', '', ''],
|
||||
angleStylesList: ['', '', '', ''],
|
||||
circleBoxStyles: '',
|
||||
circleStyles: '',
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Service } from '@/Service/Service';
|
||||
/*****用户订单*****/
|
||||
class vpOrderService {
|
||||
private static GetUserOrderListPath : string = '/Order/GetUserOrderList';
|
||||
/*****获取用户订单列表*****/
|
||||
static GetUserOrderList(page:number) {
|
||||
var result = Service.Request(this.GetUserOrderListPath, 'GET', {page});
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
export { Service, vpOrderService };
|
||||
|
After Width: | Height: | Size: 597 B |
@@ -0,0 +1,476 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
|
||||
|
||||
<!-- 商品图片展示 -->
|
||||
<view class="product-image-section">
|
||||
<image :src="Service.GetMateUrlByImg(goodsInfo.img)" mode="aspectFill" class="product-image">
|
||||
</image>
|
||||
</view>
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<view class="product-info-section">
|
||||
<text class="product-name">{{goodsInfo.name}}</text>
|
||||
<text class="product-price">¥{{goodsInfo.price}}</text>
|
||||
<text class="product-description">{{goodsInfo.brief}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 商家信息卡片 -->
|
||||
<view class="shop-card-section">
|
||||
<view class="shop-header">
|
||||
<text class="shop-name">{{merchInfo.name}}</text>
|
||||
<view class="shop-tags">
|
||||
<view class="tag" style="background-color: #FF9500;">{{merchInfo.code=='Discounts'?'积分可用':'积分不可用'}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="shop-info">
|
||||
<view class="info-item">
|
||||
<u-icon name="map" size="21" color="#999999" class="info-icon"></u-icon>
|
||||
<text class="info-text">{{merchInfo.address}}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<u-icon name="clock" size="21" color="#999999" class="info-icon"></u-icon>
|
||||
<text class="info-text">{{merchInfo.time}}</text>
|
||||
</view>
|
||||
<!-- <view class="info-item">
|
||||
<u-icon name="home" size="22" color="#999999" class="info-icon"></u-icon>
|
||||
<text class="info-text">朝阳美食社区</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 同店推荐 -->
|
||||
<view v-if="recommendations.length>0" class="recommendations-section">
|
||||
<text class="section-title">同店推荐</text>
|
||||
<view class="recommendations-list">
|
||||
<view @click="Service.GoPage('/pages/goods/goodsDetail')" class="recommendation-item"
|
||||
v-for="(item, index) in recommendations" :key="index">
|
||||
<image :src="Service.GetMateUrlByImg(item.img)" mode="aspectFill" class="recommendation-image">
|
||||
</image>
|
||||
<view class="recommendation-info">
|
||||
<text class="recommendation-name">{{ item.name }}</text>
|
||||
<text class="recommendation-price">{{ item.price }}</text>
|
||||
</view>
|
||||
<u-icon name="arrow-right" size="28rpx" color="#999999" class="arrow-icon"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<up-loadmore v-if="recommendations.length>0" :status="status" />
|
||||
<view class="" style="width: 100%; height: 150rpx;">
|
||||
|
||||
</view>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<view class="action-buttons">
|
||||
<u-button type="primary" @click="handleViewLocation()" :custom-style="locationButtonStyle">查看位置</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Service } from "@/Service/Service"
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { vpMerchService } from '@/Service/vp/vpMerchService'
|
||||
|
||||
// 商品信息
|
||||
interface Goods {
|
||||
brief : string
|
||||
img : string
|
||||
name : string
|
||||
price : number
|
||||
}
|
||||
|
||||
// 商家数据类型
|
||||
interface shop {
|
||||
showImg : string
|
||||
price : number
|
||||
name : string
|
||||
score : number
|
||||
tag : string
|
||||
merchId : string
|
||||
address : string
|
||||
phone : string
|
||||
sale : number
|
||||
lat : string
|
||||
lon : string
|
||||
time : string
|
||||
code:string
|
||||
}
|
||||
|
||||
|
||||
let merchInfo = ref<shop>({
|
||||
showImg: '',
|
||||
price: 0,
|
||||
name: '',
|
||||
score: 0,
|
||||
tag: '',
|
||||
merchId: '',
|
||||
address: '',
|
||||
phone: '',
|
||||
sale: 0,
|
||||
lat: '',
|
||||
lon: '',
|
||||
time: '',
|
||||
code:''
|
||||
})
|
||||
|
||||
// 营业时间处理
|
||||
let weekList = ref<Array<number>>([])
|
||||
let openTime = ref()
|
||||
let closeTime = ref()
|
||||
let timefunc = ref(-1)
|
||||
|
||||
// 推荐商品数据类型
|
||||
interface Recommendation {
|
||||
goodsId : string;
|
||||
name : string;
|
||||
price : string;
|
||||
img : string;
|
||||
}
|
||||
|
||||
let goodsId = ref()
|
||||
let status = ref()
|
||||
let page = ref(0)
|
||||
|
||||
let goodsInfo = ref<Goods>({
|
||||
brief: '',
|
||||
img: '',
|
||||
name: '',
|
||||
price: 0
|
||||
})
|
||||
|
||||
|
||||
// 同店推荐商品数据
|
||||
const recommendations = ref<Recommendation[]>([]);
|
||||
|
||||
// 联系商家按钮样式
|
||||
const contactButtonStyle = ref({
|
||||
backgroundColor: '#FF6600',
|
||||
borderColor: '#FF6600',
|
||||
color: '#FFFFFF',
|
||||
fontSize: '28rpx',
|
||||
height: '75rpx',
|
||||
borderRadius: '45rpx',
|
||||
marginRight: '20rpx'
|
||||
});
|
||||
|
||||
// 查看位置按钮样式
|
||||
const locationButtonStyle = ref({
|
||||
backgroundColor: '#FF6600',
|
||||
borderColor: '#FF6600',
|
||||
color: '#FFFFFF',
|
||||
fontSize: '28rpx',
|
||||
height: '75rpx',
|
||||
borderRadius: '45rpx',
|
||||
marginLeft: '20rpx'
|
||||
});
|
||||
|
||||
|
||||
onLoad((data : any) => {
|
||||
goodsId.value = data.goodsId
|
||||
getData()
|
||||
})
|
||||
|
||||
|
||||
const getData = () => {
|
||||
status.value = 'loadmore'
|
||||
page.value = 1
|
||||
recommendations.value = []
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList = () => {
|
||||
if (status.value !== 'loadmore') {
|
||||
return
|
||||
}
|
||||
status.value = 'loading'
|
||||
|
||||
vpMerchService.GetGoodsInfo(goodsId.value, page.value).then(res => {
|
||||
goodsInfo.value = res.data.goodsInfo
|
||||
merchInfo.value = res.data.merchInfo
|
||||
merchInfo.value.time = !res.data.merchInfo.busTime ? '' : timeCancle(res.data.merchInfo.busTime)
|
||||
recommendations.value = res.data.merchGoods
|
||||
let index = recommendations.value.findIndex((item) => {
|
||||
return item.goodsId == goodsId.value
|
||||
})
|
||||
recommendations.value.splice(index, 1)
|
||||
status.value = res.data.merchGoods.length == 10 ? 'loadmore' : 'nomore'
|
||||
page.value++
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 处理联系商家
|
||||
const handleContactShop = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: merchInfo.value.phone, // 要拨打的电话号码
|
||||
success: function () {
|
||||
console.log('拨打电话成功');
|
||||
},
|
||||
fail: function (err) {
|
||||
console.error('拨打电话失败', err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 处理查看位置
|
||||
const handleViewLocation = () => {
|
||||
uni.openLocation({
|
||||
latitude: Number(merchInfo.value.lat),
|
||||
longitude: Number(merchInfo.value.lon),
|
||||
name: merchInfo.value.name,
|
||||
address: merchInfo.value.address,
|
||||
success: function (e) {
|
||||
console.log(e);
|
||||
},
|
||||
fail: function (e) {
|
||||
console.log(e);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
// 营业时间处理
|
||||
const timeCancle = (busTime : string) => {
|
||||
let data = busTime.split('_')
|
||||
openTime.value = data[1].split('-')[0]
|
||||
closeTime.value = data[1].split('-')[1]
|
||||
let timeData = data[0].split('-')
|
||||
for (let i = 0; i < timeData.length; i++) {
|
||||
if (timeData[i] == '0') {
|
||||
timeData[i] = '7'
|
||||
}
|
||||
}
|
||||
let time = ''
|
||||
timeData.sort((a : any, b : any) => {
|
||||
return a - b
|
||||
})
|
||||
timeData.map((item : any) => {
|
||||
weekList.value.push(item == '7' ? (Number(6)) : Number(item - 1))
|
||||
})
|
||||
let timeIndex = weekList.value[0] - 0
|
||||
let judgment = weekList.value.findIndex((item, index) => {
|
||||
return item - index !== timeIndex
|
||||
})
|
||||
|
||||
// 1是至 /0是全显示
|
||||
if (judgment == -1) {
|
||||
timefunc.value = 1
|
||||
} else {
|
||||
timefunc.value = 0
|
||||
}
|
||||
|
||||
if (timefunc.value == 0) {
|
||||
weekList.value.map((item) => {
|
||||
time = time + '周' + chinese(item) + ' '
|
||||
})
|
||||
} else {
|
||||
time = '周' + chinese((weekList.value[0])) + '至' + '周' + chinese((weekList.value[weekList.value.length - 1]))
|
||||
}
|
||||
|
||||
time = time + ' ' + data[1]
|
||||
|
||||
return time
|
||||
}
|
||||
|
||||
const chinese = (item : number) => {
|
||||
if (item + 1 == 1) {
|
||||
return '一'
|
||||
}
|
||||
if (item + 1 == 2) {
|
||||
return '二'
|
||||
} if (item + 1 == 3) {
|
||||
return '三'
|
||||
} if (item + 1 == 4) {
|
||||
return '四'
|
||||
} if (item + 1 == 5) {
|
||||
return '五'
|
||||
} if (item + 1 == 6) {
|
||||
return '六'
|
||||
} if (item + 1 == 7) {
|
||||
return '日'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* 商品图片展示 */
|
||||
.product-image-section {
|
||||
width: 100%;
|
||||
height: 500rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.product-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 商品信息 */
|
||||
.product-info-section {
|
||||
padding: 30rpx;
|
||||
margin: 30rpx;
|
||||
box-shadow: 0 0 10rpx 4rpx #E2e2e2;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
font-size: 36rpx;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
font-size: 40rpx;
|
||||
color: #FF6600;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.product-description {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
/* 商家信息卡片 */
|
||||
.shop-card-section {
|
||||
padding: 30rpx;
|
||||
margin: 30rpx;
|
||||
box-shadow: 0 0 10rpx 4rpx #E2e2e2;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.shop-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.shop-tags {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 24rpx;
|
||||
color: #FFFFFF;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.tag.new-shop {
|
||||
background-color: #4CD964;
|
||||
}
|
||||
|
||||
.tag.popular {
|
||||
background-color: #FF9500;
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
font-size: 28rpx;
|
||||
margin-left: 15rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
/* 同店推荐 */
|
||||
.recommendations-section {
|
||||
padding: 0 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 36rpx;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.recommendations-list {
|
||||
gap: 30rpx;
|
||||
}
|
||||
|
||||
.recommendation-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
margin: 20rpx 0;
|
||||
box-shadow: 0 0 10rpx 4rpx #E2e2e2;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.recommendation-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.recommendation-image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 16rpx;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.recommendation-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.recommendation-name {
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
display: block;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.recommendation-price {
|
||||
font-size: 30rpx;
|
||||
color: #FF6600;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
/* 底部操作按钮 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
padding: 30rpx 30rpx;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #ffffff;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,496 @@
|
||||
<template>
|
||||
<view class="orders-page">
|
||||
<!-- 页面加载中 - 显示骨架屏 -->
|
||||
<view v-if="pageLoading" class="page-loading-wrapper">
|
||||
<!-- Tab 切换栏骨架屏 -->
|
||||
<view class="tab-bar">
|
||||
<view class="skeleton-tab-item"></view>
|
||||
<view class="skeleton-tab-item"></view>
|
||||
<view class="skeleton-tab-item"></view>
|
||||
</view>
|
||||
|
||||
<!-- 订单列表骨架屏 -->
|
||||
<view class="orders-content">
|
||||
<SkeletonOrderCard v-for="i in 5" :key="i" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 页面加载完成 - 显示实际内容 -->
|
||||
<view v-else>
|
||||
<!-- Tab 切换栏 -->
|
||||
<view class="tab-bar">
|
||||
<view v-for="(tab, index) in tabs" :key="index" class="tab-item"
|
||||
:class="{ active: currentTab === index }" @click="switchTab(index)">
|
||||
<text class="tab-text">{{ tab.label }}</text>
|
||||
<view v-if="currentTab === index" class="tab-indicator"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单列表 -->
|
||||
<view class="orders-content">
|
||||
<view v-if="orderList.length > 0" class="orders-list">
|
||||
<view v-for="order in orderList" @click="Service.GoPage('/pages/goods/orderDetail?orderId='+order.orderId)" :key="order.orderId" class="order-card" >
|
||||
<!-- 店铺信息 -->
|
||||
<view class="order-header">
|
||||
<image class="shop-avatar" :src="Service.GetMateUrlByImg(order.merchLogo)"
|
||||
mode="aspectFill" />
|
||||
<view class="shop-info">
|
||||
<text class="shop-name">{{ order.merchName }}</text>
|
||||
<text class="order-time">{{ Service.formatDate(order.addTime,1) }}</text>
|
||||
</view>
|
||||
<view class="order-status status-completed">
|
||||
<text class="status-text">已完成</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单详情 -->
|
||||
<view class="order-details">
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">订单金额</text>
|
||||
<text class="detail-value amount">¥{{ Number(order.amount).toFixed(2) }}</text>
|
||||
</view>
|
||||
|
||||
<view v-if="order.useIntegral > 0" class="detail-row">
|
||||
<text class="detail-label">使用积分</text>
|
||||
<text class="detail-value used-points">-{{ order.useIntegral }}</text>
|
||||
</view>
|
||||
|
||||
<view v-if="order.getIntegral > 0" class="detail-row">
|
||||
<text class="detail-label">获得积分</text>
|
||||
<text class="detail-value points-text">+{{ order.getIntegral }}</text>
|
||||
</view>
|
||||
|
||||
|
||||
<view v-if="order.discount" class="detail-row">
|
||||
<text class="detail-label">优惠券</text>
|
||||
<text class="detail-value coupon-text">{{ '满'+JSON.parse(order.discount).needMoney+'减'+JSON.parse(order.discount).deductMoney }}</text>
|
||||
</view>
|
||||
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">付款金额</text>
|
||||
<text class="detail-value amount">¥{{Number(order.payAmount).toFixed(2) }}</text>
|
||||
</view>
|
||||
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 空状态 -->
|
||||
<view v-if="orderList.length === 0" class="empty-state">
|
||||
<text class="ri-file-list-line empty-icon"></text>
|
||||
<text class="empty-text">暂无订单</text>
|
||||
</view>
|
||||
<up-loadmore v-else :status="status" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ref,
|
||||
onMounted
|
||||
} from 'vue'
|
||||
import SkeletonOrderCard from '../../components/skeleton/skeleton-order-card.vue'
|
||||
import {
|
||||
onLoad, onReachBottom
|
||||
} from '@dcloudio/uni-app'
|
||||
import { vpOrderService, Service } from '@/Service/vp/vpOrderService'
|
||||
|
||||
// 当前Tab
|
||||
const currentTab = ref(0)
|
||||
|
||||
// 加载状态
|
||||
const pageLoading = ref(true)
|
||||
|
||||
// Tab 配置
|
||||
const tabs = [{
|
||||
label: '全部订单',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: '待评价',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: '退款',
|
||||
value: 2
|
||||
}
|
||||
]
|
||||
|
||||
let page = ref(1)
|
||||
let status = ref('nomore')
|
||||
let orderList = ref<Array<any>>([])
|
||||
onLoad(() => {
|
||||
getData()
|
||||
})
|
||||
|
||||
onReachBottom(()=>{
|
||||
getList()
|
||||
})
|
||||
|
||||
const getData = () => {
|
||||
page.value = 1
|
||||
status.value = 'loadmore'
|
||||
orderList.value = []
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList = () => {
|
||||
if (status.value !== 'loadmore') {
|
||||
return
|
||||
}
|
||||
status.value = 'loading'
|
||||
vpOrderService.GetUserOrderList(page.value).then(res => {
|
||||
if (res.code == 0) {
|
||||
pageLoading.value = false
|
||||
orderList.value = [...orderList.value, ...res.data.list]
|
||||
status.value = res.data.list.length == 10 ? 'loadmore' : 'nomore'
|
||||
page.value++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 切换Tab
|
||||
const switchTab = (index:any) => {
|
||||
currentTab.value = index
|
||||
if(index==0){
|
||||
getData()
|
||||
}else{
|
||||
orderList.value=[]
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.orders-page {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
padding-bottom: calc(100rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
/* 状态栏 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 24rpx;
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
}
|
||||
|
||||
.nav-placeholder {
|
||||
width: 48rpx;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* Tab 切换栏 */
|
||||
.tab-bar {
|
||||
background: #FFFFFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24rpx 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.tab-item.active .tab-text {
|
||||
color: #FF6B00;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tab-indicator {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 40rpx;
|
||||
height: 4rpx;
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
|
||||
/* 订单内容区域 */
|
||||
.orders-content {
|
||||
padding: 20rpx;
|
||||
padding-top: 24rpx;
|
||||
}
|
||||
|
||||
/* 订单列表 */
|
||||
.orders-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.order-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
/* 订单头部 */
|
||||
.order-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1rpx solid #F5F5F5;
|
||||
}
|
||||
|
||||
.shop-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 12rpx;
|
||||
margin-right: 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6rpx;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.order-time {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.order-status {
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 24rpx;
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.order-status.status-completed {
|
||||
background: #E8F5E9;
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.order-status.status-pending {
|
||||
background: #FFF4E6;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
.order-status.status-refunding {
|
||||
background: #E3F2FD;
|
||||
color: #2196F3;
|
||||
}
|
||||
|
||||
.order-status.status-refunded {
|
||||
background: #FFEBEE;
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
/* 订单详情 */
|
||||
.order-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: 26rpx;
|
||||
color: #222222;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.detail-value.amount {
|
||||
font-size: 32rpx;
|
||||
color: #FF6B00;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.detail-value.points-text {
|
||||
color: #4CAF50;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.detail-value.used-points {
|
||||
color: #F44336;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.detail-value.coupon-text {
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
.detail-value.order-no {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
padding: 120rpx 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 96rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 26rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
/* 底部导航栏 - 精致简约风格 */
|
||||
.tabbar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #FFFFFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
padding: 12rpx 0 calc(12rpx + env(safe-area-inset-bottom));
|
||||
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
z-index: 999;
|
||||
height: calc(90rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.tabbar-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10rpx 0;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tabbar-icon {
|
||||
font-size: 44rpx;
|
||||
color: #CCCCCC;
|
||||
margin-bottom: 8rpx;
|
||||
transition: all 0.3s ease;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.tabbar-text {
|
||||
font-size: 22rpx;
|
||||
color: #CCCCCC;
|
||||
font-weight: 400;
|
||||
transition: all 0.3s ease;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* 激活状态 */
|
||||
.tabbar-item.active .tabbar-icon {
|
||||
color: #FF6B00;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.tabbar-item.active .tabbar-text {
|
||||
color: #FF6B00;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 订单页头部骨架屏样式 */
|
||||
.page-loading-wrapper {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
.skeleton-nav-title {
|
||||
flex: 1;
|
||||
height: 36rpx;
|
||||
margin: 0 24rpx;
|
||||
border-radius: 4rpx;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.3) 25%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.3) 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading-white 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.skeleton-tab-item {
|
||||
flex: 1;
|
||||
height: 28rpx;
|
||||
border-radius: 4rpx;
|
||||
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes loading-white {
|
||||
0% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,245 @@
|
||||
<template>
|
||||
<view>
|
||||
<view :style="{'height':topHeight+'rpx'}"
|
||||
style=" position: fixed; top: 0; z-index: 2; width: 100%; background-color: #fff; ">
|
||||
<view class="" :style="{'margin-top':top+'rpx','height':height+'rpx','line-height':height+'rpx'}"
|
||||
style=" margin-left: 40rpx; display: flex; align-items: center;">
|
||||
<img :src="Service.GetIconImg('/static/index/index/location.png')" style="width: 40rpx; height: 40rpx;"
|
||||
alt="" />
|
||||
<text style="margin-left: 15rpx; font-size: 26rpx; font-weight: 600; ">许昌市魏都区</text>
|
||||
</view>
|
||||
<!-- 内容 -->
|
||||
<view class="" style="margin: 40rpx 20rpx">
|
||||
<up-swiper imgMode='heightFix' :list="swiperList" height='140' @change="e => current = e.current"
|
||||
:autoplay="false">
|
||||
<template #indicator>
|
||||
<view class="indicator">
|
||||
<view class="indicator__dot" v-for="(item, index) in swiperList" :key="index"
|
||||
:class="[index === current && 'indicator__dot--active']">
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</up-swiper>
|
||||
</view>
|
||||
|
||||
<view class=""
|
||||
style=" display: flex; align-items: center; justify-content: space-around; margin: 20rpx 0; background-color: #fff; padding: 20rpx">
|
||||
<view v-for="(item, index) in tabList" @click="chooseTab(index)"
|
||||
style="display: flex; flex-direction: column; align-items: center; justify-content: center;"
|
||||
:key="index">
|
||||
<view class="" :class="{tabimgActive:index==tabCurrent,tabimg: index!=tabCurrent }"
|
||||
style=" border-radius: 50%; display: flex; align-items: center; justify-content: center; height: 80rpx; width: 80rpx; ">
|
||||
<img :src="Service.GetIconImg( index==tabCurrent? item.imged:item.img)"
|
||||
style="width: 45rpx; height: 45rpx; "></img>
|
||||
</view>
|
||||
<view :class="{tabActivefont:index==tabCurrent,tabfont:index!=tabCurrent}"
|
||||
style="font-size: 26rpx; margin-top: 15rpx;" class="">
|
||||
{{item.name}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class=""
|
||||
style="display: grid; grid-template-columns: repeat(2,1fr); gap: 20rpx; margin-top: 40rpx; background-color: #fff; padding: 20rpx; ">
|
||||
<view class="">
|
||||
<image :src="Service.GetMateUrlByImg('/static/dele/dele1.jpg')" mode="widthFix" style="width: 100%;" alt="" />
|
||||
</view>
|
||||
<view class="">
|
||||
<image :src="Service.GetMateUrlByImg('/static/dele/dele2.jpg')" mode="widthFix" style="width: 100%;" alt="" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { onShow, onLoad } from '@dcloudio/uni-app';
|
||||
import { Service } from "@/Service/Service"
|
||||
|
||||
// 导航栏
|
||||
let topHeight = ref()
|
||||
let height = ref()
|
||||
let top = ref()
|
||||
|
||||
|
||||
let current = ref(0)
|
||||
let swiperList = ref(
|
||||
[
|
||||
'/static/dele/dele1.jpg',
|
||||
'/static/dele/dele2.jpg'
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
let tabCurrent = ref(0)
|
||||
let tabList = ref(
|
||||
[
|
||||
{
|
||||
name: '美食',
|
||||
img: '/static/index/index/food.png',
|
||||
imged: '/static/index/index/fooded.png'
|
||||
},
|
||||
{
|
||||
name: '饮品',
|
||||
img: '/static/index/index/cofe.png',
|
||||
imged: '/static/index/index/cofed.png'
|
||||
},
|
||||
{
|
||||
name: '超市',
|
||||
img: '/static/index/index/shop.png',
|
||||
imged: '/static/index/index/shoped.png'
|
||||
},
|
||||
{
|
||||
name: '美妆',
|
||||
img: '/static/index/index/good.png',
|
||||
imged: '/static/index/index/gooded.png'
|
||||
},
|
||||
{
|
||||
name: '医疗',
|
||||
img: '/static/index/index/medical.png',
|
||||
imged: '/static/index/index/medicaled.png'
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
|
||||
onShow(() => {
|
||||
|
||||
})
|
||||
onLoad(() => {
|
||||
let res = wx.getMenuButtonBoundingClientRect()
|
||||
topHeight.value = (res.top + res.height + 5) * 2
|
||||
height.value = res.height * 2
|
||||
top.value = res.top * 2
|
||||
})
|
||||
|
||||
|
||||
const chooseTab = (e) => {
|
||||
tabCurrent.value = e
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
@include flex(row);
|
||||
justify-content: center;
|
||||
|
||||
&__dot {
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
border-radius: 100px;
|
||||
background-color: rgba(255, 255, 255, 0.35);
|
||||
margin: 0 5px;
|
||||
transition: background-color 0.3s;
|
||||
|
||||
&--active {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tabimgActive {
|
||||
background-color: var(--nav-mian);
|
||||
}
|
||||
|
||||
.tabimg {
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
|
||||
.tabActivefont {
|
||||
color: var(--nav-mian);
|
||||
}
|
||||
|
||||
.tabfont {
|
||||
color: #333333
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: fit-content;
|
||||
font-size: 24rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
|
||||
// 瀑布流
|
||||
.demo-warter {
|
||||
border-radius: 8px;
|
||||
margin: 5px;
|
||||
background-color: #ffffff;
|
||||
padding: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.u-close {
|
||||
position: absolute;
|
||||
top: 32rpx;
|
||||
right: 32rpx;
|
||||
}
|
||||
|
||||
.demo-image {
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.demo-title {
|
||||
font-size: 30rpx;
|
||||
margin-top: 5px;
|
||||
|
||||
}
|
||||
|
||||
.demo-tag {
|
||||
display: flex;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.demo-tag-owner {
|
||||
|
||||
color: #FFFFFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 50rpx;
|
||||
font-size: 20rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.demo-tag-text {
|
||||
|
||||
|
||||
margin-left: 10px;
|
||||
border-radius: 50rpx;
|
||||
line-height: 1;
|
||||
padding: 4rpx 14rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 50rpx;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.demo-price {
|
||||
font-size: 30rpx;
|
||||
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.demo-shop {
|
||||
font-size: 22rpx;
|
||||
|
||||
margin-top: 5px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,406 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
|
||||
|
||||
<!-- 商品图片展示 -->
|
||||
<view class="product-image-section">
|
||||
<image :src="Service.GetMateUrlByImg(merchInfo.showImg)" mode="aspectFill" class="product-image">
|
||||
</image>
|
||||
</view>
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<view class="product-info-section">
|
||||
<view class="shop-header">
|
||||
<view class="shop-name">{{merchInfo.name}}</view>
|
||||
<view v-if="merchInfo.tag" v-for="(tagItem,tagIndex) in merchInfo.tag.split(',') " :key='tagIndex' class="shop-tags">
|
||||
<text class="tag" :style="{ 'background-color':tagIndex==0?'#4CD964':(tagIndex==1?'#FF9500':'#fff')}" >{{tagItem}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="display: flex; align-items: center;">
|
||||
<view class="func-detail">
|
||||
<text style="color: #FF6B35;">{{merchInfo.score}}</text>分
|
||||
</view>
|
||||
<view class="func-detail">
|
||||
月销<text style="color: #FF6B35;">{{merchInfo.sale}}</text>
|
||||
</view>
|
||||
<view class="func-detail">
|
||||
人均<text style="color: #FF6B35;">¥{{merchInfo.price}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商家信息卡片 -->
|
||||
<view class="shop-card-section">
|
||||
|
||||
<view class="shop-info">
|
||||
<view class="" @click="handleViewLocation()"
|
||||
style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<view class="info-item">
|
||||
<u-icon name="map" size="22" color="#999999" class="info-icon"></u-icon>
|
||||
<text class="info-text">{{merchInfo.address}}</text>
|
||||
</view>
|
||||
<view class="">
|
||||
<u-icon name="arrow-right" size="18" color="#999999" class="info-icon"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" @click="handleContactShop()"
|
||||
style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<view class="info-item">
|
||||
<u-icon name="phone" size="22" color="#999999" class="info-icon"></u-icon>
|
||||
<text class="info-text">{{merchInfo.phone}}</text>
|
||||
</view>
|
||||
<view class="">
|
||||
<u-icon name="arrow-right" size="18" color="#999999" class="info-icon"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="communityInfo" class="info-item">
|
||||
<u-icon name="home" size="22" color="#999999" class="info-icon"></u-icon>
|
||||
<text class="info-text">{{communityInfo}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 同店推荐 -->
|
||||
<view v-if="recommendations.length>0" class="recommendations-section">
|
||||
<text class="section-title">店内商品</text>
|
||||
<view class="recommendations-list">
|
||||
<view @click="Service.GoPage('/pages/goods/goodsDetail?goodsId='+item.goodsId)" class="recommendation-item"
|
||||
v-for="(item, index) in recommendations" :key="index">
|
||||
<image :src="Service.GetMateUrlByImg(item.img)" mode="aspectFill" class="recommendation-image"></image>
|
||||
<view class="recommendation-info">
|
||||
<text class="recommendation-name">{{ item.name }}</text>
|
||||
<text class="recommendation-price">{{ item.price }}</text>
|
||||
</view>
|
||||
<u-icon name="arrow-right" size="28rpx" color="#999999" class="arrow-icon"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<up-loadmore v-if="recommendations.length>0" :status="status" />
|
||||
<view class="" style="width: 100%; height: 150rpx;">
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Service } from "@/Service/Service"
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { vpHomeService } from '@/Service/vp/vpHomeService'
|
||||
|
||||
|
||||
// 商家数据类型
|
||||
interface shop{
|
||||
showImg : string
|
||||
price:number
|
||||
name:string
|
||||
score:number
|
||||
tag:string
|
||||
merchId:string
|
||||
address:string
|
||||
phone:string
|
||||
sale:number
|
||||
lat:string
|
||||
lon:string
|
||||
}
|
||||
|
||||
|
||||
// 推荐商品数据类型
|
||||
interface Recommendation {
|
||||
goodsId: string;
|
||||
name : string;
|
||||
price : number;
|
||||
img : string;
|
||||
}
|
||||
|
||||
let merchId = ref()
|
||||
let page = ref(0)
|
||||
let status = ref('loadmore')
|
||||
|
||||
|
||||
let merchInfo=ref<shop>({
|
||||
showImg : '',
|
||||
price:0,
|
||||
name:'',
|
||||
score: 0,
|
||||
tag: '',
|
||||
merchId: '',
|
||||
address: '',
|
||||
phone: '',
|
||||
sale: 0,
|
||||
lat: '',
|
||||
lon: '',
|
||||
})
|
||||
|
||||
let communityInfo=ref()
|
||||
|
||||
|
||||
// 同店推荐商品数据
|
||||
const recommendations = ref<Recommendation[]>();
|
||||
|
||||
// 联系商家按钮样式
|
||||
const contactButtonStyle = ref({
|
||||
backgroundColor: '#FF6600',
|
||||
borderColor: '#FF6600',
|
||||
color: '#FFFFFF',
|
||||
fontSize: '28rpx',
|
||||
height: '75rpx',
|
||||
borderRadius: '45rpx',
|
||||
marginRight: '20rpx'
|
||||
});
|
||||
|
||||
// 查看位置按钮样式
|
||||
const locationButtonStyle = ref({
|
||||
backgroundColor: '#FF6600',
|
||||
borderColor: '#FF6600',
|
||||
color: '#FFFFFF',
|
||||
fontSize: '28rpx',
|
||||
height: '75rpx',
|
||||
borderRadius: '45rpx',
|
||||
marginLeft: '20rpx'
|
||||
});
|
||||
|
||||
|
||||
onLoad((data : any) => {
|
||||
merchId.value = data.merchId
|
||||
getData()
|
||||
})
|
||||
|
||||
const getData = () => {
|
||||
status.value = 'loadmore'
|
||||
page.value = 1
|
||||
recommendations.value=[]
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList = () => {
|
||||
if (status.value !== 'loadmore') {
|
||||
return
|
||||
}
|
||||
status.value = 'loading'
|
||||
|
||||
vpHomeService.GetMerchInfo(merchId.value,page.value).then(res=>{
|
||||
merchInfo.value=res.data.merchInfo
|
||||
communityInfo.value=res.data.communityInfo
|
||||
recommendations.value=res.data.merchGoods
|
||||
status.value = res.data.merchGoods.length == 10 ? 'loadmore' : 'nomore'
|
||||
page.value++
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 处理联系商家
|
||||
const handleContactShop = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: merchInfo.value.phone, // 要拨打的电话号码
|
||||
success: function () {
|
||||
console.log('拨打电话成功');
|
||||
},
|
||||
fail: function (err) {
|
||||
console.error('拨打电话失败', err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 处理查看位置
|
||||
const handleViewLocation = () => {
|
||||
wx.openLocation({
|
||||
latitude: Number(merchInfo.value.lat),
|
||||
longitude: Number(merchInfo.value.lon),
|
||||
name: merchInfo.value.name,
|
||||
address: merchInfo.value.address,
|
||||
success: function (e) {
|
||||
console.log(e);
|
||||
},
|
||||
fail: function (e) {
|
||||
console.log(e);
|
||||
}
|
||||
})
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* 商品图片展示 */
|
||||
.product-image-section {
|
||||
width: 100%;
|
||||
height: 500rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.product-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 商家信息 */
|
||||
|
||||
.func-detail {
|
||||
margin-right: 30rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.product-info-section {
|
||||
padding: 30rpx;
|
||||
margin: 30rpx;
|
||||
box-shadow: 0 0 10rpx 4rpx #E2e2e2;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
font-size: 36rpx;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
font-size: 40rpx;
|
||||
color: #FF6600;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.product-description {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
/* 商家信息卡片 */
|
||||
.shop-card-section {
|
||||
padding: 30rpx;
|
||||
margin: 30rpx;
|
||||
box-shadow: 0 0 10rpx 4rpx #E2e2e2;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.shop-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 38rpx;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.shop-tags {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 24rpx;
|
||||
color: #FFFFFF;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.tag.new-shop {
|
||||
background-color: #4CD964;
|
||||
}
|
||||
|
||||
.tag.popular {
|
||||
background-color: #FF9500;
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
font-size: 28rpx;
|
||||
margin-left: 15rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
/* 同店推荐 */
|
||||
.recommendations-section {
|
||||
padding: 0 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 36rpx;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.recommendations-list {
|
||||
gap: 30rpx;
|
||||
}
|
||||
|
||||
.recommendation-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
margin: 20rpx 0;
|
||||
box-shadow: 0 0 10rpx 4rpx #E2e2e2;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.recommendation-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.recommendation-image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 16rpx;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.recommendation-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.recommendation-name {
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
display: block;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.recommendation-price {
|
||||
font-size: 30rpx;
|
||||
color: #FF6600;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
/* 底部操作按钮 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
padding: 30rpx 30rpx;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #ffffff;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<view>
|
||||
积分商城
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow,onLoad } from "@dcloudio/uni-app";
|
||||
onLoad(() => {
|
||||
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
@@ -0,0 +1,312 @@
|
||||
<template>
|
||||
<view class="favorites-page">
|
||||
<!-- 沉浸式状态栏 -->
|
||||
<view class="status-bar"></view>
|
||||
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<image class="back-icon" src="/static/icons/back.svg" @click="goBack" mode="aspectFit" />
|
||||
<text class="nav-title">我的收藏</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
|
||||
<!-- 收藏列表 -->
|
||||
<view class="content">
|
||||
<view v-if="collectList.length > 0" class="favorites-list">
|
||||
<view v-for="item in collectList" :key="item.merchId" class="shop-card" @click="Service.GoPage('/pages/community/merchantDetail?merchId='+item.merchId)">
|
||||
<!-- 店铺图片 -->
|
||||
<image class="shop-image" :src="Service.GetMateUrlByImg(item.logo)" mode="aspectFill" />
|
||||
|
||||
<!-- 店铺信息 -->
|
||||
<view class="shop-info">
|
||||
<text class="shop-name">{{ item.name }}</text>
|
||||
<view class="shop-meta">
|
||||
<text class="ri-star-fill rating-icon"></text>
|
||||
<text class="rating-text">{{ item.score }}分</text>
|
||||
<text class="sales-text">月售{{ item.sale }}</text>
|
||||
</view>
|
||||
<view class="shop-tags">
|
||||
<view v-for="(coupon, idx) in item.tips" :key="idx" :class="getTagClass(coupon)" style="font-size: 24rpx;"
|
||||
class="shop-tag">
|
||||
{{ coupon }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 取消收藏按钮 -->
|
||||
<view class="cancel-btn" @click.stop="cancelFavorite(item)">
|
||||
<text :class="{ 'ri-heart-fill': item.state==1,'ri-heart-line':item.state!==1 }"
|
||||
class="action-pill-icon"></text>
|
||||
</view>
|
||||
</view>
|
||||
<up-loadmore :status="status" />
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-else class="empty-state">
|
||||
<text class="ri-heart-line empty-icon"></text>
|
||||
<text class="empty-text">暂无收藏店铺</text>
|
||||
<text class="empty-desc">去首页逛逛,收藏喜欢的店铺吧</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {onShow,onLoad,onReachBottom} from "@dcloudio/uni-app";
|
||||
import {Service} from "@/Service/Service"
|
||||
import { ref, computed } from "vue";
|
||||
import { vpDiscountService } from "@/Service/vp/vpDiscountService"
|
||||
import { vpUserService } from "@/Service/vp/vpUserService"
|
||||
|
||||
|
||||
let collectList = ref<Array<any>>([])
|
||||
let status = ref<string>('loadmore')
|
||||
let pageNo = ref<number>(1)
|
||||
let longitude=ref(0)
|
||||
let latitude=ref(0)
|
||||
|
||||
|
||||
onLoad(()=>{
|
||||
getLocation()
|
||||
})
|
||||
onReachBottom(()=>{
|
||||
getList()
|
||||
})
|
||||
|
||||
// 返回
|
||||
const goBack = () => {
|
||||
Service.GoPageBack()
|
||||
}
|
||||
|
||||
|
||||
const getLocation = () => {
|
||||
uni.getLocation({
|
||||
type: 'wgs84',
|
||||
success: function (res) {
|
||||
longitude.value = res.longitude
|
||||
latitude.value = res.latitude
|
||||
getData()
|
||||
},
|
||||
fail: function (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const getData=()=>{
|
||||
pageNo.value=1
|
||||
status.value='loadmore'
|
||||
collectList.value=[]
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList=()=>{
|
||||
if (status.value !== 'loadmore') {
|
||||
return
|
||||
}
|
||||
status.value = 'loading'
|
||||
vpUserService.GetUserCollectList(longitude.value,latitude.value,pageNo.value).then(res=>{
|
||||
if(res.code==0){
|
||||
collectList.value=[...collectList.value,...res.data.collectList]
|
||||
status.value=res.data.collectList.length==10?'loadmore':'nomore'
|
||||
pageNo.value++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 根据标签文本获取样式类
|
||||
const getTagClass = (tagText : string) => {
|
||||
const tagMap = {
|
||||
'可用积分': 'tag-points-available',
|
||||
'可用券': 'tag-coupon'
|
||||
}
|
||||
return tagMap[tagText] || 'tag-points'
|
||||
}
|
||||
|
||||
|
||||
// 取消收藏
|
||||
const cancelFavorite = (item:any) => {
|
||||
uni.showModal({
|
||||
title: '取消收藏',
|
||||
content: `确定取消收藏吗?`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
vpUserService.CollectMerch(item.merchId).then(res=>{
|
||||
if(res.code==0){
|
||||
getData()
|
||||
}else{
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.favorites-page {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
.action-pill-icon {
|
||||
font-size: 28rpx;
|
||||
color: #FF6B00;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 状态栏 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 60rpx 24rpx 20rpx 24rpx;
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.nav-placeholder {
|
||||
width: 48rpx;
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.content {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
/* 收藏列表 */
|
||||
.favorites-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.shop-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.shop-image {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 12rpx;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.shop-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.rating-icon {
|
||||
font-size: 24rpx;
|
||||
color: #FFB800;
|
||||
}
|
||||
|
||||
.rating-text {
|
||||
font-size: 24rpx;
|
||||
color: #FF6B00;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.sales-text {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.shop-tags {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 20rpx;
|
||||
color: #666666;
|
||||
background: #F5F5F5;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background: #FFEBEE;
|
||||
border-radius: 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cancel-icon {
|
||||
font-size: 32rpx;
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 200rpx 0;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 120rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.empty-desc {
|
||||
font-size: 24rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
</style>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,29 @@
|
||||
import { Service } from '@/Service/Service';
|
||||
/*****地址*****/
|
||||
class vpMerchService {
|
||||
private static GetMerchListPath: string = '/Merch/GetMerchList';
|
||||
/*****商家列表*****/
|
||||
static GetMerchList(assId: string,comId:string,lon:number,lat:number,page:number) {
|
||||
var result = Service.Request(this.GetMerchListPath, 'GET', {assId,comId,lon,lat,page});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static GetMerchInfoPath: string = '/Merch/GetMerchInfo';
|
||||
/*****店铺详情*****/
|
||||
static GetMerchInfo(merchId: string,page:number) {
|
||||
var result = Service.Request(this.GetMerchInfoPath, 'GET', {merchId,page});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static GetGoodsInfoPath: string = '/Merch/GetGoodsInfo';
|
||||
/*****商品详情*****/
|
||||
static GetGoodsInfo(goodsId: string,page:number) {
|
||||
var result = Service.Request(this.GetGoodsInfoPath, 'GET', {goodsId,page});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
export { Service, vpMerchService };
|
||||
3234
.svn/pristine/0a/0a5a721c7f53af1e0aa1229276b96258e9b39d75.svn-base
Normal file
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<view style="padding: 20rpx; ">
|
||||
|
||||
|
||||
<!-- 当前积分区域 -->
|
||||
<view class="points-detail-section" style="margin-bottom: 20rpx;">
|
||||
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<view class="" style="font-size: 30rpx; color: #999;">
|
||||
积分余额
|
||||
</view>
|
||||
<view class="" style="color: red; font-weight: bold; font-size: 34rpx; ">
|
||||
{{ integral }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="margin-top: 20rpx;">
|
||||
<u-button type="primary" @click="Service.GoPage('/pages/userFunc/withdrow')"
|
||||
:custom-style="contactButtonStyle">立即提现</u-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 积分明细 -->
|
||||
<view class="points-detail-section">
|
||||
<text class="section-title">积分明细</text>
|
||||
<view class="points-list">
|
||||
<view class="points-item" v-for="(item, index) in pointsList" :key="index">
|
||||
<text :class="['points-number', item.code=='收入' ? 'positive' : 'negative']">
|
||||
{{ item.code=='收入' ? '+' : '-' }}{{ item.amount }}
|
||||
</text>
|
||||
<view class="" style="margin-left: 20rpx;">
|
||||
<view class="points-type" style="font-size: 26rpx; font-weight: bold; ">{{ item.name }}</view>
|
||||
<view class="points-date" style="margin-top: 4rpx;">{{ Service.formatDate(item.addTime,1) }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="pointsList.length==0" class="" style=" font-size: 28rpx; text-align: center;padding: 10rpx 0;">
|
||||
暂无记录
|
||||
</view>
|
||||
<up-loadmore v-if="pointsList.length!==0" :status="status" />
|
||||
</view>
|
||||
|
||||
<!-- <scroll-view scroll-y="true" class="scroll-Y" >
|
||||
<text class="section-title">积分明细</text>
|
||||
<view class="points-list">
|
||||
<view class="points-item" v-for="(item, index) in pointsList" :key="index">
|
||||
<text :class="['points-number', item.code=='收入' ? 'positive' : 'negative']">
|
||||
{{ item.code=='收入' ? '+' : '-' }}{{ item.amount }}
|
||||
</text>
|
||||
<view class="" style="margin-left: 20rpx;">
|
||||
<view class="points-type" style="font-size: 26rpx; font-weight: bold; ">{{ item.name }}</view>
|
||||
<view class="points-date" style="margin-top: 4rpx;">{{ Service.formatDate(item.addTime,1) }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="pointsList.length==0" class="" style=" font-size: 28rpx; text-align: center;padding: 10rpx 0;">
|
||||
暂无记录
|
||||
</view>
|
||||
<up-loadmore v-if="pointsList.length!==0" :status="status" />
|
||||
</scroll-view> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { vpUserService } from '@/Service/vp/vpUserService'
|
||||
import { Service, vpMerchService } from '@/Service/vp/vpMerchService'
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
// 积分明细数据类型
|
||||
interface PointsItem {
|
||||
amount : number
|
||||
code : string
|
||||
name : string
|
||||
addTime : string
|
||||
}
|
||||
|
||||
// 按钮样式
|
||||
const contactButtonStyle = ref({
|
||||
backgroundColor: '#FF6600',
|
||||
borderColor: '#FF6600',
|
||||
color: '#FFFFFF',
|
||||
fontSize: '28rpx',
|
||||
height: '75rpx',
|
||||
borderRadius: '45rpx',
|
||||
marginRight: '20rpx'
|
||||
});
|
||||
|
||||
let integral = ref(0)
|
||||
let status = ref('loadmore')
|
||||
let page = ref(1)
|
||||
// 积分明细数据
|
||||
const pointsList = ref<PointsItem[]>([
|
||||
|
||||
]);
|
||||
|
||||
|
||||
onLoad(() => {
|
||||
getUseraccInfo()
|
||||
})
|
||||
|
||||
|
||||
|
||||
onReachBottom(() => {
|
||||
console.log(1111);
|
||||
getList()
|
||||
})
|
||||
|
||||
|
||||
|
||||
const getUseraccInfo = () => {
|
||||
|
||||
status.value = 'loadmore'
|
||||
page.value = 1
|
||||
pointsList.value = []
|
||||
getList()
|
||||
}
|
||||
|
||||
|
||||
const getList = () => {
|
||||
|
||||
if (status.value !== 'loadmore') {
|
||||
return
|
||||
}
|
||||
status.value = 'loading'
|
||||
vpMerchService.GetMerchAccInfo(page.value).then(res => {
|
||||
integral.value = res.data.merchAcc.integral
|
||||
pointsList.value = [...pointsList.value, ...res.data.merchAccLog]
|
||||
status.value = res.data.merchAccLog.length == 10 ? 'loadmore' : 'nomore'
|
||||
page.value++
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
/* 积分明细 */
|
||||
.points-detail-section {
|
||||
background-color: #ffffff;
|
||||
padding: 40rpx 30rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.points-list {
|
||||
gap: 36rpx;
|
||||
}
|
||||
|
||||
.points-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
|
||||
.points-item:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.points-type {
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.points-number {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
margin-right: 24rpx;
|
||||
min-width: 80rpx;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.points-number.positive {
|
||||
color: #4CD964;
|
||||
}
|
||||
|
||||
.points-number.negative {
|
||||
color: #FF4D4F;
|
||||
}
|
||||
|
||||
.points-date {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
</style>
|
||||
|
After Width: | Height: | Size: 614 B |
|
After Width: | Height: | Size: 7.8 KiB |
@@ -0,0 +1,735 @@
|
||||
<template>
|
||||
<view class="" style="padding: 30rpx; height: 100vh; overflow: auto; ">
|
||||
|
||||
<view class="" style="position: relative;">
|
||||
|
||||
<image v-if="!storeData.photo" :src="Service.GetIconImg('/static/userFunc/null.png')"
|
||||
style="border-radius: 20rpx; width: 750rpx; height: 500rpx; " mode="aspectFit" alt="" />
|
||||
<image v-else :src="Service.GetMateUrlByImg(storeData.photo)"
|
||||
style="border-radius: 20rpx; width: 750rpx; height: 500rpx; " mode="aspectFill" alt="" />
|
||||
<view @click="uploaduserImg(750,500,'MerchShow','showImg'),imgType='logo'"
|
||||
style=" display: flex; align-items: center; justify-content: center; border-radius: 50%; width: 70rpx; height: 70rpx; position: absolute; bottom: 24rpx; right: 12rpx; background-color: #F97316; ">
|
||||
<img :src="Service.GetIconImg('/static/userFunc/photo-store.png')"
|
||||
style="width: 40rpx; height: 40rpx; " alt="" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view class=""
|
||||
style=" margin-top: 20rpx; padding: 20rpx;border-radius: 20rpx; box-shadow: 0 0 10rpx 4rpx #E2e2e2; ">
|
||||
<up-form labelWidth='300rpx' :labelStyle="{'font-weight':600}" labelPosition="top" :model="storeData"
|
||||
ref="form1">
|
||||
<up-form-item label="店铺名称: " :borderBottom="true" ref="item1">
|
||||
<up-input v-if="!upImgShow" v-model="storeData.name" placeholder="请输入店铺名称" border="none"></up-input>
|
||||
</up-form-item>
|
||||
<up-form-item @click="showStoreClass=true" label="店铺分类: " :borderBottom="true" ref="item1">
|
||||
<view class="" >
|
||||
<view v-if="!storeData.storeClass" class=""
|
||||
style="display: flex; align-items: center;">
|
||||
<text style="font-size: 26rpx; color: #c0c4cc; margin-right: 10rpx;">请选择店铺分类 </text>
|
||||
<up-icon name="arrow-right" color="#c0c4cc" size="12"></up-icon>
|
||||
</view>
|
||||
<view v-else class="">
|
||||
{{storeData.storeClass}}
|
||||
</view>
|
||||
</view>
|
||||
</up-form-item>
|
||||
<up-form-item label="店铺地址: " @click="chooseAddress()" :borderBottom="true" ref="item1">
|
||||
<view class="">
|
||||
<view v-if="!storeData.province" class=""
|
||||
style="display: flex; align-items: center;">
|
||||
<text style="font-size: 26rpx; color: #c0c4cc; margin-right: 10rpx;">请选择地址 </text>
|
||||
<up-icon name="arrow-right" color="#c0c4cc" size="12"></up-icon>
|
||||
</view>
|
||||
<view v-else class="">
|
||||
{{storeData.province+storeData.city+storeData.county}}
|
||||
</view>
|
||||
</view>
|
||||
</up-form-item>
|
||||
<up-form-item label="详细地址: " :borderBottom="true" ref="item1">
|
||||
<up-input v-if="!upImgShow" v-model="storeData.address" placeholder="请输入您的详细地址"
|
||||
border="none"></up-input>
|
||||
</up-form-item>
|
||||
<up-form-item label="平均消费金额: " :borderBottom="true" ref="item1">
|
||||
<up-input v-if="!upImgShow" v-model="storeData.price" placeholder="请输入您的平均消费金额"
|
||||
border="none"></up-input>
|
||||
</up-form-item>
|
||||
<up-form-item label="联系电话: " :borderBottom="true" ref="item1">
|
||||
<up-input v-if="!upImgShow" v-model="storeData.phone" placeholder="请输入您的联系电话"
|
||||
border="none"></up-input>
|
||||
</up-form-item>
|
||||
<up-form-item label="营业时间: " :borderBottom="true" ref="item1">
|
||||
<view class="">
|
||||
<view v-if="!storeData.time" @click="showDate=!showDate" class=""
|
||||
style="display: flex; align-items: center;">
|
||||
<text style="font-size: 26rpx; color: #c0c4cc; margin-right: 10rpx;">请选择营业时间 </text>
|
||||
<up-icon name="arrow-right" color="#c0c4cc" size="12"></up-icon>
|
||||
</view>
|
||||
<view v-else @click="showDate=!showDate" class="">
|
||||
{{storeData.time}}
|
||||
</view>
|
||||
</view>
|
||||
</up-form-item>
|
||||
|
||||
</up-form>
|
||||
</view>
|
||||
|
||||
<!-- <view class=""
|
||||
style="margin-top: 20rpx; padding: 20rpx;border-radius: 20rpx; box-shadow: 0 0 10rpx 4rpx #E2e2e2; ">
|
||||
<view style="display: flex; align-items: center; justify-content: space-between;" class="">
|
||||
<view class="" style="font-weight: 600;">
|
||||
店铺标签
|
||||
</view>
|
||||
<view class="">
|
||||
<up-icon @click="showTagfunc()" name="plus" color="#000" size="16" :bold='true'></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="display: grid; grid-template-columns: repeat(4,1fr); ">
|
||||
<view class="tag" v-for="(item,index) in tagList" :key="index"
|
||||
style=" position: relative; margin-top: 15rpx; padding: 10rpx 20rpx; border-radius: 8rpx; margin-right: 15rpx; background-color: var(--nav-mian); color: #fff; ">
|
||||
{{item}}
|
||||
|
||||
<view class="" @click="deleTag(index)" style="position: absolute; right: -10rpx; top: -8rpx; " >
|
||||
<up-icon size="12" :bold='true' color=" var(--nav-mian)" name="close"></up-icon>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- <view class=""
|
||||
style="margin-top: 20rpx; padding: 20rpx;border-radius: 20rpx; box-shadow: 0 0 10rpx 4rpx #E2e2e2; ">
|
||||
<view style="display: flex; align-items: center; justify-content: space-between;" class="">
|
||||
<view class="" style="font-weight: 600;">
|
||||
所属社区
|
||||
</view>
|
||||
<view class="">
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="padding: 15rpx 0;">
|
||||
<view v-if="!storeData.community" @click="showCommunity=!showCommunity" class=""
|
||||
style="display: flex; align-items: center;">
|
||||
<text style="font-size: 26rpx; color: #c0c4cc; margin-right: 10rpx;">请选择所属社区 </text>
|
||||
<up-icon name="arrow-right" color="#c0c4cc" size="12"></up-icon>
|
||||
</view>
|
||||
<view v-else @click="showCommunity=!showCommunity" class="">
|
||||
{{storeData.community}}
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
|
||||
<view class="section" style="margin-top: 20rpx;" >
|
||||
<view style="font-size: 34rpx; font-weight: 600; margin-bottom: 20rpx;">商家展示图(最多三张)</view>
|
||||
<view class="uploader-wrapper" style="">
|
||||
<view v-if="storeData.imgs.length<3" @click="uploaduserImg(414,248,'StoreGoodsImg','轮播图'),imgType='img'"
|
||||
class="upload-slot" >
|
||||
<up-icon name="plus-circle-fill" size="30" color="#fa6400"></up-icon>
|
||||
<text>点击上传图片</text>
|
||||
</view>
|
||||
<view v-if="storeData.imgs.length>0" v-for="(imgItem,imgIndex) in storeData.imgs" :key="imgIndex"
|
||||
class="upload-slot"
|
||||
style=" position: relative; overflow: hidden; margin-top: 15rpx; ">
|
||||
<image :src="Service.GetMateUrlByImg(imgItem)" style="width: 100%; height: 100%; "
|
||||
mode=""></image>
|
||||
<view class="" @click="deleImg(imgIndex)"
|
||||
style="position: absolute; top: 20rpx; right: 20rpx; ">
|
||||
<up-icon name="close" bold="true" color="var(--nav-mian)" size="20"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="" style="width: 100%; height: 200rpx; ">
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="" @click="save()"
|
||||
style=" z-index: 1000; width: 100%; background-color: #fff; position: fixed; bottom: 0; left: 0; padding: 20rpx; ">
|
||||
<up-button shape='circle' color='#FF6B35' text="保存修改"></up-button>
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
<!-- 弹窗 -->
|
||||
<up-popup :show="showDate">
|
||||
<view style="width: 100%; padding: 30rpx; ">
|
||||
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<view class="">
|
||||
|
||||
</view>
|
||||
<up-icon @click="showDate=!showDate" name="close" color="#000" size="18"></up-icon>
|
||||
</view>
|
||||
<up-form labelWidth='180rpx' :labelStyle="{'font-weight':600}" labelPosition="top" :model="storeData"
|
||||
ref="form1">
|
||||
<up-form-item label="营业时间: ">
|
||||
<view class="" style=" width: 100%; display: grid; grid-template-columns: repeat(4,1fr);">
|
||||
<view v-for="(item,index) in 7" @click="chooseWeek(index)"
|
||||
style=" margin-top: 15rpx; font-size: 32rpx; padding: 10rpx 30rpx;border-radius: 14rpx;"
|
||||
:class="{ tab:weekList.findIndex(item => item == index)===-1,tabActive: weekList.findIndex(item => item == index)!=-1 }"
|
||||
:key="index" class="tag">
|
||||
周{{chinese(index)}}
|
||||
</view>
|
||||
</view>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="开业时间: " labelPosition="left">
|
||||
<view v-if="!openTime" @click="showOpenTime=!showOpenTime" class=""
|
||||
style="display: flex; align-items: center;">
|
||||
<text style="font-size: 26rpx; color: #c0c4cc; margin-right: 10rpx;">请选择开业时间 </text>
|
||||
<up-icon name="arrow-right" color="#c0c4cc" size="12"></up-icon>
|
||||
</view>
|
||||
<view v-else @click="showOpenTime=!showOpenTime" class="">
|
||||
{{openTime}}
|
||||
</view>
|
||||
</up-form-item>
|
||||
<up-form-item label="结业时间: " labelPosition="left">
|
||||
<view v-if="!closeTime" @click="showCloseTime=!showCloseTime" class=""
|
||||
style="display: flex; align-items: center;">
|
||||
<text style="font-size: 26rpx; color: #c0c4cc; margin-right: 10rpx;">请选择开业时间 </text>
|
||||
<up-icon name="arrow-right" color="#c0c4cc" size="12"></up-icon>
|
||||
</view>
|
||||
<view v-else @click="showCloseTime=!showCloseTime" class="">
|
||||
{{closeTime}}
|
||||
</view>
|
||||
</up-form-item>
|
||||
|
||||
</up-form>
|
||||
<view class="" style="margin-top: 20rpx;">
|
||||
<up-button @click="dateconfiom" text="确认"></up-button>
|
||||
</view>
|
||||
<up-datetime-picker @confirm='showOpenTime=!showOpenTime' @cancel="showOpenTime=!showOpenTime"
|
||||
:show="showOpenTime" v-model="openTime" mode="time"></up-datetime-picker>
|
||||
<up-datetime-picker @confirm='showCloseTime=!showCloseTime' @cancel="showCloseTime=!showCloseTime"
|
||||
:show="showCloseTime" v-model="closeTime" mode="time"></up-datetime-picker>
|
||||
|
||||
</view>
|
||||
</up-popup>
|
||||
<up-popup :show="showTag">
|
||||
<view style="width: 100%; padding: 30rpx; ">
|
||||
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<view class="">
|
||||
|
||||
</view>
|
||||
<up-icon @click="showTag=!showTag" name="close" color="#000" size="18"></up-icon>
|
||||
</view>
|
||||
<view class="">
|
||||
<view style="display: flex; align-items: center; justify-content: space-between;" class="">
|
||||
<view class="">
|
||||
店铺标签
|
||||
</view>
|
||||
<view class="">
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="margin: 20rpx 0;">
|
||||
<up-input placeholder="请输入标签" border="bottom" v-model="tag"></up-input>
|
||||
</view>
|
||||
<view class="" style="margin-top: 20rpx;">
|
||||
<up-button @click="addTag()" text="确认"></up-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</up-popup>
|
||||
<!-- 社区 -->
|
||||
<up-picker :show="showCommunity" @confirm="communityFunc" @cancel='showCommunity=!showCommunity' :columns="columns"
|
||||
keyName="name" valueName="name"></up-picker>
|
||||
|
||||
<up-picker :show="showStoreClass" @confirm="storeClassFunc" @cancel='showStoreClass=!showStoreClass' :columns="storeColumns"
|
||||
keyName="name" valueName="assortId"></up-picker>
|
||||
|
||||
<ImageCropperFunc style="z-index: 200;" :show="upImgShow" :url="upImgUrl" :imgName="imgName" :width="upImgwidth"
|
||||
:height="upImgheight" :upType="upImgType" :retFun="upImgData">
|
||||
</ImageCropperFunc>
|
||||
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { Service } from "@/Service/Service"
|
||||
import { ref } from "vue";
|
||||
import { vpCommunityService } from '@/Service/vp/vpCommunityService'
|
||||
import ImageCropperFunc from "@/components/ImageCropper"
|
||||
import { vpMerchService } from '@/Service/vp/vpMerchService'
|
||||
|
||||
|
||||
|
||||
interface data {
|
||||
photo : string,
|
||||
name : string,
|
||||
address : string,
|
||||
phone : string,
|
||||
time : string,
|
||||
community : string
|
||||
comId : string
|
||||
province : string
|
||||
city : string
|
||||
county : string
|
||||
latitude : number
|
||||
longitude : number
|
||||
price:any
|
||||
assortId:any
|
||||
storeClass:any
|
||||
imgs:Array<any>
|
||||
}
|
||||
|
||||
|
||||
let storeData = ref<data>({
|
||||
assortId:'',
|
||||
storeClass:'',
|
||||
price:null,
|
||||
photo: '',
|
||||
name: '',
|
||||
address: '',
|
||||
phone: '',
|
||||
time: '',
|
||||
community: '',
|
||||
comId: '',
|
||||
province: '',
|
||||
city: '',
|
||||
county: '',
|
||||
latitude: 0,
|
||||
longitude: 0,
|
||||
imgs:[]
|
||||
})
|
||||
|
||||
|
||||
// 时间
|
||||
let showDate = ref(false)
|
||||
let weekList = ref<Array<number>>([])
|
||||
let openTime = ref()
|
||||
let showOpenTime = ref(false)
|
||||
let closeTime = ref()
|
||||
let showCloseTime = ref(false)
|
||||
// 时间方式
|
||||
let timefunc = ref(-1)
|
||||
|
||||
// 照片类型(logo,展示图)
|
||||
let imgType=ref('')
|
||||
|
||||
// 标签
|
||||
let showTag = ref(false)
|
||||
let tag = ref()
|
||||
let tagList = ref<Array<string>>([])
|
||||
|
||||
// 社区
|
||||
let showCommunity = ref(false)
|
||||
const columns = ref([]);
|
||||
|
||||
|
||||
// 店铺分类
|
||||
let showStoreClass = ref(false)
|
||||
const storeColumns = ref<Array<any>>([]);
|
||||
|
||||
|
||||
|
||||
// 裁剪图片参数
|
||||
let upImgShow = ref<boolean>(false)
|
||||
let upImgUrl = ref<string>('')
|
||||
let upImgwidth = ref<number>(0)
|
||||
let upImgheight = ref<number>(0)
|
||||
let upImgType = ref<string>('')
|
||||
let imgName = ref<string>('')
|
||||
|
||||
onLoad(() => {
|
||||
getStoreClass()
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
|
||||
const getData=()=>{
|
||||
vpMerchService.GetMyMerchInfo().then(res=>{
|
||||
storeData.value.address=res.data.merchInfo.address
|
||||
storeData.value.city=res.data.merchInfo.city
|
||||
// storeData.value.comId=res.data.merchInfo.communityId
|
||||
// columns.value[0].map((item)=>{
|
||||
// if(item.communityId==res.data.merchInfo.communityId){
|
||||
// storeData.value.community=item.name
|
||||
// }
|
||||
// })
|
||||
storeData.value.assortId=res.data.merchInfo.assId
|
||||
storeColumns.value[0].map(item=>{
|
||||
if(item.assortId==res.data.merchInfo.assId){
|
||||
storeData.value.storeClass= item.name
|
||||
}
|
||||
})
|
||||
storeData.value.county=res.data.merchInfo.county
|
||||
storeData.value.latitude=res.data.merchInfo.lat
|
||||
storeData.value.longitude=res.data.merchInfo.lon
|
||||
storeData.value.name=res.data.merchInfo.name
|
||||
storeData.value.phone=res.data.merchInfo.phone
|
||||
storeData.value.photo=res.data.merchInfo.logo
|
||||
storeData.value.imgs=JSON.parse(res.data.merchInfo.showImg)
|
||||
storeData.value.price=res.data.merchInfo.price
|
||||
storeData.value.province=res.data.merchInfo.province
|
||||
storeData.value.time=!res.data.merchInfo.busTime?'': timeCancle(res.data.merchInfo.busTime)
|
||||
tagList.value=JSON.parse(res.data.merchInfo.tag)==null?new Array():JSON.parse(res.data.merchInfo.tag)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
const getStoreClass=()=>{
|
||||
vpMerchService.GetMerchAssort().then(res=>{
|
||||
storeColumns.value[0]=res.data.assortList
|
||||
getData()
|
||||
})
|
||||
}
|
||||
|
||||
// 商家分类
|
||||
const storeClassFunc = (e : any) => {
|
||||
showStoreClass.value=false
|
||||
storeData.value.storeClass = e.value[0].name
|
||||
storeData.value.assortId = e.value[0].assortId
|
||||
}
|
||||
|
||||
const getCommunityList = () => {
|
||||
vpCommunityService.GetCommunityList().then(res => {
|
||||
columns.value[0] = res.data.comList
|
||||
getData()
|
||||
})
|
||||
}
|
||||
|
||||
const deleImg=(index:any)=>{
|
||||
storeData.value.imgs.splice(index,1)
|
||||
}
|
||||
|
||||
// 社区
|
||||
const communityFunc = (e : any) => {
|
||||
showCommunity.value = !showCommunity.value
|
||||
storeData.value.community = e.value[0].name
|
||||
storeData.value.comId = e.value[0].communityId
|
||||
}
|
||||
|
||||
|
||||
// 标签
|
||||
const showTagfunc = () => {
|
||||
showTag.value = !showTag.value
|
||||
tag.value = ''
|
||||
}
|
||||
|
||||
const addTag = () => {
|
||||
if (!tag.value) {
|
||||
Service.Msg('请输入标签内容')
|
||||
return
|
||||
}
|
||||
tagList.value.push(tag.value)
|
||||
showTag.value = !showTag.value
|
||||
}
|
||||
|
||||
|
||||
const deleTag=(index:number)=>{
|
||||
tagList.value.splice(index,1)
|
||||
}
|
||||
|
||||
// 选择照片
|
||||
const uploaduserImg = (width : any, height : any, Type : any, Name : any) => {
|
||||
|
||||
uni.chooseImage({
|
||||
count: 1, // 最多选择1张图片
|
||||
sizeType: ['original', 'compressed'], // 支持原图和压缩图
|
||||
sourceType: ['album', 'camera'], // 可从相册选择或使用相机拍照
|
||||
success: function (res) {
|
||||
let path = res.tempFiles[0].path
|
||||
upImgUrl.value = path
|
||||
upImgwidth.value = width
|
||||
upImgheight.value = height
|
||||
upImgType.value = Type
|
||||
imgName.value = Name
|
||||
upImgShow.value = true
|
||||
},
|
||||
fail: function (err) {
|
||||
console.error('选择失败:', err.errMsg);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const upImgData = (e : any) => {
|
||||
console.log(e);
|
||||
upImgShow.value = false
|
||||
if(imgType.value=='logo'){
|
||||
storeData.value.photo = e.url
|
||||
}else{
|
||||
storeData.value.imgs.push(Service.GetMateUrlByImg(e.url))
|
||||
console.log(storeData.value.imgs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 地址
|
||||
const chooseAddress = () => {
|
||||
wx.chooseLocation({
|
||||
success: res => {
|
||||
storeData.value.province = res.address.split('省')[0] + '省'
|
||||
storeData.value.city = res.address.split('省')[1].split('市')[0] + '市'
|
||||
storeData.value.county = res.address.split('省')[1].split('市')[1].split('区')[0] + '区'
|
||||
storeData.value.address = res.address.split('省')[1].split('市')[1].split('区')[1]
|
||||
storeData.value.longitude = res.longitude
|
||||
storeData.value.latitude = res.latitude
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const chooseWeek = (index : number) => {
|
||||
if (weekList.value.findIndex(item => item == index) != -1) {
|
||||
let i = weekList.value.findIndex(item => item == index)
|
||||
weekList.value.splice(i, 1)
|
||||
return
|
||||
}
|
||||
weekList.value.push(index)
|
||||
}
|
||||
|
||||
const chinese = (item : number) => {
|
||||
if (item + 1 == 1) {
|
||||
return '一'
|
||||
}
|
||||
if (item + 1 == 2) {
|
||||
return '二'
|
||||
} if (item + 1 == 3) {
|
||||
return '三'
|
||||
} if (item + 1 == 4) {
|
||||
return '四'
|
||||
} if (item + 1 == 5) {
|
||||
return '五'
|
||||
} if (item + 1 == 6) {
|
||||
return '六'
|
||||
} if (item + 1 == 7) {
|
||||
return '日'
|
||||
}
|
||||
}
|
||||
|
||||
const dateconfiom = () => {
|
||||
storeData.value.time = ''
|
||||
if (weekList.value.length == 0) {
|
||||
Service.Msg('请选择营业时间')
|
||||
return
|
||||
}
|
||||
|
||||
if (!openTime.value) {
|
||||
Service.Msg('请选择开业时间')
|
||||
return
|
||||
}
|
||||
if (!closeTime.value) {
|
||||
Service.Msg('请选择结业时间')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (openTime.value > closeTime.value) {
|
||||
Service.Msg('开业时间不得小于结业时间')
|
||||
return
|
||||
}
|
||||
|
||||
weekList.value.sort((a, b) => {
|
||||
return a - b
|
||||
})
|
||||
|
||||
let timeIndex = weekList.value[0] - 0
|
||||
let judgment = weekList.value.findIndex((item, index) => {
|
||||
return item - index !== timeIndex
|
||||
})
|
||||
|
||||
// 1是至 /0是全显示
|
||||
if (judgment == -1) {
|
||||
timefunc.value = 1
|
||||
} else {
|
||||
timefunc.value = 0
|
||||
}
|
||||
|
||||
if (timefunc.value == 0) {
|
||||
weekList.value.map((item) => {
|
||||
storeData.value.time = storeData.value.time + '周' + chinese(item) + ' '
|
||||
})
|
||||
} else {
|
||||
storeData.value.time = '周' + chinese((weekList.value[0])) + '至' + '周' + chinese((weekList.value[weekList.value.length - 1]))
|
||||
}
|
||||
storeData.value.time = storeData.value.time + ' ' + openTime.value + '-' + closeTime.value
|
||||
|
||||
showDate.value = !showDate.value
|
||||
}
|
||||
|
||||
|
||||
|
||||
const save = () => {
|
||||
if(!storeData.value.photo){
|
||||
Service.Msg('请上传图片!')
|
||||
return
|
||||
}
|
||||
if(!storeData.value.name){
|
||||
Service.Msg('请输入店铺名称!')
|
||||
return
|
||||
}
|
||||
if(!storeData.value.storeClass){
|
||||
Service.Msg('请选择店铺吗分类!')
|
||||
return
|
||||
}
|
||||
if(!storeData.value.province && !storeData.value.city && storeData.value.county ){
|
||||
Service.Msg('请选择地址!')
|
||||
return
|
||||
}
|
||||
if(!storeData.value.address){
|
||||
Service.Msg('请输入详细地址!')
|
||||
return
|
||||
}
|
||||
if(!storeData.value.price){
|
||||
Service.Msg('请输入平均消费金额!')
|
||||
return
|
||||
}
|
||||
if(!storeData.value.phone){
|
||||
Service.Msg('请输入联系电话!')
|
||||
return
|
||||
}
|
||||
if(!storeData.value.time){
|
||||
Service.Msg('请输入营业时间!')
|
||||
return
|
||||
}
|
||||
|
||||
if(storeData.value.imgs.length==0){
|
||||
Service.Msg('请上传店铺展示图!')
|
||||
return
|
||||
}
|
||||
|
||||
// if(tagList.value.length==0){
|
||||
// Service.Msg('请添加店铺标签!')
|
||||
// return
|
||||
// }
|
||||
// if(!storeData.value.comId){
|
||||
// Service.Msg('请选择所属社区!')
|
||||
// return
|
||||
// }
|
||||
// start时间处理
|
||||
let time = ''
|
||||
let newWeekList = new Array()
|
||||
weekList.value.forEach((item, index) => {
|
||||
let date = item + 1
|
||||
if (date == 7) {
|
||||
date = 0
|
||||
}
|
||||
newWeekList.push(date)
|
||||
})
|
||||
|
||||
newWeekList.sort((a, b) => {
|
||||
return a - b
|
||||
})
|
||||
|
||||
newWeekList.forEach((item, index) => {
|
||||
time = time + (time == '' ? '' : '-') + item
|
||||
})
|
||||
time = time + '_' + storeData.value.time.split(' ')[storeData.value.time.split(' ').length - 1]
|
||||
// end时间处理
|
||||
let tag = JSON.stringify(tagList.value)
|
||||
let imgs=JSON.stringify(storeData.value.imgs)
|
||||
vpMerchService.UpdateMerch( storeData.value.assortId,storeData.value.photo , '',storeData.value.name,imgs,storeData.value.phone,storeData.value.price,storeData.value.province,storeData.value.city,storeData.value.county,storeData.value.address,storeData.value.longitude,storeData.value.latitude,time,'').then(res=>{
|
||||
if(res.code==0){
|
||||
Service.Msg('保存成功!')
|
||||
setTimeout(()=>{
|
||||
Service.GoPageBack()
|
||||
},2500)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const timeCancle=(busTime:string)=>{
|
||||
let data= busTime.split('_')
|
||||
openTime.value=data[1].split('-')[0]
|
||||
closeTime.value=data[1].split('-')[1]
|
||||
let timeData=data[0].split('-')
|
||||
for(let i=0;i<timeData.length;i++){
|
||||
if(timeData[i]=='0'){
|
||||
timeData[i]='7'
|
||||
}
|
||||
}
|
||||
let time=''
|
||||
timeData.sort((a:any,b:any)=>{
|
||||
return a-b
|
||||
})
|
||||
weekList.value=[]
|
||||
timeData.map((item:any)=>{
|
||||
weekList.value.push(item=='7'?(Number(6)):Number(item-1))
|
||||
})
|
||||
let timeIndex = weekList.value[0] - 0
|
||||
let judgment = weekList.value.findIndex((item, index) => {
|
||||
return item - index !== timeIndex
|
||||
})
|
||||
|
||||
// 1是至 /0是全显示
|
||||
if (judgment == -1) {
|
||||
timefunc.value = 1
|
||||
} else {
|
||||
timefunc.value = 0
|
||||
}
|
||||
|
||||
if (timefunc.value == 0) {
|
||||
weekList.value.map((item) => {
|
||||
time = time + '周' + chinese(item) + ' '
|
||||
})
|
||||
} else {
|
||||
time = '周' + chinese((weekList.value[0])) + '至' + '周' + chinese((weekList.value[weekList.value.length - 1]))
|
||||
}
|
||||
|
||||
time=time+data[1]
|
||||
|
||||
return time
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
.section {
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin-bottom: 20rpx;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.uploader-wrapper {
|
||||
.upload-slot {
|
||||
width: 100%;
|
||||
height: 300rpx;
|
||||
background-color: #fff8f5;
|
||||
border: 1rpx dashed #e2e2e2;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16rpx;
|
||||
font-size: 26rpx;
|
||||
color: #fa6400;
|
||||
}
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: fit-content;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.tabActive {
|
||||
background-color: var(--nav-mian);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tab {
|
||||
background-color: #F5F5F5;
|
||||
color: #333333
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<view style="padding: 20rpx;">
|
||||
<up-search placeholder="搜索相关内容..." :showAction='false' v-model="search"></up-search>
|
||||
|
||||
<view class="" style=" margin: 25rpx 0; display: flex; align-items: center; justify-content: space-between;" >
|
||||
<view @click="changetab(index)" v-for="(item,index) in tag" :key="index" :class="{active:index!=currentIndex,actived:index==currentIndex}" class="tag" style=" padding: 14rpx 36rpx; border-radius: 46rpx; " >
|
||||
{{item}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="padding: 30rpx 20rpx; border-bottom: 1rpx solid #e2e2e2; border-top: 1rpx solid #e2e2e2; " >
|
||||
<view class="" style="font-weight: 600; font-size: 32rpx;" >
|
||||
精品推荐
|
||||
</view>
|
||||
<view class="" @click="Service.GoPage('/pages/article/news')" style="margin-top: 20rpx; position: relative; ">
|
||||
<image :src="Service.GetMateUrlByImg('/static/dele/dele1.jpg')" mode="aspectFill" style=" border-radius: 20rpx; width: 100%; height: 350rpx;" alt="" />
|
||||
<view class="" style="position: absolute; left: 20rpx; bottom: 50rpx; color: #fff; " >
|
||||
<view class="" style="font-size: 34rpx;" >
|
||||
城市漫步:寻找完美咖啡馆
|
||||
</view>
|
||||
<view class="" style=" margin: 15rpx 0; font-size: 26rpx;" >
|
||||
探索城市中隐藏的咖啡珍宝,感受不同的咖啡文化
|
||||
</view>
|
||||
<view class="" style="font-size: 22rpx; display: flex; align-items: center;" >
|
||||
<img :src="Service.GetIconImg('/static/article/time.png')" style="width: 24rpx; height: 24rpx;" alt="" />
|
||||
<text style="margin-left: 10rpx;" >2小时前</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="" style="padding: 20rpx; border-bottom: 1rpx solid #e2e2e2;" >
|
||||
<view class="" @click="Service.GoPage('/pages/article/news')" style="display: flex; margin-top: 20rpx; border-radius: 20rpx; " >
|
||||
<image :src="Service.GetMateUrlByImg('/static/dele/dele1.jpg')" mode="aspectFill" style=" border-radius: 20rpx; width: 190rpx; height: 150rpx;" alt="" />
|
||||
<view class="" style=" flex: 1; margin-left: 20rpx; display: flex; flex-direction: column; justify-content: space-between; " >
|
||||
<view class="" style=" " >
|
||||
<view class="" style="font-weight: 700; font-size: 32rpx; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;" >
|
||||
老北京炸酱面
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style=" margin: 8rpx 0; color: #666666; font-size: 26rpx; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; " >
|
||||
老北京炸酱面是北京市传统小吃,属北京菜系,在京津冀地区广为流传。该菜品以面条为主料,搭配炸酱与时令菜码拌制而成,核心酱料选用肥瘦相间的五花肉丁,配以干黄酱和甜面酱混合炒制,经慢火熬煮形成深褐色酱料
|
||||
</view>
|
||||
<view class="" style=" display: flex; align-items: center; " >
|
||||
<img :src="Service.GetIconImg('/static/article/date.png')" style="width: 24rpx; height: 24rpx;" alt="" />
|
||||
<text style="color: #666666; margin-left: 10rpx; font-size: 26rpx;" >2026-10-15</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<up-loadmore :status="status" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { ref } from "vue";
|
||||
import { Service } from "@/Service/Service"
|
||||
|
||||
let search = ref()
|
||||
let status=ref('nomore')
|
||||
let currentIndex=ref(0)
|
||||
let tag=ref([
|
||||
'全部',
|
||||
'热门',
|
||||
'推荐',
|
||||
'最近'
|
||||
])
|
||||
|
||||
onLoad(() => {
|
||||
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
|
||||
const changetab=(e:number)=>{
|
||||
currentIndex.value=e
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
.active{
|
||||
background-color: #F3F4F6;
|
||||
color: #4B5563;
|
||||
}
|
||||
.actived{
|
||||
background-color: #FF6B35;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tag{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: fit-content;
|
||||
font-size: 28rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,235 @@
|
||||
<template>
|
||||
<view style=" padding: 60rpx 40rpx 20rpx; ">
|
||||
|
||||
<view class="" style="font-size: 26rpx;">
|
||||
提现金额
|
||||
</view>
|
||||
<view class="" style="margin-bottom: 20rpx;">
|
||||
<up-input v-model="account" type="digit" :customStyle="{'padding':'12rpx 0','height':'100rpx'}"
|
||||
fontSize='18' prefixIconStyle="font-size: 28px;color: #000;font-weight:bold" placeholder="请输入金额"
|
||||
border="bottom" prefixIcon="rmb"></up-input>
|
||||
</view>
|
||||
<view class="" style="font-size: 26rpx; display: flex; align-items: center; ">
|
||||
<view class="" style="color: #999; ">
|
||||
当前剩余 {{ Number(userData.integral).toFixed(2) }} 元
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
|
||||
<view class="" style="margin: 40rpx 20rpx; ">
|
||||
<u-button type="primary" @click="save" :custom-style="contactButtonStyle">立即提现</u-button>
|
||||
</view>
|
||||
<view class="" style="margin: 20rpx; padding: 20rpx 30rpx; background-color: #f6f6f6; border-radius: 20rpx; ">
|
||||
<view class="" style="font-weight: bold; font-size: 30rpx;">
|
||||
提示
|
||||
</view>
|
||||
|
||||
<view class="" style="font-size: 28rpx;">
|
||||
<view class="" style="text-indent: 1em; margin-top: 10rpx; ">
|
||||
1.单笔最小提现1元
|
||||
</view>
|
||||
<view class="" style="text-indent: 1em; margin-top: 10rpx; ">
|
||||
2.单笔最多可提现200元
|
||||
</view>
|
||||
<view class="" style="text-indent: 1em; margin-top: 10rpx; ">
|
||||
3.每次最多一次申请提现
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="" v-if="withLog!==null"
|
||||
style="margin: 20rpx; padding: 20rpx 30rpx 30rpx; background-color: #f6f6f6; border-radius: 10rpx; ">
|
||||
<view class="" style="font-weight: bold; font-size: 30rpx; display: flex; gap: 6rpx; ">
|
||||
<img :src="Service.GetIconImg('/static/index/pay/light.png')" style="width: 40rpx; height: 40rpx;" alt="" />
|
||||
待收款提醒
|
||||
</view>
|
||||
|
||||
<view class="" style="font-size: 28rpx; margin: 20rpx 0; text-indent: 2em; ">
|
||||
金额: {{ Number(withLog.amount).toFixed(2) }}
|
||||
</view>
|
||||
<view class="" style="font-size: 28rpx; margin: 20rpx 0; text-indent: 2em; ">
|
||||
时间: {{ withLog.addTime }}
|
||||
</view>
|
||||
<view class=""
|
||||
style=" display: flex; justify-content: flex-end; width: 100%; ">
|
||||
|
||||
<button @click="withdrow(withdrowData)" class="button-withdrow" style=" ">确认收款</button>
|
||||
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { ref } from "vue";
|
||||
import { Service, vpMerchService } from '@/Service/vp/vpMerchService'
|
||||
import { vpLoginService } from '@/Service/vp/vpLoginService'
|
||||
import { vpUserService } from '@/Service/vp/vpUserService'
|
||||
|
||||
let name = ref('')
|
||||
let account = ref<number>()
|
||||
let userData = ref({
|
||||
integral: 0,
|
||||
merchId: ''
|
||||
})
|
||||
// 按钮样式
|
||||
const contactButtonStyle = ref({
|
||||
backgroundColor: '#FF6600',
|
||||
borderColor: '#FF6600',
|
||||
color: '#FFFFFF',
|
||||
fontSize: '28rpx',
|
||||
height: '90rpx',
|
||||
borderRadius: '45rpx',
|
||||
marginRight: '20rpx'
|
||||
});
|
||||
|
||||
|
||||
let withdrowData = ref({
|
||||
data:{
|
||||
appId: '',
|
||||
mchId: '',
|
||||
packInfo: '',
|
||||
withId:''
|
||||
}
|
||||
})
|
||||
|
||||
let withLog = ref<any>(null)
|
||||
|
||||
|
||||
|
||||
onLoad(() => {
|
||||
getData()
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
|
||||
|
||||
const getData = () => {
|
||||
vpMerchService.GetMerchAccInfo('', 1).then(res => {
|
||||
if (res.code == 0) {
|
||||
userData.value = res.data.merchAcc
|
||||
withLog.value = res.data.withLog
|
||||
withdrowData.value.data.mchId = res.data.mchId
|
||||
withdrowData.value.data.appId = res.data.appId
|
||||
withdrowData.value.data.packInfo = res.data.withLog.packInfo
|
||||
withdrowData.value.data.withId=res.data.withLog.withId
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const save = () => {
|
||||
if (account.value === 0 || !account.value) {
|
||||
Service.Msg('请输入金额')
|
||||
return
|
||||
}
|
||||
if (account.value < 1) {
|
||||
Service.Msg('单笔最小提现1元')
|
||||
return
|
||||
}
|
||||
if (account.value > 200) {
|
||||
Service.Msg('单笔最多可提现200元')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Service.LoadIng('提现中')
|
||||
uni.getProvider({
|
||||
service: 'oauth',
|
||||
success: function (res : any) {
|
||||
uni.login({
|
||||
onlyAuthorize: true,
|
||||
provider: res.provider,
|
||||
success: function (loginRes) {
|
||||
vpLoginService.GetOpenIdByWeixin(loginRes.code, res.provider == 'weixin' ? 1 : 3).then(content => {
|
||||
if (content.code == 0) {
|
||||
vpUserService.WxWith(content.data, account.value, '').then(wxres => {
|
||||
Service.LoadClose()
|
||||
if (wxres.code == 0) {
|
||||
withdrow(wxres)
|
||||
} else {
|
||||
Service.Msg(wxres.msg)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Service.Msg(content.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
const withdrow = (wxres) => {
|
||||
if (wx.canIUse('requestMerchantTransfer')) {
|
||||
wx.requestMerchantTransfer({
|
||||
mchId: wxres.data.mchId,
|
||||
appId: wxres.data.appId,
|
||||
package: wxres.data.packInfo,
|
||||
success: () => {
|
||||
vpUserService.UpdateWith(wxres.data.withId).then(withdrow => {
|
||||
|
||||
})
|
||||
|
||||
Service.Msg('提现成功!')
|
||||
setTimeout(() => {
|
||||
Service.GoPageBack()
|
||||
}, 1000)
|
||||
},
|
||||
fail: (res) => {
|
||||
console.log('fail:', res);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
wx.showModal({
|
||||
content: '你的微信版本过低,请更新至最新版本。',
|
||||
showCancel: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
padding: 30rpx 30rpx;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #ffffff;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
|
||||
.button-withdrow {
|
||||
width: 36%;
|
||||
color: #fff;
|
||||
background-color: var(--nav-mian);
|
||||
border-color: #f6f6f6;
|
||||
border-radius: 16rpx;
|
||||
font-size: 24rpx;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.button-withdrow::after {
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,961 @@
|
||||
# Painter 画板 测试版
|
||||
|
||||
> uniapp 海报画板,更优雅的海报生成方案
|
||||
> [查看更多](https://limeui.qcoon.cn/#/painter)
|
||||
|
||||
## 平台兼容
|
||||
|
||||
| H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App |
|
||||
| --- | ---------- | ------------ | ---------- | ---------- | --------- | --- |
|
||||
| √ | √ | √ | 未测 | √ | √ | √ |
|
||||
|
||||
## 安装
|
||||
在市场导入**[海报画板](https://ext.dcloud.net.cn/plugin?id=2389)uni_modules**版本的即可,无需`import`
|
||||
|
||||
## 代码演示
|
||||
|
||||
### 插件demo
|
||||
- lime-painter 为 demo
|
||||
- 位于 uni_modules/lime-painter/components/lime-painter
|
||||
- 导入插件后直接使用可查看demo
|
||||
```vue
|
||||
<lime-painter />
|
||||
```
|
||||
|
||||
|
||||
### 基本用法
|
||||
|
||||
- 插件提供 JSON 及 Template 的方式绘制海报
|
||||
- 参考 css 块状流布局模拟 css schema。
|
||||
- 另外flex布局还不是成完善,请谨慎使用,普通的流布局我觉得已经够用了。
|
||||
|
||||
#### 方式一 Template
|
||||
|
||||
- 提供`l-painter-view`、`l-painter-text`、`l-painter-image`、`l-painter-qrcode`四种类型组件
|
||||
- 通过 `css` 属性绘制样式,与 style 使用方式保持一致。
|
||||
```html
|
||||
<l-painter>
|
||||
//如果使用Template出现顺序错乱,可使用`template` 等所有变量完成再显示
|
||||
<template v-if="show">
|
||||
<l-painter-view
|
||||
css="background: #07c160; height: 120rpx; width: 120rpx; display: inline-block"
|
||||
></l-painter-view>
|
||||
<l-painter-view
|
||||
css="background: #1989fa; height: 120rpx; width: 120rpx; border-top-right-radius: 60rpx; border-bottom-left-radius: 60rpx; display: inline-block; margin: 0 30rpx;"
|
||||
></l-painter-view>
|
||||
<l-painter-view
|
||||
css="background: #ff9d00; height: 120rpx; width: 120rpx; border-radius: 50%; display: inline-block"
|
||||
></l-painter-view>
|
||||
<template>
|
||||
</l-painter>
|
||||
```
|
||||
|
||||
#### 方式二 JSON
|
||||
|
||||
- 在 json 里四种类型组件的`type`为`view`、`text`、`image`、`qrcode`
|
||||
- 通过 `board` 设置海报所需的 JSON 数据进行绘制或`ref`获取组件实例调用组件内的`render(json)`
|
||||
- 所有类型的 schema 都具有`css`字段,css 的 key 值使用**驼峰**如:`lineHeight`
|
||||
|
||||
```html
|
||||
<l-painter :board="poster"/>
|
||||
```
|
||||
|
||||
```js
|
||||
data() {
|
||||
return {
|
||||
poster: {
|
||||
css: {
|
||||
// 根节点若无尺寸,自动获取父级节点
|
||||
width: '750rpx'
|
||||
},
|
||||
views: [
|
||||
{
|
||||
css: {
|
||||
background: "#07c160",
|
||||
height: "120rpx",
|
||||
width: "120rpx",
|
||||
display: "inline-block"
|
||||
},
|
||||
type: "view"
|
||||
},
|
||||
{
|
||||
css: {
|
||||
background: "#1989fa",
|
||||
height: "120rpx",
|
||||
width: "120rpx",
|
||||
borderTopRightRadius: "60rpx",
|
||||
borderBottomLeftRadius: "60rpx",
|
||||
display: "inline-block",
|
||||
margin: "0 30rpx"
|
||||
},
|
||||
views: [],
|
||||
type: "view"
|
||||
},
|
||||
{
|
||||
css: {
|
||||
background: "#ff9d00",
|
||||
height: "120rpx",
|
||||
width: "120rpx",
|
||||
borderRadius: "50%",
|
||||
display: "inline-block"
|
||||
},
|
||||
views: [],
|
||||
type: "view"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### View 容器
|
||||
|
||||
- 类似于 `div` 可以嵌套承载更多的 view、text、image,qrcode 共同构建一颗完整的节点树
|
||||
- 在 JSON 里具有 `views` 的数组字段,用于嵌套承载节点。
|
||||
|
||||
#### 方式一 Template
|
||||
|
||||
```html
|
||||
<l-painter>
|
||||
<l-painter-view css="background: #f0f0f0; padding-top: 100rpx;">
|
||||
<l-painter-view
|
||||
css="background: #d9d9d9; width: 33.33%; height: 100rpx; display: inline-block"
|
||||
></l-painter-view>
|
||||
<l-painter-view
|
||||
css="background: #bfbfbf; width: 66.66%; height: 100rpx; display: inline-block"
|
||||
></l-painter-view>
|
||||
</l-painter-view>
|
||||
</l-painter>
|
||||
```
|
||||
|
||||
#### 方式二 JSON
|
||||
|
||||
```js
|
||||
{
|
||||
css: {},
|
||||
views: [
|
||||
{
|
||||
type: 'view',
|
||||
css: {
|
||||
background: '#f0f0f0',
|
||||
paddingTop: '100rpx'
|
||||
},
|
||||
views: [
|
||||
{
|
||||
type: 'view',
|
||||
css: {
|
||||
background: '#d9d9d9',
|
||||
width: '33.33%',
|
||||
height: '100rpx',
|
||||
display: 'inline-block'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'view',
|
||||
css: {
|
||||
background: '#bfbfbf',
|
||||
width: '66.66%',
|
||||
height: '100rpx',
|
||||
display: 'inline-block'
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Text 文本
|
||||
|
||||
- 通过 `text` 属性填写文本内容。
|
||||
- 支持`\n`换行符
|
||||
- 支持省略号,使用 css 的`line-clamp`设置行数,当文字内容超过会显示省略号。
|
||||
- 支持`text-decoration`
|
||||
|
||||
#### 方式一 Template
|
||||
|
||||
```html
|
||||
<l-painter>
|
||||
<l-painter-view css="background: #e0e2db; padding: 30rpx; color: #222a29">
|
||||
<l-painter-text
|
||||
text="登鹳雀楼\n白日依山尽,黄河入海流\n欲穷千里目,更上一层楼"
|
||||
/>
|
||||
<l-painter-text
|
||||
text="登鹳雀楼\n白日依山尽,黄河入海流\n欲穷千里目,更上一层楼"
|
||||
css="text-align:center; padding-top: 20rpx; text-decoration: line-through "
|
||||
/>
|
||||
<l-painter-text
|
||||
text="登鹳雀楼\n白日依山尽,黄河入海流\n欲穷千里目,更上一层楼"
|
||||
css="text-align:right; padding-top: 20rpx"
|
||||
/>
|
||||
<l-painter-text
|
||||
text="水调歌头\n明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。"
|
||||
css="line-clamp: 3; padding-top: 20rpx; background: linear-gradient(,#ff971b 0%, #ff5000 100%); background-clip: text"
|
||||
/>
|
||||
</l-painter-view>
|
||||
</l-painter>
|
||||
```
|
||||
|
||||
#### 方式二 JSON
|
||||
|
||||
```js
|
||||
// 基础用法
|
||||
{
|
||||
type: 'text',
|
||||
text: '登鹳雀楼\n白日依山尽,黄河入海流\n欲穷千里目,更上一层楼',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: '登鹳雀楼\n白日依山尽,黄河入海流\n欲穷千里目,更上一层楼',
|
||||
css: {
|
||||
// 设置居中对齐
|
||||
textAlign: 'center',
|
||||
// 设置中划线
|
||||
textDecoration: 'line-through'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: '登鹳雀楼\n白日依山尽,黄河入海流\n欲穷千里目,更上一层楼',
|
||||
css: {
|
||||
// 设置右对齐
|
||||
textAlign: 'right',
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: '登鹳雀楼\n白日依山尽,黄河入海流\n欲穷千里目,更上一层楼',
|
||||
css: {
|
||||
// 设置行数,超出显示省略号
|
||||
lineClamp: 3,
|
||||
// 渐变文字
|
||||
background: 'linear-gradient(,#ff971b 0%, #1989fa 100%)',
|
||||
backgroundClip: 'text'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Image 图片
|
||||
|
||||
- 通过 `src` 属性填写图片路径。
|
||||
- 图片路径支持:网络图片,本地 static 里的图片路径,缓存路径,**字节的static目录是写相对路径**
|
||||
- 通过 `css` 的 `object-fit`属性可以设置图片的填充方式,可选值见下方 CSS 表格。
|
||||
- 通过 `css` 的 `object-position`配合 `object-fit` 可以设置图片的对齐方式,类似于`background-position`,详情见下方 CSS 表格。
|
||||
- 使用网络图片时:小程序需要去公众平台配置 [downloadFile](https://mp.weixin.qq.com/) 域名
|
||||
- 使用网络图片时:**H5 和 Nvue 需要决跨域问题**
|
||||
|
||||
#### 方式一 Template
|
||||
|
||||
```html
|
||||
<l-painter>
|
||||
<!-- 基础用法 -->
|
||||
<l-painter-image
|
||||
src="https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg"
|
||||
css="width: 200rpx; height: 200rpx"
|
||||
/>
|
||||
<!-- 填充方式 -->
|
||||
<!-- css object-fit 设置 填充方式 见下方表格-->
|
||||
<l-painter-image
|
||||
src="https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg"
|
||||
css="width: 200rpx; height: 200rpx; object-fit: contain; background: #eee"
|
||||
/>
|
||||
<!-- css object-position 设置 图片的对齐方式-->
|
||||
<l-painter-image
|
||||
src="https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg"
|
||||
css="width: 200rpx; height: 200rpx; object-fit: contain; object-position: 50% 50%; background: #eee"
|
||||
/>
|
||||
</l-painter>
|
||||
```
|
||||
|
||||
#### 方式二 JSON
|
||||
|
||||
```js
|
||||
// 基础用法
|
||||
{
|
||||
type: 'image',
|
||||
src: 'https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg',
|
||||
css: {
|
||||
width: '200rpx',
|
||||
height: '200rpx'
|
||||
}
|
||||
},
|
||||
// 填充方式
|
||||
// css objectFit 设置 填充方式 见下方表格
|
||||
{
|
||||
type: 'image',
|
||||
src: 'https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg',
|
||||
css: {
|
||||
width: '200rpx',
|
||||
height: '200rpx',
|
||||
objectFit: 'contain'
|
||||
}
|
||||
},
|
||||
// css objectPosition 设置 图片的对齐方式
|
||||
{
|
||||
type: 'image',
|
||||
src: 'https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg',
|
||||
css: {
|
||||
width: '200rpx',
|
||||
height: '200rpx',
|
||||
objectFit: 'contain',
|
||||
objectPosition: '50% 50%'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Qrcode 二维码
|
||||
|
||||
- 通过`text`属性填写需要生成二维码的文本。
|
||||
- 通过 `css` 里的 `color` 可设置生成码点的颜色。
|
||||
- 通过 `css` 里的 `background`可设置背景色。
|
||||
- 通过 `css `里的 `width`、`height`设置尺寸。
|
||||
|
||||
#### 方式一 Template
|
||||
|
||||
```html
|
||||
<l-painter>
|
||||
<l-painter-qrcode
|
||||
text="limeui.qcoon.cn"
|
||||
css="width: 200rpx; height: 200rpx"
|
||||
/>
|
||||
</l-painter>
|
||||
```
|
||||
|
||||
#### 方式二 JSON
|
||||
|
||||
```js
|
||||
{
|
||||
type: 'qrcode',
|
||||
text: 'limeui.qcoon.cn',
|
||||
css: {
|
||||
width: '200rpx',
|
||||
height: '200rpx',
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 富文本
|
||||
- 这是一个有限支持的测试能力,只能通过JSON方式,不要抱太大希望!
|
||||
- 首先需要把富文本转成JSON,这需要引入`parser`这个包,如果你不使用是不会进入主包
|
||||
|
||||
```html
|
||||
<l-painter ref="painter"/>
|
||||
```
|
||||
```js
|
||||
import parseHtml from '@/uni_modules/lime-painter/parser'
|
||||
const json = parseHtml(`<p><span>测试测试</span><img src="/static/logo.png"/></p>`)
|
||||
this.$refs.painter.render(json)
|
||||
```
|
||||
|
||||
### 生成图片
|
||||
|
||||
- 方式1、通过设置`isCanvasToTempFilePath`自动生成图片并在 `@success` 事件里接收海报临时路径
|
||||
- 方式2、通过调用内部方法生成图片:
|
||||
|
||||
```html
|
||||
<l-painter ref="painter">...code</l-painter>
|
||||
```
|
||||
|
||||
```js
|
||||
this.$refs.painter.canvasToTempFilePathSync({
|
||||
fileType: "jpg",
|
||||
// 如果返回的是base64是无法使用 saveImageToPhotosAlbum,需要设置 pathType为url
|
||||
pathType: 'url',
|
||||
quality: 1,
|
||||
success: (res) => {
|
||||
console.log(res.tempFilePath);
|
||||
// 非H5 保存到相册
|
||||
// H5 提示用户长按图另存
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: res.tempFilePath,
|
||||
success: function () {
|
||||
console.log('save success');
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 主动调用方式
|
||||
|
||||
- 通过获取组件实例内部的`render`函数 传递`JSON`即可
|
||||
|
||||
```html
|
||||
<l-painter ref="painter" />
|
||||
```
|
||||
|
||||
```js
|
||||
// 渲染
|
||||
this.$refs.painter.render(jsonSchema);
|
||||
// 生成图片
|
||||
this.$refs.painter.canvasToTempFilePathSync({
|
||||
fileType: "jpg",
|
||||
// 如果返回的是base64是无法使用 saveImageToPhotosAlbum,需要设置 pathType为url
|
||||
pathType: 'url',
|
||||
quality: 1,
|
||||
success: (res) => {
|
||||
console.log(res.tempFilePath);
|
||||
// 非H5 保存到相册
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: res.tempFilePath,
|
||||
success: function () {
|
||||
console.log('save success');
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### H5跨域
|
||||
- 一般是需要后端或管理OSS资源的大佬处理
|
||||
- 一般OSS的处理方式:
|
||||
|
||||
1、设置来源
|
||||
```cmd
|
||||
*
|
||||
```
|
||||
|
||||
2、允许Methods
|
||||
```html
|
||||
GET
|
||||
```
|
||||
|
||||
3、允许Headers
|
||||
```html
|
||||
access-control-allow-origin:*
|
||||
```
|
||||
|
||||
4、最后如果还是不行,可试下给插件设置`useCORS`
|
||||
```html
|
||||
<l-painter useCORS>
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 海报示例
|
||||
|
||||
- 提供一份示例,只把插件当成生成图片的工具,非必要不要在弹窗里使用。
|
||||
- 通过设置`isCanvasToTempFilePath`主动生成图片,再由 `@success` 事件接收海报临时路径
|
||||
- 设置`hidden`隐藏画板。
|
||||
请注意,示例用到了图片,海报的渲染是包括下载图片的时间,也许在某天图片会失效或访问超级慢,请更换为你的图片再查看,另外如果你是小程序请在使用示例时把**不校验合法域名**勾上!!!!!不然不显示还以为是插件的锅,求求了大佬们!
|
||||
#### 方式一 Template
|
||||
|
||||
```html
|
||||
<image :src="path" mode="widthFix"></image>
|
||||
<l-painter
|
||||
isCanvasToTempFilePath
|
||||
@success="path = $event"
|
||||
hidden
|
||||
css="width: 750rpx; padding-bottom: 40rpx; background: linear-gradient(,#ff971b 0%, #ff5000 100%)"
|
||||
>
|
||||
<l-painter-image
|
||||
src="https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg"
|
||||
css="margin-left: 40rpx; margin-top: 40rpx; width: 84rpx; height: 84rpx; border-radius: 50%;"
|
||||
/>
|
||||
<l-painter-view
|
||||
css="margin-top: 40rpx; padding-left: 20rpx; display: inline-block"
|
||||
>
|
||||
<l-painter-text
|
||||
text="隔壁老王"
|
||||
css="display: block; padding-bottom: 10rpx; color: #fff; font-size: 32rpx; fontWeight: bold"
|
||||
/>
|
||||
<l-painter-text
|
||||
text="为您挑选了一个好物"
|
||||
css="color: rgba(255,255,255,.7); font-size: 24rpx"
|
||||
/>
|
||||
</l-painter-view>
|
||||
<l-painter-view
|
||||
css="margin-left: 40rpx; margin-top: 30rpx; padding: 32rpx; box-sizing: border-box; background: #fff; border-radius: 16rpx; width: 670rpx; box-shadow: 0 20rpx 58rpx rgba(0,0,0,.15)"
|
||||
>
|
||||
<l-painter-image
|
||||
src="https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg"
|
||||
css="object-fit: cover; object-position: 50% 50%; width: 606rpx; height: 606rpx; border-radius: 12rpx;"
|
||||
/>
|
||||
<l-painter-view
|
||||
css="margin-top: 32rpx; color: #FF0000; font-weight: bold; font-size: 28rpx; line-height: 1em;"
|
||||
>
|
||||
<l-painter-text text="¥" css="vertical-align: bottom" />
|
||||
<l-painter-text
|
||||
text="39"
|
||||
css="vertical-align: bottom; font-size: 58rpx"
|
||||
/>
|
||||
<l-painter-text text=".39" css="vertical-align: bottom" />
|
||||
<l-painter-text
|
||||
text="¥59.99"
|
||||
css="vertical-align: bottom; padding-left: 10rpx; font-weight: normal; text-decoration: line-through; color: #999999"
|
||||
/>
|
||||
</l-painter-view>
|
||||
<l-painter-view css="margin-top: 32rpx; font-size: 26rpx; color: #8c5400">
|
||||
<l-painter-text text="自营" css="color: #212121; background: #ffb400;" />
|
||||
<l-painter-text
|
||||
text="30天最低价"
|
||||
css="margin-left: 16rpx; background: #fff4d9; text-decoration: line-through;"
|
||||
/>
|
||||
<l-painter-text
|
||||
text="满减优惠"
|
||||
css="margin-left: 16rpx; background: #fff4d9"
|
||||
/>
|
||||
<l-painter-text
|
||||
text="超高好评"
|
||||
css="margin-left: 16rpx; background: #fff4d9"
|
||||
/>
|
||||
</l-painter-view>
|
||||
<l-painter-view css="margin-top: 30rpx">
|
||||
<l-painter-text
|
||||
css="line-clamp: 2; color: #333333; line-height: 1.8em; font-size: 36rpx; width: 478rpx; padding-right:32rpx; box-sizing: border-box"
|
||||
text="360儿童电话手表9X 智能语音问答定位支付手表 4G全网通20米游泳级防水视频通话拍照手表男女孩星空蓝"
|
||||
></l-painter-text>
|
||||
<l-painter-qrcode
|
||||
css="width: 128rpx; height: 128rpx;"
|
||||
text="limeui.qcoon.cn"
|
||||
></l-painter-qrcode>
|
||||
</l-painter-view>
|
||||
</l-painter-view>
|
||||
</l-painter>
|
||||
```
|
||||
|
||||
```js
|
||||
data() {
|
||||
return {
|
||||
path: ''
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 方式二 JSON
|
||||
|
||||
```html
|
||||
<image :src="path" mode="widthFix"></image>
|
||||
<l-painter
|
||||
:board="poster"
|
||||
isCanvasToTempFilePath
|
||||
@success="path = $event"
|
||||
hidden
|
||||
/>
|
||||
```
|
||||
|
||||
```js
|
||||
data() {
|
||||
return {
|
||||
path: '',
|
||||
poster: {
|
||||
css: {
|
||||
width: "750rpx",
|
||||
paddingBottom: "40rpx",
|
||||
background: "linear-gradient(,#000 0%, #ff5000 100%)"
|
||||
},
|
||||
views: [
|
||||
{
|
||||
src: "https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg",
|
||||
type: "image",
|
||||
css: {
|
||||
background: "#fff",
|
||||
objectFit: "cover",
|
||||
marginLeft: "40rpx",
|
||||
marginTop: "40rpx",
|
||||
width: "84rpx",
|
||||
border: "2rpx solid #fff",
|
||||
boxSizing: "border-box",
|
||||
height: "84rpx",
|
||||
borderRadius: "50%"
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "view",
|
||||
css: {
|
||||
marginTop: "40rpx",
|
||||
paddingLeft: "20rpx",
|
||||
display: "inline-block"
|
||||
},
|
||||
views: [
|
||||
{
|
||||
text: "隔壁老王",
|
||||
type: "text",
|
||||
css: {
|
||||
display: "block",
|
||||
paddingBottom: "10rpx",
|
||||
color: "#fff",
|
||||
fontSize: "32rpx",
|
||||
fontWeight: "bold"
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "为您挑选了一个好物",
|
||||
type: "text",
|
||||
css: {
|
||||
color: "rgba(255,255,255,.7)",
|
||||
fontSize: "24rpx"
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
css: {
|
||||
marginLeft: "40rpx",
|
||||
marginTop: "30rpx",
|
||||
padding: "32rpx",
|
||||
boxSizing: "border-box",
|
||||
background: "#fff",
|
||||
borderRadius: "16rpx",
|
||||
width: "670rpx",
|
||||
boxShadow: "0 20rpx 58rpx rgba(0,0,0,.15)"
|
||||
},
|
||||
views: [
|
||||
{
|
||||
src: "https://m.360buyimg.com/babel/jfs/t1/196317/32/13733/288158/60f4ea39E6fb378ed/d69205b1a8ed3c97.jpg",
|
||||
type: "image",
|
||||
css: {
|
||||
objectFit: "cover",
|
||||
objectPosition: "50% 50%",
|
||||
width: "606rpx",
|
||||
height: "606rpx"
|
||||
},
|
||||
}, {
|
||||
css: {
|
||||
marginTop: "32rpx",
|
||||
color: "#FF0000",
|
||||
fontWeight: "bold",
|
||||
fontSize: "28rpx",
|
||||
lineHeight: "1em"
|
||||
},
|
||||
views: [{
|
||||
text: "¥",
|
||||
type: "text",
|
||||
css: {
|
||||
verticalAlign: "bottom"
|
||||
},
|
||||
}, {
|
||||
text: "39",
|
||||
type: "text",
|
||||
css: {
|
||||
verticalAlign: "bottom",
|
||||
fontSize: "58rpx"
|
||||
},
|
||||
}, {
|
||||
text: ".39",
|
||||
type: "text",
|
||||
css: {
|
||||
verticalAlign: "bottom"
|
||||
},
|
||||
}, {
|
||||
text: "¥59.99",
|
||||
type: "text",
|
||||
css: {
|
||||
verticalAlign: "bottom",
|
||||
paddingLeft: "10rpx",
|
||||
fontWeight: "normal",
|
||||
textDecoration: "line-through",
|
||||
color: "#999999"
|
||||
}
|
||||
}],
|
||||
|
||||
type: "view"
|
||||
}, {
|
||||
css: {
|
||||
marginTop: "32rpx",
|
||||
fontSize: "26rpx",
|
||||
color: "#8c5400"
|
||||
},
|
||||
views: [{
|
||||
text: "自营",
|
||||
type: "text",
|
||||
css: {
|
||||
color: "#212121",
|
||||
background: "#ffb400"
|
||||
},
|
||||
}, {
|
||||
text: "30天最低价",
|
||||
type: "text",
|
||||
css: {
|
||||
marginLeft: "16rpx",
|
||||
background: "#fff4d9",
|
||||
textDecoration: "line-through"
|
||||
},
|
||||
}, {
|
||||
text: "满减优惠",
|
||||
type: "text",
|
||||
css: {
|
||||
marginLeft: "16rpx",
|
||||
background: "#fff4d9"
|
||||
},
|
||||
}, {
|
||||
text: "超高好评",
|
||||
type: "text",
|
||||
css: {
|
||||
marginLeft: "16rpx",
|
||||
background: "#fff4d9"
|
||||
},
|
||||
|
||||
}],
|
||||
|
||||
type: "view"
|
||||
}, {
|
||||
css: {
|
||||
marginTop: "30rpx"
|
||||
},
|
||||
views: [
|
||||
{
|
||||
text: "360儿童电话手表9X 智能语音问答定位支付手表 4G全网通20米游泳级防水视频通话拍照手表男女孩星空蓝",
|
||||
type: "text",
|
||||
css: {
|
||||
paddingRight: "32rpx",
|
||||
boxSizing: "border-box",
|
||||
lineClamp: 2,
|
||||
color: "#333333",
|
||||
lineHeight: "1.8em",
|
||||
fontSize: "36rpx",
|
||||
width: "478rpx"
|
||||
},
|
||||
}, {
|
||||
text: "limeui.qcoon.cn",
|
||||
type: "qrcode",
|
||||
css: {
|
||||
width: "128rpx",
|
||||
height: "128rpx",
|
||||
},
|
||||
|
||||
}],
|
||||
type: "view"
|
||||
}],
|
||||
type: "view"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 自定义字体
|
||||
- 需要平台的支持,已知微信小程序支持,其它的没试过,如果可行请告之
|
||||
|
||||
```
|
||||
// 需要在app.vue中下载字体
|
||||
uni.loadFontFace({
|
||||
global:true,
|
||||
scopes: ['native'],
|
||||
family: '自定义字体名称',
|
||||
source: 'url("https://sungd.github.io/Pacifico.ttf")',
|
||||
|
||||
success() {
|
||||
console.log('success')
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// 然后就可以在插件的css中写font-family: '自定义字体名称'
|
||||
```
|
||||
|
||||
|
||||
### Nvue
|
||||
- 必须为HBX 3.4.11及以上
|
||||
|
||||
|
||||
### 原生小程序
|
||||
|
||||
- 插件里的`painter.js`支持在原生小程序中使用
|
||||
- new Painter 之后在`source`里传入 JSON
|
||||
- 再调用`render`绘制海报
|
||||
- 如需生成图片,请查看微信小程序 cavnas 的[canvasToTempFilePath](https://developers.weixin.qq.com/miniprogram/dev/api/canvas/wx.canvasToTempFilePath.html)
|
||||
|
||||
```html
|
||||
<canvas type="2d" id="painter" style="width: 100%"></canvas>
|
||||
```
|
||||
|
||||
```js
|
||||
import { Painter } from "./painter";
|
||||
page({
|
||||
data: {
|
||||
poster: {
|
||||
css: {
|
||||
width: "750rpx",
|
||||
},
|
||||
views: [
|
||||
{
|
||||
type: "view",
|
||||
css: {
|
||||
background: "#d2d4c8",
|
||||
paddingTop: "100rpx",
|
||||
},
|
||||
views: [
|
||||
{
|
||||
type: "view",
|
||||
css: {
|
||||
background: "#5f7470",
|
||||
width: "33.33%",
|
||||
height: "100rpx",
|
||||
display: "inline-block",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "view",
|
||||
css: {
|
||||
background: "#889696",
|
||||
width: "33.33%",
|
||||
height: "100rpx",
|
||||
display: "inline-block",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "view",
|
||||
css: {
|
||||
background: "#b8bdb5",
|
||||
width: "33.33%",
|
||||
height: "100rpx",
|
||||
display: "inline-block",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
async onLoad() {
|
||||
const res = await this.getCentext();
|
||||
const painter = new Painter(res);
|
||||
// 返回计算布局后的整个内容尺寸
|
||||
const { width, height } = await painter.source(this.data.poster);
|
||||
// 得到计算后的尺寸后 可给canvas尺寸赋值,达到动态响应效果
|
||||
// 渲染
|
||||
await painter.render();
|
||||
},
|
||||
// 获取canvas 2d
|
||||
// 非2d 需要传一个 createImage 方法用于获取图片信息 即把 getImageInfo 的 success 通过 promise resolve 返回
|
||||
getCentext() {
|
||||
return new Promise((resolve) => {
|
||||
wx.createSelectorQuery()
|
||||
.select(`#painter`)
|
||||
.node()
|
||||
.exec((res) => {
|
||||
let { node: canvas } = res[0];
|
||||
resolve({
|
||||
canvas,
|
||||
context: canvas.getContext("2d"),
|
||||
width: canvas.width,
|
||||
height: canvas.height,
|
||||
// createImage: getImageInfo()
|
||||
pixelRatio: 2,
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 旧版(1.6.x)更新
|
||||
|
||||
- 由于 1.8.x 版放弃了以定位的方式,所以 1.6.x 版更新之后要每个样式都加上`position: absolute`
|
||||
- 旧版的 `image` mode 模式被放弃,使用`object-fit`
|
||||
- 旧版的 `isRenderImage` 改成 `is-canvas-to-temp-file-path`
|
||||
- 旧版的 `maxLines` 改成 `line-clamp`
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| -------------------------- | ------------------------------------------------------------ | ---------------- | ------------ |
|
||||
| board | JSON 方式的海报元素对象集 | <em>object</em> | - |
|
||||
| css | 海报内容最外层的样式,可以理解为`body` | <em>object</em> | 参数请向下看 |
|
||||
| custom-style | canvas 元素的样式 | <em>string</em> | |
|
||||
| hidden | 隐藏画板 | <em>boolean</em> | `false` |
|
||||
| is-canvas-to-temp-file-path | 是否生成图片,在`@success`事件接收图片地址 | <em>boolean</em> | `false` |
|
||||
| after-delay | 生成图片错乱,可延时生成图片 | <em>number</em> | `100` |
|
||||
| type | canvas 类型,对微信头条支付宝小程序可有效,可选值:`2d`,`''` | <em>string</em> | `2d` |
|
||||
| file-type | 生成图片的后缀类型, 可选值:`png`、`jpg` | <em>string</em> | `png` |
|
||||
| path-type | 生成图片路径类型,可选值`url`、`base64` | <em>string</em> | `-` |
|
||||
| pixel-ratio | 生成图片的像素密度,默认为对应手机的像素密度,`nvue`无效 | <em>number</em> | `-` |
|
||||
| hidpi | H5和APP是否使用高清处理 | <em>boolean</em> | `true` |
|
||||
| width | **废弃** 画板的宽度,一般只用于通过内部方法时加上 | <em>number</em> | `` |
|
||||
| height | **废弃** 画板的高度 ,同上 | <em>number</em> | `` |
|
||||
|
||||
### css
|
||||
| 属性名 | 支持的值或类型 | 默认值 |
|
||||
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |
|
||||
| (min\max)width | 支持`%`、`rpx`、`px` | - |
|
||||
| height | 同上 | - |
|
||||
| color | `string` | - |
|
||||
| position | 定位,可选值:`absolute`、`fixed` | - |
|
||||
| ↳ left、top、right、bottom | 配合`position`才生效,支持`%`、`rpx`、`px` | - |
|
||||
| margin | 可简写或各方向分别写,如:`margin-top`,支持`auto`、`rpx`、`px` | - |
|
||||
| padding | 可简写或各方向分别写,支持`rpx`、`px` | - |
|
||||
| border | 可简写或各个值分开写:`border-width`、`border-style` 、`border-color`,简写请按顺序写 | - |
|
||||
| line-clamp | `number`,超过行数显示省略号 | - |
|
||||
| vertical-align | 文字垂直对齐,可选值:`bottom`、`top`、`middle` | `middle` |
|
||||
| line-height | 文字行高,支持`rpx`、`px`、`em` | `1.4em` |
|
||||
| font-weight | 文字粗细,可选值:`normal`、`bold` | `normal` |
|
||||
| font-size | 文字大小,`string`,支持`rpx`、`px` | `14px` |
|
||||
| text-decoration | 文本修饰,可选值:`underline` 、`line-through`、`overline` | - |
|
||||
| text-stroke | 文字描边,可简写或各个值分开写,如:`text-stroke-color`, `text-stroke-width` | - |
|
||||
| text-align | 文本水平对齐,可选值:`right` 、`center` | `left` |
|
||||
| display | 框类型,可选值:`block`、`inline-block`、`flex`、`none`,当为`none`时是不渲染该段, `flex`功能简陋。 | - |
|
||||
| flex | 配合 display: flex; 属性定义了在分配多余空间,目前只用为数值如: flex: 1 | - |
|
||||
| align-self | 配合 display: flex; 单个项目垂直轴对齐方式: `flex-start` `flex-end` `center` | `flex-start` |
|
||||
| justify-content | 配合 display: flex; 水平轴对齐方式: `flex-start` `flex-end` `center` | `flex-start` |
|
||||
| align-items | 配合 display: flex; 垂直轴对齐方式: `flex-start` `flex-end` `center` | `flex-start` |
|
||||
| border-radius | 圆角边框,支持`%`、`rpx`、`px` | - |
|
||||
| box-sizing | 可选值:`border-box` | - |
|
||||
| box-shadow | 投影 | - |
|
||||
| background(color) | 支持渐变,但必须写百分比!如:`linear-gradient(,#ff971b 0%, #ff5000 100%)`、`radial-gradient(#0ff 15%, #f0f 60%)`,目前 radial-gradient 渐变的圆心为元素中点,半径为最长边,不支持设置 | - |
|
||||
| background-clip | 文字渐变,配合`background`背景渐变,设置`background-clip: text` 达到文字渐变效果 | - |
|
||||
| background-image | view 元素背景:`url(src)`,若只是设置背景图,请不要设置`background-repeat` | - |
|
||||
| background-repeat | 设置是否及如何重复背景纹理,可选值:`repeat`、`repeat-x`、`repeat-y`、`no-repeat` | `repeat` |
|
||||
| [object-fit](https://developer.mozilla.org/zh-CN/docs/Web/CSS/object-fit/) | 图片元素适应容器方式,类似于`mode`,可选值:`cover`、 `contain`、 `fill`、 `none` | - |
|
||||
| [object-position](https://developer.mozilla.org/zh-CN/docs/Web/CSS/object-position) | 图片的对齐方式,配合`object-fit`使用 | - |
|
||||
|
||||
### 图片填充模式 object-fit
|
||||
|
||||
| 名称 | 含义 |
|
||||
| ------- | ------------------------------------------------------ |
|
||||
| contain | 保持宽高缩放图片,使图片的长边能完全显示出来 |
|
||||
| cover | 保持宽高缩放图片,使图片的短边能完全显示出来,裁剪长边 |
|
||||
| fill | 拉伸图片,使图片填满元素 |
|
||||
| none | 保持图片原有尺寸 |
|
||||
|
||||
### 事件 Events
|
||||
|
||||
| 事件名 | 说明 | 返回值 |
|
||||
| -------- | ---------------------------------------------------------------- | ------ |
|
||||
| success | 生成图片成功,若使用`is-canvas-to-temp-filePath` 可以接收图片地址 | path |
|
||||
| fail | 生成图片失败 | error |
|
||||
| done | 绘制成功 | |
|
||||
| progress | 绘制进度 | number |
|
||||
|
||||
### 暴露函数 Expose
|
||||
| 事件名 | 说明 | 返回值 |
|
||||
| -------- | ---------------------------------------------------------------- | ------ |
|
||||
| render(object) | 渲染器,传入JSON 绘制海报 | promise |
|
||||
| [canvasToTempFilePath](https://uniapp.dcloud.io/api/canvas/canvasToTempFilePath.html#canvastotempfilepath)(object) | 把当前画布指定区域的内容导出生成指定大小的图片,并返回文件临时路径。 | |
|
||||
| canvasToTempFilePathSync(object) | 同步接口,同上 | |
|
||||
|
||||
|
||||
## 常见问题
|
||||
|
||||
- 1、H5 端使用网络图片需要解决跨域问题。
|
||||
- 2、小程序使用网络图片需要去公众平台增加下载白名单!二级域名也需要配!
|
||||
- 3、H5 端生成图片是 base64,有时显示只有一半可以使用原生标签`<IMG/>`
|
||||
- 4、发生保存图片倾斜变形或提示 native buffer exceed size limit 时,使用 pixel-ratio="2"参数,降分辨率。
|
||||
- 5、h5 保存图片不需要调接口,提示用户长按图片保存。
|
||||
- 6、画板不能隐藏,包括`v-if`,`v-show`、`display:none`、`opacity:0`,另外也不要把画板放在弹窗里。如果需要隐藏画板请设置 `custom-style="position: fixed; left: 200%"`
|
||||
- 7、微信小程序真机调试请使用 **真机调试2.0**,不支持1.0。
|
||||
- 8、微信小程序打开调试时可以生但并闭无法生成时,这种情况一般是没有在公众号配置download域名
|
||||
- 9、HBX 3.4.5之前的版本不支持vue3
|
||||
- 10、在微信开发工具上 canvas 层级最高无法zindex,并不影响真机
|
||||
- 11、请不要导入非uni_modules插件
|
||||
- 12、关于QQ小程序 报 Propertyor method"toJSON"is not defined 请把基础库调到 1.50.3
|
||||
- 13、支付宝小程序 IDE 不支持 生成图片 请以真机调试结果为准
|
||||
- 14、返回值为字符串 `data:,` 大概是尺寸超过限制,设置 pixel-ratio="2"
|
||||
- 华为手机 APP 上无法生成图片,请使用 HBX2.9.11++(已过时,忽略这条)
|
||||
- IOS APP 请勿使用 HBX2.9.3.20201014 的版本!这个版本无法生成图片。(已过时,忽略这条)
|
||||
- 苹果微信 7.0.20 存在闪退和图片无法 onload 为微信 bug(已过时,忽略这条)
|
||||
- 微信小程序 IOS 旧接口 如父级设置圆角,子级也设会导致子级的失效,为旧接口BUG。
|
||||
- 微信小程序 安卓 旧接口 如使用图片必须加背景色,为旧接口BUG。
|
||||
- 微信小程序 安卓端 [图片可能在首次可以加载成功,再次加载会不触发任何事件](https://developers.weixin.qq.com/community/develop/doc/000ee2b8dacf4009337f51f4556800?highLine=canvas%25202d%2520createImage),临时解决方法是给图片加个时间戳
|
||||
## 打赏
|
||||
|
||||
如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
|
||||
|
||||

|
||||

|
||||
|
After Width: | Height: | Size: 547 B |
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<view style="margin: 20rpx ;">
|
||||
<view class=""
|
||||
style=" display: flex; align-items: center; justify-content: center; padding: 60rpx 20rpx; border-radius: 20rpx; background-color: #fff; ">
|
||||
<image :src="path" style="width: 100%;" mode="widthFix" alt="" />
|
||||
</view>
|
||||
<!-- <view class=""
|
||||
style=" gap: 20rpx; margin-top: 20rpx; display: flex; align-items: center; background-color: #fff; border-radius: 15rpx;padding: 20rpx; ">
|
||||
<up-icon size='20' name="info-circle"></up-icon>
|
||||
<view class="" style="flex: 1; font-size: 28rpx; color: #666; ">
|
||||
绑定用户,获取积分
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { vpUserService, Service } from '@/Service/vp/vpUserService'
|
||||
import { ref } from "vue";
|
||||
|
||||
|
||||
let path = ref()
|
||||
onLoad(() => {
|
||||
getData()
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
|
||||
|
||||
const getData = () => {
|
||||
vpUserService.GetShareEwm().then(res => {
|
||||
if (res.code == 0) {
|
||||
path.value = 'https://vp.xypays.cn'+ res.data.path
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,369 @@
|
||||
{
|
||||
"easycom": {
|
||||
// 注意一定要放在custom里,否则无效,https://ask.dcloud.net.cn/question/131175
|
||||
"custom": {
|
||||
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
|
||||
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
|
||||
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue"
|
||||
}
|
||||
},
|
||||
|
||||
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
|
||||
|
||||
{
|
||||
|
||||
"path": "pages/index/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "首页",
|
||||
"navigationBarBackgroundColor": "#36394D",
|
||||
"navigationStyle": "custom",
|
||||
"backgroundColor": "#F8F8F8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/community",
|
||||
"style": {
|
||||
"navigationBarTitleText": "社区",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/user",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "pages/index/shop",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "积分商城"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "pages/index/order",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "我的订单"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "v派商家",
|
||||
"navigationBarBackgroundColor": "#fff",
|
||||
"backgroundColor": "#F8F8F8"
|
||||
},
|
||||
"subPackages": [{
|
||||
"root": "pages/community",
|
||||
"pages": [{
|
||||
"path": "noticeList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "社区公告"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "merchantCom",
|
||||
"style": {
|
||||
"navigationBarTitleText": "社区商家"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "merchantDetail",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "美食小店"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/goods",
|
||||
"pages": [
|
||||
{
|
||||
"path": "integralGoods",
|
||||
"style": {
|
||||
"navigationBarTitleText": "积分商品"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "merchant",
|
||||
"style": {
|
||||
"navigationBarTitleText": "热门商家"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "goodsDetail",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "商品详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "goodsContro",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "商品管理"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "addGoods",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "添加商品"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "Pay",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "付款"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "goodsPay",
|
||||
"style": {
|
||||
"navigationBarTitleText": "积分订单"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "search",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "搜索",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "goodsClass",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "分类",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/article",
|
||||
"pages": [{
|
||||
"path": "articleCom",
|
||||
"style": {
|
||||
"navigationBarTitleText": "社区公告"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "newsList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "新闻公告"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "news",
|
||||
"style": {
|
||||
"navigationBarTitleText": "新闻详情"
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
}, {
|
||||
"root": "pages/userFunc",
|
||||
"pages": [{
|
||||
"path" : "setData",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "编辑资料"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "integration",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "积分明细"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "trade",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "交易记录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "editStore",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "编辑店铺"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "statistics",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "数据统计"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "set",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "设置"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "bind",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "绑定手机号"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "password",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "修改支付密码"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "storeInter",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "积分明细"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "withdrow",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "提现"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "vip",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "会员码",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "promoteCode",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "推广码"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "addressList",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "收货地址"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path" : "addAddress",
|
||||
"style" :
|
||||
{
|
||||
"navigationBarTitleText" : "添加地址"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "alliance-card",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的联盟卡",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "member-benefits",
|
||||
"style": {
|
||||
"navigationBarTitleText": "会员权益",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "promotion",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的推广",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "invite",
|
||||
"style": {
|
||||
"navigationBarTitleText": "邀请好友",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "feedback",
|
||||
"style": {
|
||||
"navigationBarTitleText": "意见反馈",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "about-us",
|
||||
"style": {
|
||||
"navigationBarTitleText": "关于我们",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "coupon",
|
||||
"style": {
|
||||
"navigationBarTitleText": "优惠券",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "favorites",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的收藏",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
"tabBar": {
|
||||
"color": "#8a8a8a",
|
||||
"selectedColor": "#EC754B",
|
||||
"backgroundColor": "#ffffff",
|
||||
"list": [{
|
||||
"text": "首页",
|
||||
"pagePath": "pages/index/index",
|
||||
"iconPath": "/static/tabBar/home.png",
|
||||
"selectedIconPath": "/static/tabBar/homed.png"
|
||||
},
|
||||
// {
|
||||
// "text": "社区",
|
||||
// "pagePath": "pages/index/community",
|
||||
// "iconPath": "/static/tabBar/community.png",
|
||||
// "selectedIconPath": "/static/tabBar/communityed.png"
|
||||
// }
|
||||
// ,
|
||||
// {
|
||||
// "text": "积分商城",
|
||||
// "pagePath": "pages/index/shop",
|
||||
// "iconPath": "/static/tabBar/shop.png",
|
||||
// "selectedIconPath": "/static/tabBar/shoped.png"
|
||||
// },
|
||||
{
|
||||
"text": "我的订单",
|
||||
"pagePath": "pages/index/order",
|
||||
"iconPath": "/static/tabBar/shop.png",
|
||||
"selectedIconPath": "/static/tabBar/shoped.png"
|
||||
},
|
||||
{
|
||||
"text": "个人中心",
|
||||
"pagePath": "pages/index/user",
|
||||
"iconPath": "/static/tabBar/user.png",
|
||||
"selectedIconPath": "/static/tabBar/usered.png"
|
||||
}]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"uni-datetime-picker.selectDate": "选择日期",
|
||||
"uni-datetime-picker.selectTime": "选择时间",
|
||||
"uni-datetime-picker.selectDateTime": "选择日期时间",
|
||||
"uni-datetime-picker.startDate": "开始日期",
|
||||
"uni-datetime-picker.endDate": "结束日期",
|
||||
"uni-datetime-picker.startTime": "开始时间",
|
||||
"uni-datetime-picker.endTime": "结束时间",
|
||||
"uni-datetime-picker.ok": "确定",
|
||||
"uni-datetime-picker.clear": "清除",
|
||||
"uni-datetime-picker.cancel": "取消",
|
||||
"uni-datetime-picker.year": "年",
|
||||
"uni-datetime-picker.month": "月",
|
||||
"uni-calender.SUN": "日",
|
||||
"uni-calender.MON": "一",
|
||||
"uni-calender.TUE": "二",
|
||||
"uni-calender.WED": "三",
|
||||
"uni-calender.THU": "四",
|
||||
"uni-calender.FRI": "五",
|
||||
"uni-calender.SAT": "六",
|
||||
"uni-calender.confirm": "确认"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export class BaseConfig {
|
||||
// protected static servesUrl: string = "http://192.168.0.142:5002";//线下
|
||||
protected static servesUrl: string = "https://vp.xypays.cn";
|
||||
protected static imgUrl : string = "https://vp.clouds.xypays.cn";
|
||||
protected static mediaUrl: string = "http://byc1.xypays.cn/";
|
||||
protected static uploadUrl: string = "/TencentCos/GetUpLoadInfo";
|
||||
protected static payuploadUrl: string = "https://vp.xypays.cn";
|
||||
}
|
||||
13369
.svn/pristine/10/101e13d8720f3099fd32f83c24dca6a81e3c3c7f.svn-base
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="" v-if="props.show">
|
||||
<!-- <qf-image-cropper :src="props.url" :width="props.width" :height="props.height" :radius="20"
|
||||
@crop="handleCrop"></qf-image-cropper> -->
|
||||
<Tcropper :imageUrl="props.url" :width="props.width" @cancel="cancel" :height="props.height" :radius="0" @confirm='handleCrop' ></Tcropper>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { onBeforeUnmount, onMounted, reactive } from 'vue';
|
||||
import Tcropper from "../uni_modules/t-cropper/components/t-cropper/t-cropper.vue"
|
||||
import { Service } from '@/Service/Service'
|
||||
|
||||
|
||||
|
||||
|
||||
// const props = defineProps({
|
||||
// show : string,
|
||||
// // url : string,
|
||||
// // width : number,
|
||||
// // height:number,
|
||||
// // upType:string,
|
||||
// // retFun:Function
|
||||
// });
|
||||
|
||||
|
||||
let props = defineProps(["show","url",'width','height','upType',"retFun","imgName"]);
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
});
|
||||
|
||||
|
||||
const handleCrop = (e) => {
|
||||
let arr = e.tempFilePath.split('.')
|
||||
let name = arr[arr.length - 1]
|
||||
Service.uploadH5(e.tempFilePath, name ,(data:any)=>{
|
||||
props.retFun( {name:props.imgName,url:data})
|
||||
})
|
||||
}
|
||||
const cancel=()=>{
|
||||
console.log(11111);
|
||||
uni.$emit('cancle')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,466 @@
|
||||
<template>
|
||||
<view class="member-code-page">
|
||||
<!-- 沉浸式状态栏 -->
|
||||
<view class="status-bar"></view>
|
||||
|
||||
<!-- 顶部导航 -->
|
||||
<view class="nav-bar">
|
||||
<image class="back-icon" src="/static/icons/back.svg" @click="Service.GoPageBack()" mode="aspectFit" />
|
||||
<text class="nav-title">会员码</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<view class="content">
|
||||
<!-- 会员卡片 -->
|
||||
<view class="member-card">
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-section">
|
||||
<image class="user-avatar" :src="Service.GetMateUrlByImg(userData.headImg)" mode="aspectFill" />
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{ userData.nick }}</text>
|
||||
<!-- ID和会员等级标签在一排 -->
|
||||
<view class="tags-row">
|
||||
<view class="user-id-tag" @click="copyMemberId">
|
||||
<text class="ri-vip-crown-2-fill id-icon"></text>
|
||||
<text class="id-text">ID: {{ userData.userNo }}</text>
|
||||
<text class="ri-file-copy-line id-copy"></text>
|
||||
</view>
|
||||
<!-- <view class="user-member-tag">
|
||||
<text class="ri-vip-crown-fill member-icon"></text>
|
||||
<text class="member-text">黄金会员</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<view class="divider"></view>
|
||||
|
||||
<!-- 条形码区域 -->
|
||||
<view class="barcode-section">
|
||||
<u-barcode value="1234567890" :displayValue='false' :width="300"
|
||||
:height="100" />
|
||||
<text class="barcode-number">{{ user.memberCode }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 二维码区域 -->
|
||||
<view class="qrcode-section">
|
||||
<view class="qrcode-wrapper" @click="previewQrcode">
|
||||
<view class="qrcode-placeholder">
|
||||
<text class="ri-qr-code-line qrcode-icon"></text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="code-tip">向商家出示会员码,扫码享受积分优惠</text>
|
||||
</view>
|
||||
|
||||
<!-- 刷新提示 -->
|
||||
<view class="refresh-section">
|
||||
<text class="ri-time-line refresh-icon"></text>
|
||||
<text class="refresh-text">{{ refreshCountdown }}秒后自动刷新</text>
|
||||
<view class="refresh-btn" @click="refreshCode">
|
||||
<text class="ri-refresh-line btn-icon"></text>
|
||||
<text class="btn-text">刷新</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 温馨提示 -->
|
||||
<view class="tips-section">
|
||||
<view class="tips-title">
|
||||
<text class="ri-lightbulb-line tips-icon"></text>
|
||||
<text class="title-text">温馨提示</text>
|
||||
</view>
|
||||
<view class="tips-list">
|
||||
<text class="tips-item">• 会员码每分钟自动更新,确保使用安全</text>
|
||||
<text class="tips-item">• 结账时请向商家出示此码</text>
|
||||
<text class="tips-item">• 消费可获得积分,积分可抵扣现金</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { vpUserService, Service } from '@/Service/vp/vpUserService'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
// 用户数据
|
||||
const user = ref({
|
||||
nickname: '美食达人',
|
||||
avatar: 'https://picsum.photos/200/200?random=100',
|
||||
points: 2580,
|
||||
memberLevel: 'gold',
|
||||
memberId: '8888888',
|
||||
memberCode: 'VIP8888888888888'
|
||||
})
|
||||
|
||||
let userData = ref<any>({})
|
||||
|
||||
// 刷新倒计时(秒)
|
||||
const refreshCountdown = ref(60)
|
||||
let refreshTimer = null
|
||||
let countdownTimer = null
|
||||
|
||||
// 会员等级文本
|
||||
const memberLevelText = computed(() => {
|
||||
const levelMap = {
|
||||
'gold': '黄金会员',
|
||||
'platinum': '铂金会员',
|
||||
'diamond': '钻石会员'
|
||||
}
|
||||
return levelMap[user.value.memberLevel] || '普通会员'
|
||||
})
|
||||
onLoad(() => {
|
||||
fetchUserInfo()
|
||||
})
|
||||
|
||||
// 获取用户信息
|
||||
const fetchUserInfo = () => {
|
||||
vpUserService.GetUserInfo().then(res => {
|
||||
if (res.code == 0) {
|
||||
userData.value = res.data.userInfo
|
||||
console.log(userData.value);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 刷新会员码
|
||||
const refreshCode = () => {
|
||||
// 生成新的会员码
|
||||
const newCode = 'VIP' + Date.now().toString().slice(-12)
|
||||
user.value.memberCode = newCode
|
||||
|
||||
// 重置倒计时
|
||||
refreshCountdown.value = 60
|
||||
|
||||
uni.showToast({
|
||||
title: '会员码已刷新',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
}
|
||||
|
||||
// 预览条形码
|
||||
const previewBarcode = () => {
|
||||
uni.showToast({
|
||||
title: '条形码预览功能开发中',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 预览二维码
|
||||
const previewQrcode = () => {
|
||||
uni.showToast({
|
||||
title: '二维码预览功能开发中',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 复制会员ID
|
||||
const copyMemberId = () => {
|
||||
uni.setClipboardData({
|
||||
data: user.value.memberId,
|
||||
success: () => {
|
||||
uni.showToast({
|
||||
title: '会员ID已复制',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 开始自动刷新
|
||||
const startAutoRefresh = () => {
|
||||
// 清除之前的定时器
|
||||
if (refreshTimer) {
|
||||
clearInterval(refreshTimer)
|
||||
}
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer)
|
||||
}
|
||||
|
||||
// 倒计时定时器(每秒更新)
|
||||
countdownTimer = setInterval(() => {
|
||||
refreshCountdown.value--
|
||||
if (refreshCountdown.value <= 0) {
|
||||
refreshCountdown.value = 60
|
||||
// 自动刷新会员码
|
||||
const newCode = 'VIP' + Date.now().toString().slice(-12)
|
||||
user.value.memberCode = newCode
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 清理定时器
|
||||
onUnmounted(() => {
|
||||
if (refreshTimer) {
|
||||
clearInterval(refreshTimer)
|
||||
}
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 引入全局标签样式 */
|
||||
@import '@/styles/member-tags.scss';
|
||||
|
||||
/* 现代化会员码页面 */
|
||||
.member-code-page {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #FFF4E6 0%, #F5F5F5 100%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 状态栏 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 24rpx;
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.nav-placeholder {
|
||||
width: 40rpx;
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 32rpx 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 会员卡片 */
|
||||
.member-card {
|
||||
width: 100%;
|
||||
background: #FFFFFF;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(255, 107, 0, 0.12);
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
/* 用户信息区域 */
|
||||
.user-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 48rpx;
|
||||
margin-right: 20rpx;
|
||||
border: 3rpx solid #FF6B00;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
/* 分割线 */
|
||||
.divider {
|
||||
height: 1rpx;
|
||||
background: linear-gradient(90deg, transparent, #E0E0E0, transparent);
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
/* 条形码区域 */
|
||||
.barcode-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.barcode-wrapper {
|
||||
width: 480rpx;
|
||||
background: #F8F8F8;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx 32rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.barcode-placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.barcode-lines {
|
||||
font-size: 40rpx;
|
||||
color: #000000;
|
||||
letter-spacing: 2rpx;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.barcode-number {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
letter-spacing: 4rpx;
|
||||
}
|
||||
|
||||
/* 二维码区域 */
|
||||
.qrcode-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.qrcode-wrapper {
|
||||
width: 360rpx;
|
||||
height: 360rpx;
|
||||
background: #F8F8F8;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.qrcode-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #000000;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.qrcode-icon {
|
||||
font-size: 200rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.code-tip {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 刷新区域 */
|
||||
.refresh-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.refresh-icon {
|
||||
font-size: 24rpx;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
.refresh-text {
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4rpx;
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
padding: 10rpx 20rpx;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
font-size: 20rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 24rpx;
|
||||
color: #FFFFFF;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 温馨提示 */
|
||||
.tips-section {
|
||||
width: 100%;
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.tips-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.tips-icon {
|
||||
font-size: 28rpx;
|
||||
color: #FFA500;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.tips-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.tips-item {
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1 @@
|
||||
@import './styles/index.scss';
|
||||
@@ -0,0 +1,898 @@
|
||||
<template>
|
||||
<view class="profile-page">
|
||||
<!-- 沉浸式状态栏 -->
|
||||
<view class="status-bar"></view>
|
||||
|
||||
<!-- 个人中心骨架屏 -->
|
||||
<SkeletonProfile v-if="pageLoading" />
|
||||
|
||||
<!-- 个人中心内容 -->
|
||||
<view v-else>
|
||||
|
||||
<!-- 顶部用户信息区 - 浅色清新设计 -->
|
||||
<view class="user-header">
|
||||
<view class="user-info">
|
||||
<image class="user-avatar" :src="Service.GetMateUrlByImg(user.headImg)" mode="aspectFill" />
|
||||
<view class="user-details">
|
||||
<text class="user-name">{{ user.nick }}</text>
|
||||
<!-- ID和会员等级标签在一排 -->
|
||||
<view class="tags-row">
|
||||
<view class="user-id-tag" @click="copyMemberId">
|
||||
<text class="ri-vip-crown-2-fill id-icon"></text>
|
||||
<text class="id-text">ID: {{user.userNo}}</text>
|
||||
<text class="ri-file-copy-line id-copy"></text>
|
||||
</view>
|
||||
<!-- <view class="user-member-tag" v-if="user.memberLevel">
|
||||
<text class="ri-vip-crown-fill member-icon"></text>
|
||||
<text class="member-text">{{ memberLevelText }}</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 数据统计卡片 - 白色背景独立区域 -->
|
||||
<view class="stats-card-wrapper">
|
||||
<view class="stats-card">
|
||||
<view class="stats-item" @click="Service.GoPage('/pages/userFunc/integration')">
|
||||
<view class="stats-icon-box">
|
||||
<text class="ri-coin-line stats-icon"></text>
|
||||
</view>
|
||||
<view class="stats-content">
|
||||
<text class="stats-value">{{ accInfo.integral }}</text>
|
||||
<text class="stats-label">积分</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stats-divider"></view>
|
||||
<view class="stats-item" @click="Service.GoPage('/pages/userFunc/coupon')">
|
||||
<view class="stats-icon-box">
|
||||
<text class="ri-coupon-3-line stats-icon"></text>
|
||||
</view>
|
||||
<view class="stats-content">
|
||||
<text class="stats-value">{{ discount }}</text>
|
||||
<text class="stats-label">优惠券</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 会员码入口 -->
|
||||
<view class="member-code-section" @click="Service.GoPage('/pages/userFunc/vip')">
|
||||
<view class="member-code-card">
|
||||
<view class="code-left">
|
||||
<view class="code-title">
|
||||
<text class="ri-qr-code-line code-icon"></text>
|
||||
<text class="title-text">会员码</text>
|
||||
</view>
|
||||
<text class="code-desc">向商家出示会员码,享受积分优惠</text>
|
||||
</view>
|
||||
<view class="code-preview">
|
||||
<text class="ri-qr-code-line preview-icon"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能入口 - 网格布局 -->
|
||||
<view class="function-section">
|
||||
<view class="function-grid">
|
||||
<view class="function-item" @click="Service.GoPage('/pages/userFunc/vip')">
|
||||
<view class="function-icon-box" style="background: #FFF8E1;">
|
||||
<text class="ri-file-list-3-line function-icon" style="color: #FF9800;"></text>
|
||||
</view>
|
||||
<text class="function-name">我的订单</text>
|
||||
</view>
|
||||
|
||||
<view class="function-item" @click="Service.GoPage('/pages/userFunc/favorites')">
|
||||
<view class="function-icon-box" style="background: #FFEBEE;">
|
||||
<text class="ri-heart-line function-icon" style="color: #F44336;"></text>
|
||||
</view>
|
||||
<text class="function-name">我的收藏</text>
|
||||
</view>
|
||||
|
||||
<!-- <view class="function-item" @click="goToAddress">
|
||||
<view class="function-icon-box" style="background: #E3F2FD;">
|
||||
<text class="ri-map-pin-line function-icon" style="color: #2196F3;"></text>
|
||||
</view>
|
||||
<text class="function-name">收货地址</text>
|
||||
</view> -->
|
||||
|
||||
<view class="function-item" @click="serviceFunc">
|
||||
<view class="function-icon-box" style="background: #F3E5F5;">
|
||||
<text class="ri-customer-service-2-line function-icon" style="color: #9C27B0;"></text>
|
||||
</view>
|
||||
<text class="function-name">联系客服</text>
|
||||
</view>
|
||||
<view class="function-item" @click="showPhoneAuthModal = true">
|
||||
<view class="function-icon-box" style="background: #e7e7f5;">
|
||||
<text class="ri-smartphone-line function-icon" style="color: #8a77f5;"></text>
|
||||
</view>
|
||||
<text class="function-name">绑定手机号</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能列表 -->
|
||||
<view class="menu-section">
|
||||
<!-- 联盟卡功能块 -->
|
||||
<view class="scan-section" @click="Service.GoPage('/pages/userFunc/alliance-card')">
|
||||
<view class="scan-card">
|
||||
<view class="scan-icon-box">
|
||||
<text class="ri-bank-card-line scan-icon"></text>
|
||||
</view>
|
||||
<view class="scan-content">
|
||||
<text class="scan-title">我的联盟卡</text>
|
||||
<text class="scan-desc">绑定联盟卡,享受专属权益</text>
|
||||
</view>
|
||||
<text class="ri-arrow-right-s-line scan-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-group">
|
||||
<view class="menu-item" @click="Service.GoPage('/pages/userFunc/member-benefits')">
|
||||
<view class="item-left">
|
||||
<text class="ri-gift-line item-icon" style="color: #FF6B00;"></text>
|
||||
<text class="item-name">会员权益</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-desc">积分抵扣 · 专享优惠</text>
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="Service.GoPage('/pages/userFunc/promotion')">
|
||||
<view class="item-left">
|
||||
<text class="ri-team-line item-icon" style="color: #9C27B0;"></text>
|
||||
<text class="item-name">我的推广</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-desc">{{ promotionCount }}人</text>
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="Service.GoPage('/pages/userFunc/invite')">
|
||||
<view class="item-left">
|
||||
<text class="ri-user-add-line item-icon" style="color: #10B981;"></text>
|
||||
<text class="item-name">邀请好友</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-tag">赚积分</text>
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-group">
|
||||
<view class="menu-item" @click="Service.GoPage('/pages/userFunc/feedback')">
|
||||
<view class="item-left">
|
||||
<text class="ri-feedback-line item-icon" style="color: #3B82F6;"></text>
|
||||
<text class="item-name">意见反馈</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="Service.GoPage('/pages/userFunc/about-us')">
|
||||
<view class="item-left">
|
||||
<text class="ri-information-line item-icon" style="color: #64748B;"></text>
|
||||
<text class="item-name">关于我们</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="goToSettings">
|
||||
<view class="item-left">
|
||||
<text class="ri-settings-4-line item-icon" style="color: #64748B;"></text>
|
||||
<text class="item-name">设置</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 手机号授权弹窗 -->
|
||||
<view v-if="showPhoneAuthModal" class="phone-auth-overlay" @click="closePhoneAuthModal">
|
||||
<view class="phone-auth-modal" @click.stop>
|
||||
<!-- 关闭按钮 -->
|
||||
<view class="modal-close" @click="closePhoneAuthModal">
|
||||
<text class="ri-close-line close-icon"></text>
|
||||
</view>
|
||||
|
||||
<!-- 顶部图标 -->
|
||||
<view class="modal-header">
|
||||
<view class="header-icon-box">
|
||||
<text class="ri-wechat-fill header-icon"></text>
|
||||
</view>
|
||||
<text class="header-title">微信授权</text>
|
||||
<text class="header-desc">使用微信授权快速获取手机号</text>
|
||||
</view>
|
||||
|
||||
<!-- 微信授权按钮 -->
|
||||
<button class="auth-btn wechat" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">
|
||||
<view class="btn-content">
|
||||
<text class="ri-wechat-fill btn-icon"></text>
|
||||
<text class="btn-text">微信授权手机号</text>
|
||||
</view>
|
||||
</button>
|
||||
|
||||
<!-- 温馨提示 -->
|
||||
<view class="auth-tips">
|
||||
<view class="tips-item">
|
||||
<text class="ri-checkbox-circle-fill tips-icon"></text>
|
||||
<text class="tips-text">授权后可快速联系在线客服</text>
|
||||
</view>
|
||||
<view class="tips-item">
|
||||
<text class="ri-shield-check-fill tips-icon"></text>
|
||||
<text class="tips-text">您的信息将严格保密</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { Service } from "@/Service/Service"
|
||||
import { ref } from "vue";
|
||||
import { vpUserService } from '@/Service/vp/vpUserService'
|
||||
import SkeletonProfile from '@/components/skeleton/skeleton-profile.vue'
|
||||
|
||||
let user = ref<any>({
|
||||
headImg: ''
|
||||
})
|
||||
let accInfo=ref<any>({})
|
||||
let pageLoading = ref<boolean>(true)
|
||||
|
||||
let showPhoneAuthModal = ref<boolean>(false)
|
||||
|
||||
let discount=ref(0)
|
||||
|
||||
onLoad(() => {
|
||||
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
getUserinfo()
|
||||
});
|
||||
|
||||
const gotopage = (item : any) => {
|
||||
if (item.path) {
|
||||
Service.GoPage(item.path)
|
||||
}
|
||||
}
|
||||
|
||||
const serviceFunc = (item : any, index : any) => {
|
||||
if (index == 0) {
|
||||
wx.openCustomerServiceChat({
|
||||
extInfo: { url: 'https://work.weixin.qq.com/kfid/kfc959c128ce7801256' },
|
||||
corpId: 'wwb1123fbb286554ab',
|
||||
success(res) { },
|
||||
fail(err) {
|
||||
console.log(err, '失败')
|
||||
// 失败回调
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Service.GoPage(item.path)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const getUserinfo = () => {
|
||||
vpUserService.GetUserInfo().then(res => {
|
||||
pageLoading.value = false
|
||||
user.value = res.data.userInfo
|
||||
discount.value=res.data.discount
|
||||
accInfo.value=res.data.accInfo
|
||||
})
|
||||
}
|
||||
|
||||
// 复制会员ID
|
||||
const copyMemberId = () => {
|
||||
console.log('000')
|
||||
uni.setClipboardData({
|
||||
data: user.value.userNo.toString(),
|
||||
success: () => {
|
||||
Service.Msg('会员ID已复制')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 关闭手机号授权弹窗
|
||||
const closePhoneAuthModal = () => {
|
||||
showPhoneAuthModal.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 引入全局标签样式 */
|
||||
@import '@/styles/member-tags.scss';
|
||||
|
||||
.profile-page {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
padding-bottom: calc(100rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
/* 状态栏 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #FFF4E6, #FFE0B2);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 顶部用户信息区 */
|
||||
.user-header {
|
||||
background: linear-gradient(135deg, #FFF4E6, #FFE0B2);
|
||||
padding: 20rpx 20rpx 50rpx;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 60rpx;
|
||||
border: 4rpx solid rgba(255, 255, 255, 0.5);
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.user-details {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
display: block;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
/* 数据统计卡片 - 白色背景独立区域 */
|
||||
.stats-card-wrapper {
|
||||
background: #FFFFFF;
|
||||
margin: -20rpx 0 20rpx;
|
||||
border-radius: 0;
|
||||
border-top-left-radius: 24rpx;
|
||||
border-top-right-radius: 24rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.stats-card {
|
||||
background: transparent;
|
||||
padding: 24rpx 24rpx;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.stats-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
/* 统计图标盒子 */
|
||||
.stats-icon-box {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
background: linear-gradient(135deg, #FFF4E6, #FFE0B2);
|
||||
}
|
||||
|
||||
.stats-icon {
|
||||
font-size: 32rpx;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
/* 统计内容 */
|
||||
.stats-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4rpx;
|
||||
}
|
||||
|
||||
.stats-value {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #222222;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.stats-label {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.stats-divider {
|
||||
width: 1rpx;
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
height: 64rpx;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
/* 会员码入口 */
|
||||
.member-code-section {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.member-code-card {
|
||||
background: linear-gradient(135deg, #FFF4E6, #FFE0B2);
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 4rpx 12rpx rgba(255, 107, 0, 0.15);
|
||||
}
|
||||
|
||||
.code-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.code-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.code-icon {
|
||||
font-size: 32rpx;
|
||||
color: #FF9800;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.code-desc {
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
margin-left: 40rpx;
|
||||
}
|
||||
|
||||
.code-preview {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.preview-icon {
|
||||
font-size: 48rpx;
|
||||
color: #FF9800;
|
||||
}
|
||||
|
||||
/* 功能入口 */
|
||||
.function-section {
|
||||
background: #FFFFFF;
|
||||
margin: 0 20rpx 20rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx 20rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.function-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.function-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.function-icon-box {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.function-icon {
|
||||
font-size: 44rpx;
|
||||
}
|
||||
|
||||
.function-name {
|
||||
font-size: 24rpx;
|
||||
color: #222222;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 菜单列表 */
|
||||
.menu-section {
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
/* 扫一扫功能块 */
|
||||
.scan-section {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.scan-card {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
border-radius: 16rpx;
|
||||
padding: 28rpx 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 4rpx 16rpx rgba(255, 107, 0, 0.25);
|
||||
}
|
||||
|
||||
.scan-icon-box {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.scan-icon {
|
||||
font-size: 40rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.scan-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6rpx;
|
||||
}
|
||||
|
||||
.scan-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.scan-desc {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
|
||||
.scan-arrow {
|
||||
font-size: 32rpx;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
|
||||
.menu-group {
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 28rpx 24rpx;
|
||||
border-bottom: 1rpx solid #F5F5F5;
|
||||
}
|
||||
|
||||
.menu-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
font-size: 40rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 28rpx;
|
||||
color: #222222;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item-desc {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.item-tag {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9800);
|
||||
color: #FFFFFF;
|
||||
font-size: 20rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 20rpx;
|
||||
margin-right: 8rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-arrow {
|
||||
font-size: 28rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
/* 底部导航栏 - 精致简约风格 */
|
||||
.tabbar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #FFFFFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
padding: 12rpx 0 calc(12rpx + env(safe-area-inset-bottom));
|
||||
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
z-index: 999;
|
||||
height: calc(90rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.tabbar-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10rpx 0;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tabbar-icon {
|
||||
font-size: 44rpx;
|
||||
color: #CCCCCC;
|
||||
margin-bottom: 8rpx;
|
||||
transition: all 0.3s ease;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.tabbar-text {
|
||||
font-size: 22rpx;
|
||||
color: #CCCCCC;
|
||||
font-weight: 400;
|
||||
transition: all 0.3s ease;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* 激活状态 */
|
||||
.tabbar-item.active .tabbar-icon {
|
||||
color: #FF6B00;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.tabbar-item.active .tabbar-text {
|
||||
color: #FF6B00;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 手机号授权弹窗 */
|
||||
.phone-auth-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.phone-auth-modal {
|
||||
width: 100%;
|
||||
background: #FFFFFF;
|
||||
border-radius: 32rpx 32rpx 0 0;
|
||||
padding: 48rpx 32rpx 40rpx;
|
||||
padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
|
||||
animation: slideUp 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
top: 24rpx;
|
||||
right: 24rpx;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #F5F5F5;
|
||||
border-radius: 30rpx;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
font-size: 32rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 48rpx;
|
||||
}
|
||||
|
||||
.header-icon-box {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
background: linear-gradient(135deg, #E8F5E9, #C8E6C9);
|
||||
border-radius: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
font-size: 56rpx;
|
||||
color: #07C160;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.header-desc {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.auth-btn {
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
border-radius: 48rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
margin-bottom: 20rpx;
|
||||
overflow: visible;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.auth-btn.wechat {
|
||||
background: linear-gradient(135deg, #07C160, #06AD56);
|
||||
}
|
||||
|
||||
.auth-btn.wechat::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.auth-btn.manual {
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
.btn-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.auth-btn.wechat .btn-icon {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.auth-btn.manual .btn-icon {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.auth-btn.wechat .btn-text {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.auth-btn.manual .btn-text {
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.auth-tips {
|
||||
margin-top: 32rpx;
|
||||
padding: 24rpx;
|
||||
background: #FFF9F0;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.tips-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.tips-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tips-icon {
|
||||
font-size: 28rpx;
|
||||
color: #FF9800;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tips-text {
|
||||
font-size: 22rpx;
|
||||
color: #666666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,97 @@
|
||||
# qf-image-cropper
|
||||
## 图片裁剪插件
|
||||
uniapp微信小程序图片裁剪插件,支持自定义尺寸、定点等比例缩放、拖动、图片翻转、剪切圆形/圆角图片、定制样式,功能多性能高体验好注释全。
|
||||
|
||||
### 平台支持:
|
||||
1. 支持微信小程序:移动端、PC端、开发者工具
|
||||
2. 支持H5平台(2.1.0版本起)
|
||||
3. 支持APP平台(2.1.5版本起):Android、IOS
|
||||
4. 其他平台暂未测试兼容性未知
|
||||
|
||||
### 支持功能:
|
||||
1. 自定义裁剪尺寸
|
||||
2. 定点等比例缩放:移动端以双指触摸中心点为缩放中心点,PC端以鼠标所在点为缩放中心点
|
||||
3. 自由拖动:支持限制滑出边界,也支持回弹效果(滑动时可滑出边界,释放时回弹到边界)
|
||||
4. 图片翻转:在裁剪尺寸非 1:1 的情况下,翻转时宽高无法铺满裁剪区域时,图片会自动放大到合适尺寸
|
||||
5. 裁剪生成新图片
|
||||
6. 本地选择图片
|
||||
7. 可定制样式:可自由选择是否渲染裁剪边框、可伸缩裁剪顶角、参考线
|
||||
8. 裁剪圆角图片:圆形、圆角矩形
|
||||
|
||||
### 属性说明
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
|:---|:---|:---|:---|
|
||||
| src | String | | 图片资源地址 |
|
||||
| width | Number | 300 | 裁剪宽度 |
|
||||
| height | Number | 300 | 裁剪高度 |
|
||||
| showBorder | Boolean | true | 是否绘制裁剪区域边框 |
|
||||
| showGrid | Boolean | true | 是否绘制裁剪区域网格参考线 |
|
||||
| showAngle | Boolean | true | 是否展示四个支持伸缩的角 |
|
||||
| areaScale | Number | 0.3 | 裁剪区域最小缩放倍数 |
|
||||
| minScale | Number | 1 | 图片最小缩放倍数 |
|
||||
| maxScale | Number | 5 | 图片最大缩放倍数 |
|
||||
| checkRange | Boolean | true | 检查图片位置是否超出裁剪边界,如果超出则会矫正位置 |
|
||||
| backgroundColor | String | | 生成图片背景色:如果裁剪区域没有完全包含在图片中时,不设置该属性则生成图片存在一定的透明块 |
|
||||
| bounce | Boolean | true | 是否有回弹效果:当 checkRange 为 true 时有效,拖动时可以拖出边界,释放时会弹回边界 |
|
||||
| rotatable | Boolean | true | 是否支持翻转 |
|
||||
| reverseRotatable | Boolean | false | 是否支持逆向翻转 |
|
||||
| choosable | Boolean | true | 是否支持从本地选择素材 |
|
||||
| gpu | Boolean | false | 是否开启硬件加速,图片缩放过程中如果出现元素的“留影”或“重影”效果,可通过该方式解决或减轻这一问题 |
|
||||
| angleSize | Number | 20 | 四个角尺寸,单位px |
|
||||
| angleBorderWidth | Number | 2 | 四个角边框宽度,单位px |
|
||||
| zIndex | Number/String | | 调整组件层级 |
|
||||
| radius | Number | | 裁剪图片圆角半径,单位px |
|
||||
| fileType | String | png | 生成文件的类型,只支持 'jpg' 或 'png'。默认为 'png' |
|
||||
| delay | Number | 1000 | 图片从绘制到生成所需时间,单位ms<br>微信小程序平台使用 `Canvas 2D` 绘制时有效<br>如绘制大图或出现裁剪图片空白等情况应适当调大该值,因 `Canvas 2d` 采用同步绘制,需自己把控绘制完成时间 |
|
||||
| navigation | Boolean | true | 页面是否是原生标题栏:<br>H5平台当 showAngle 为 true 时,使用插件的页面在 `page.json` 中配置了 `"navigationStyle": "custom"` 时,必须将此值设为 false ,否则四个可拉伸角的触发位置会有偏差。<br>注:因H5平台的窗口高度是包含标题栏的,而屏幕触摸点的坐标是不包含的 |
|
||||
| @crop | EventHandle | | 剪裁完成后触发,event = { tempFilePath }。在H5平台下,tempFilePath 为 base64 |
|
||||
|
||||
### 基本用法
|
||||
```
|
||||
<template>
|
||||
<div>
|
||||
<qf-image-cropper :width="500" :height="500" :radius="30" @crop="handleCrop"></qf-image-cropper>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import QfImageCropper from '@/components/qf-image-cropper/qf-image-cropper.vue';
|
||||
export default {
|
||||
components: {
|
||||
QfImageCropper
|
||||
},
|
||||
methods: {
|
||||
handleCrop(e) {
|
||||
uni.previewImage({
|
||||
urls: [e.tempFilePath],
|
||||
current: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
通过ref组件实例可在进入页面后直接打开相册选择图片
|
||||
```
|
||||
mounted() {
|
||||
this.$refs.qfImageCropper.chooseImage({ sourceType: ['album'] });
|
||||
}
|
||||
```
|
||||
### 使用说明
|
||||
1.建议在`pages.json`中将引用插件的页面添加一下配置禁止下拉刷新和禁止页面滑动,防止出现性能或页面抖动等问题。
|
||||
```
|
||||
{
|
||||
"enablePullDownRefresh": false,
|
||||
"disableScroll": true
|
||||
}
|
||||
```
|
||||
2.建议使用本插件不要设置过大宽高的目标图片尺寸,建议1365x1365以内,否则可能会导致如下问题:
|
||||
```
|
||||
1.界面卡顿,内存占用过高
|
||||
2.生成图片失真(模糊)
|
||||
3.确定裁剪后一直显示 `裁剪中...`,该问题是由 `uni.canvasToTempFilePath` 无法回调导致,不同平台不同设备限制可能有所不同。
|
||||
```
|
||||
3.如裁剪后的图片存在偏移的问题,请检查是否受自己项目中父组件或全局样式影响。
|
||||
4.src属性设置网络图片时,图片资源必须是能触发 `getImageInfo` API 的 success 回调才可用于插件裁剪。因此小程序平台获取网络图片信息需先配置download域名白名单才能生效。
|
||||
5.如果组件无法正常渲染且使用了 `v-if` 时,可尝试将 `v-if` 替换为 `v-show`
|
||||
6.如果App端导入组件后无法正常渲染,请尝试重新运行
|
||||
@@ -0,0 +1,791 @@
|
||||
<template>
|
||||
<view class="index-page">
|
||||
<!-- 页面整体加载状态 -->
|
||||
<view v-if="loding" class="page-loading">
|
||||
<!-- 固定顶部区域 -->
|
||||
<view class="top-fixed">
|
||||
<view class="status-bar-placeholder"></view>
|
||||
<view class="top-section-fixed">
|
||||
<view class="location-box-skeleton">
|
||||
<view class="skeleton-location-icon"></view>
|
||||
<view class="skeleton-location-text"></view>
|
||||
<view class="skeleton-arrow-icon"></view>
|
||||
</view>
|
||||
|
||||
<view class="search-wrapper">
|
||||
<view class="search-box-skeleton"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 状态栏占位 - 沉浸式状态栏 -->
|
||||
<view class="status-bar"></view>
|
||||
|
||||
<!-- 占位区域 - 抵消固定顶部的高度 -->
|
||||
<view class="top-placeholder"></view>
|
||||
|
||||
<!-- 分类骨架屏 -->
|
||||
<view class="category-section">
|
||||
<SkeletonCategory />
|
||||
</view>
|
||||
|
||||
<!-- 商家列表骨架屏 -->
|
||||
<view class="shop-list">
|
||||
<view class="shop-waterfall">
|
||||
<view class="waterfall-column">
|
||||
<SkeletonShopCard v-for="i in 3" :key="'left-' + i" />
|
||||
</view>
|
||||
<view class="waterfall-column">
|
||||
<SkeletonShopCard v-for="i in 3" :key="'right-' + i" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 实际内容 -->
|
||||
<view v-else>
|
||||
<!-- 固定顶部区域 -->
|
||||
<view class="top-fixed">
|
||||
<view class="status-bar-placeholder"></view>
|
||||
<view class="top-section-fixed">
|
||||
<view class="location-box" @click.stop="choooseLocation()">
|
||||
<up-icon name="map" color="#fff" size="12"></up-icon>
|
||||
<text class="location-text" style="margin: 0 4rpx;" >{{ location }}</text>
|
||||
<up-icon name="arrow-right" color="#fff" size="10"></up-icon>
|
||||
</view>
|
||||
|
||||
<view class="search-wrapper">
|
||||
<view class="search-box">
|
||||
<input type="text" v-model="searchKey"
|
||||
@confirm="Service.GoPage('/pages/goods/search?search='+searchKey)" class="search-input"
|
||||
placeholder="请输入关键字" placeholder-class="search-placeholder" />
|
||||
<view @click="Service.GoPage('/pages/goods/search?search='+searchKey)" class="search-btn">搜索
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 状态栏占位 - 沉浸式状态栏 -->
|
||||
<view class="status-bar"></view>
|
||||
|
||||
<!-- 占位区域 - 抵消固定顶部的高度 -->
|
||||
<view class="top-placeholder"></view>
|
||||
|
||||
<!-- 分类导航 - 三行五列 -->
|
||||
<view class="category-section">
|
||||
<view class="category-grid">
|
||||
<view v-for="(category, index) in tabList" :key="index" class="category-item"
|
||||
@click="Service.GoPage('/pages/goods/goodsClass?type='+category.assortId+'&name='+category.name )">
|
||||
<view class="category-icon-box" :style="{ background: category.bgColor }">
|
||||
<image :src="Service.GetMateUrlByImg(category.icon)" mode=""
|
||||
style="width: 100%; height: 100%;"></image>
|
||||
</view>
|
||||
<text class="category-name">{{ category.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商家列表 - 瀑布流布局 -->
|
||||
<view class="shop-list">
|
||||
<view class="shop-waterfall">
|
||||
<!-- 左列 -->
|
||||
<view class="waterfall-column">
|
||||
<view v-for="shop in leftGoodsList" :key="shop.merchId" class="shop-card"
|
||||
@click="Service.GoPage('/pages/community/merchantDetail?merchId='+shop.merchId)">
|
||||
<view class="shop-cover-wrapper">
|
||||
<image class="shop-cover" :src="Service.GetMateUrlByImg(shop.logo)" mode="aspectFill" />
|
||||
<!-- 浮动标签 - 左下角距离 -->
|
||||
<view class="float-tag float-distance">
|
||||
<up-icon name="map" color="#fff" size="12"></up-icon>
|
||||
<text class="tag-text">{{formatDistance(shop.distance)}}</text>
|
||||
</view>
|
||||
<!-- 右上角商家标签 -->
|
||||
<view class="shop-badges">
|
||||
<text v-if="shop.codeName=='联盟商家'" class="badge-partner">{{shop.codeName }}</text>
|
||||
<text v-else class="badge-coop">{{shop.codeName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="shop-info">
|
||||
<view class="shop-header">
|
||||
<text class="shop-name">{{ shop.name }}</text>
|
||||
</view>
|
||||
|
||||
<view class="shop-meta">
|
||||
<text class="shop-sales">月售:{{ shop.sale }}</text>
|
||||
<text class="shop-avg-price">人均{{ shop.price }}</text>
|
||||
</view>
|
||||
|
||||
<view class="shop-footer">
|
||||
<view class="shop-tags">
|
||||
|
||||
<view v-for="(coupon, idx) in shop.tips" :key="idx" :class="getTagClass(coupon)"
|
||||
style="font-size: 22rpx; margin-left: 4rpx;" class="shop-tag">
|
||||
{{ coupon }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右列 -->
|
||||
<view class="waterfall-column">
|
||||
<view v-for="shop in rightGoodsList" :key="shop.merchId" class="shop-card"
|
||||
@click="Service.GoPage('/pages/community/merchantDetail?merchId='+shop.merchId)">
|
||||
<view class="shop-cover-wrapper">
|
||||
<image class="shop-cover" :src="Service.GetMateUrlByImg(shop.logo)" mode="aspectFill" />
|
||||
<!-- 浮动标签 - 左下角距离 -->
|
||||
<view class="float-tag float-distance">
|
||||
<up-icon name="map" color="#fff" size="12"></up-icon>
|
||||
<text class="tag-text">{{formatDistance(shop.distance)}}</text>
|
||||
</view>
|
||||
<!-- 右上角商家标签 -->
|
||||
<view class="shop-badges">
|
||||
<text v-if="shop.code=='Discounts'" class="badge-partner">联盟商家</text>
|
||||
<text v-else class="badge-coop">合作商家</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="shop-info">
|
||||
<view class="shop-header">
|
||||
<text class="shop-name">{{ shop.name }}</text>
|
||||
</view>
|
||||
|
||||
<view class="shop-meta">
|
||||
<text class="shop-sales">月售:{{ shop.sale }}</text>
|
||||
<text class="shop-avg-price">人均{{ shop.price }}</text>
|
||||
</view>
|
||||
|
||||
<view class="shop-footer">
|
||||
<view class="shop-tags">
|
||||
<view v-for="(coupon, idx) in shop.tips" :key="idx" :class="getTagClass(coupon)"
|
||||
style="font-size: 22rpx;" class="shop-tag">
|
||||
{{ coupon }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<up-loadmore :status="status" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
onLoad, onReachBottom, onShareAppMessage, onShareTimeline
|
||||
} from '@dcloudio/uni-app'
|
||||
import {
|
||||
ref,
|
||||
onMounted,
|
||||
computed
|
||||
} from 'vue'
|
||||
import SkeletonCategory from '../../components/skeleton/skeleton-category.vue'
|
||||
import SkeletonShopCard from '../../components/skeleton/skeleton-shop-card.vue'
|
||||
import { vpLoginService, Service } from '@/Service/vp/vpLoginService'
|
||||
import { vpMerchService } from '@/Service/vp/vpMerchService'
|
||||
import { vpUserService } from '@/Service/vp/vpUserService'
|
||||
|
||||
// 数据
|
||||
let location = ref('')
|
||||
const categories = ref([])
|
||||
const shops = ref([])
|
||||
const currentCategory = ref('all')
|
||||
let status = ref('nomore')
|
||||
|
||||
let loding = ref(true)
|
||||
|
||||
let longitude = ref(0)
|
||||
let latitude = ref(0)
|
||||
|
||||
let searchKey = ref('')
|
||||
|
||||
let leftGoodsList = ref<Array<any>>([])
|
||||
let rightGoodsList = ref<Array<any>>([])
|
||||
let tabList = ref<Array<any>>([])
|
||||
let page = ref(1)
|
||||
let scene=ref('')
|
||||
onLoad((data:any) => {
|
||||
if(data.scene){
|
||||
scene.value=data.scene
|
||||
}
|
||||
if (data.q) {
|
||||
scene.value = decodeURIComponent(data.q).split('?')[1].split('=')[1]
|
||||
}
|
||||
getLocation()
|
||||
})
|
||||
// 分享
|
||||
onShareAppMessage((res) => {
|
||||
return {
|
||||
path: '/pages/index/index'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
onShareTimeline(() => {
|
||||
return {
|
||||
path: '/pages/index/index'
|
||||
}
|
||||
})
|
||||
|
||||
onReachBottom(()=>{
|
||||
getList()
|
||||
})
|
||||
|
||||
const getLocation = () => {
|
||||
uni.getLocation({
|
||||
type: 'wgs84',
|
||||
success: function (res) {
|
||||
longitude.value = res.longitude
|
||||
latitude.value = res.latitude
|
||||
getAddress()
|
||||
if (!Service.GetUserToken()) {
|
||||
login()
|
||||
return
|
||||
}
|
||||
getdata()
|
||||
},
|
||||
fail: function (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const login = () => {
|
||||
uni.getProvider({
|
||||
service: 'oauth',
|
||||
success: function (res : any) {
|
||||
uni.login({
|
||||
onlyAuthorize: true,
|
||||
provider: res.provider,
|
||||
success: function (loginRes) {
|
||||
vpLoginService.WxLogin(loginRes.code, res.provider == 'weixin' ? 1 : 3, longitude.value, latitude.value, scene.value).then(content => {
|
||||
if (content.code == 0) {
|
||||
Service.SetUserToken(content.data.accToken)
|
||||
getdata()
|
||||
} else {
|
||||
Service.Msg(content.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const getdata = () => {
|
||||
leftGoodsList.value = []
|
||||
rightGoodsList.value = []
|
||||
status.value = 'loadmore'
|
||||
page.value = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList = () => {
|
||||
|
||||
if (status.value !== 'loadmore') {
|
||||
return
|
||||
}
|
||||
status.value = 'loading'
|
||||
|
||||
|
||||
vpMerchService.GetMerchList('', '', longitude.value, latitude.value,0,0, page.value).then(res => {
|
||||
loding.value = false
|
||||
res.data.merchList.forEach((goodsItem : any, GoodsIndex : number) => {
|
||||
if (GoodsIndex % 2 == 0) {
|
||||
leftGoodsList.value.push(goodsItem)
|
||||
} else {
|
||||
rightGoodsList.value.push(goodsItem)
|
||||
}
|
||||
})
|
||||
tabList.value = res.data.assortList
|
||||
status.value = res.data.merchList.length == 10 ? 'loadmore' : 'nomore'
|
||||
page.value++
|
||||
})
|
||||
}
|
||||
|
||||
const getAddress = () => {
|
||||
vpUserService.GetAddressInfo(longitude.value, latitude.value).then(res => {
|
||||
if (res.code == 0) {
|
||||
location.value = res.data.addrName
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const choooseLocation = () => {
|
||||
uni.chooseLocation({
|
||||
success: function (res) {
|
||||
longitude.value = res.longitude
|
||||
latitude.value = res.latitude
|
||||
location.value = res.address
|
||||
getdata()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 格式化距离
|
||||
const formatDistance = (distance : any) => {
|
||||
if (distance < 1) {
|
||||
return `${Number(distance*1000).toFixed(1)}m`
|
||||
}
|
||||
return `${(distance).toFixed(1)}km`
|
||||
}
|
||||
|
||||
// 根据标签文本获取样式类
|
||||
const getTagClass = (tagText : string) => {
|
||||
const tagMap = {
|
||||
'可用积分': 'tag-points-available',
|
||||
'可用券': 'tag-coupon'
|
||||
}
|
||||
return tagMap[tagText] || 'tag-points'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.index-page {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
/* 状态栏占位 - 沉浸式 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 固定顶部区域 - 始终固定 */
|
||||
.top-fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.status-bar-placeholder {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.top-section-fixed {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
padding: 65rpx 20rpx 28rpx;
|
||||
}
|
||||
|
||||
/* 占位区域 - 抵消固定顶部的高度 */
|
||||
.top-placeholder {
|
||||
height: calc(var(--status-bar-height) + 170rpx);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.location-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.location-icon-small {
|
||||
font-size: 28rpx;
|
||||
color: #FFFFFF;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.location-text {
|
||||
font-size: 30rpx;
|
||||
color: #FFFFFF;
|
||||
font-weight: 500;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
font-size: 24rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* 搜索区域 */
|
||||
.search-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
/* 搜索框 */
|
||||
.search-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #FFFFFF;
|
||||
border-radius: 32rpx;
|
||||
padding: 8rpx 28rpx;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #222222;
|
||||
height: 48rpx;
|
||||
line-height: 48rpx;
|
||||
}
|
||||
|
||||
.search-placeholder {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
background: #FF6B00;
|
||||
color: #FFFFFF;
|
||||
font-size: 24rpx;
|
||||
padding: 6rpx 18rpx;
|
||||
border-radius: 20rpx;
|
||||
font-weight: 500;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 分类导航 - 三行五列 */
|
||||
.category-section {
|
||||
background: #FFFFFF;
|
||||
padding: 32rpx 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.category-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 32rpx 20rpx;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.category-icon-box {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.category-icon-box text {
|
||||
font-size: 44rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 24rpx;
|
||||
color: #222222;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 商家列表 - 瀑布流布局 */
|
||||
.shop-list {
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.shop-waterfall {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.waterfall-column {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.shop-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
.shop-cover-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
}
|
||||
|
||||
.shop-cover {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 浮动标签 */
|
||||
.float-tag {
|
||||
position: absolute;
|
||||
padding: 2rpx 6rpx;
|
||||
border-radius: 3rpx;
|
||||
font-size: 16rpx;
|
||||
backdrop-filter: blur(10rpx);
|
||||
-webkit-backdrop-filter: blur(10rpx);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2rpx;
|
||||
}
|
||||
|
||||
.float-distance {
|
||||
left: 6rpx;
|
||||
bottom: 6rpx;
|
||||
background: rgba(0, 0, 0, 0.65);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.float-distance .tag-icon {
|
||||
font-size: 14rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.float-distance .tag-text {
|
||||
font-size: 16rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
padding: 16rpx;
|
||||
}
|
||||
|
||||
// 右上角
|
||||
.shop-badges {
|
||||
position: absolute;
|
||||
top: 16rpx;
|
||||
right: 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
|
||||
/* 商家信息 */
|
||||
.shop-header {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #222222;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.shop-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.shop-sales {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.shop-avg-price {
|
||||
font-size: 26rpx;
|
||||
color: #FF6B00;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 商家标签 */
|
||||
.shop-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4rpx;
|
||||
}
|
||||
|
||||
/* 标签 */
|
||||
.tag-badge {
|
||||
display: inline-block;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 6rpx;
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
margin-right: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tag-coupon {
|
||||
background: #FFF8E1;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
.shop-price {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #FF4D4F;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.shop-original {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty {
|
||||
grid-column: 1 / -1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 120rpx;
|
||||
color: #CCCCCC;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
/* 底部导航栏 - 小巧精致风格 */
|
||||
.tabbar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #FFFFFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
padding: 12rpx 0 calc(12rpx + env(safe-area-inset-bottom));
|
||||
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
z-index: 999;
|
||||
height: calc(90rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.tabbar-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10rpx 0;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tabbar-icon {
|
||||
font-size: 44rpx;
|
||||
color: #CCCCCC;
|
||||
margin-bottom: 8rpx;
|
||||
transition: all 0.3s ease;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.tabbar-text {
|
||||
font-size: 22rpx;
|
||||
color: #CCCCCC;
|
||||
font-weight: 400;
|
||||
transition: all 0.3s ease;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* 激活状态 */
|
||||
.tabbar-item.active .tabbar-icon {
|
||||
color: #FF6B00;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.tabbar-item.active .tabbar-text {
|
||||
color: #FF6B00;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 首页头部骨架屏样式 */
|
||||
.location-box-skeleton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.skeleton-location-icon {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
border-radius: 4rpx;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.3) 25%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.3) 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading-white 1.5s ease-in-out infinite;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.skeleton-location-text {
|
||||
width: 200rpx;
|
||||
height: 28rpx;
|
||||
border-radius: 4rpx;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.3) 25%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.3) 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading-white 1.5s ease-in-out infinite;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.skeleton-arrow-icon {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
border-radius: 4rpx;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.3) 25%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.3) 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading-white 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.search-box-skeleton {
|
||||
width: 100%;
|
||||
height: 60rpx;
|
||||
border-radius: 32rpx;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.3) 25%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.3) 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading-white 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes loading-white {
|
||||
0% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
.page-loading {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,186 @@
|
||||
# t-cropper
|
||||
|
||||
> **t-cropper 一款高性能移动端图片裁剪工具**
|
||||
|
||||
## 平台兼容
|
||||
|
||||
| App | H5 | 微信小程序 | 支付宝小程序 |
|
||||
| :---: | :---: | :----------: | :-----------: |
|
||||
| √ | √ | √ | √ |
|
||||
|
||||
### 属性说明
|
||||
|
||||
|属性 |类型 |默认 |备注 |
|
||||
| :--------: | :-----: | :----: | :----: |
|
||||
| mode |String | "ratio" | 裁剪模式|
|
||||
| imageUrl |String | " " | 需要裁剪的图片路径|
|
||||
| width |Number | 200 | 图片裁剪后的宽度,固定大小时有效|
|
||||
| height |Number | 200 | 图片裁剪后的高度,固定大小时有效|
|
||||
| maxWidth |Number | 1024 | 图片裁剪后的最大宽度 |
|
||||
| maxHeight |Number | 1024 | 图片裁剪后的最大高度 |
|
||||
| scaleRatio |Number | 0.7 | 裁剪比列缩放,建议不超过0.95 |
|
||||
| minRatio |Number | 1 | 最小缩放 |
|
||||
| maxRatio |Number | 3 | 最大缩放 |
|
||||
| radius |Number | 0 | 裁剪图片圆角半径,单位px |
|
||||
| delay |Number | 250 | 确定按钮快速重复点击时间 |
|
||||
| isRotateBtn |Boolean | true | 是否显示旋转按钮 |
|
||||
|
||||
### mode有效值
|
||||
|
||||
| 模式 |值 |说明 |
|
||||
| :-----: | :-----: | :----: |
|
||||
| 固定模式 |fixed | 裁剪出指定大小的图片,一般用于头像上传 |
|
||||
| 等比缩放 |ratio | 限定宽高比,裁剪大小不固定 |
|
||||
| 自由模式 |free | 不限定宽高比,裁剪大小不固定 |
|
||||
|
||||
### 事件说明
|
||||
|
||||
|事件名称 |说明 |返回 |
|
||||
| :--------: | :-----: | :----: |
|
||||
| confirm |点击确定按钮 | object |
|
||||
| cancel |点击取消按钮 | - |
|
||||
|
||||
### 示例
|
||||
|
||||
```html
|
||||
|
||||
<template>
|
||||
<view>
|
||||
<t-cropper
|
||||
mode="ratio"
|
||||
:imageUrl="imageUrl"
|
||||
:width="500"
|
||||
:height="500"
|
||||
:radius="100"
|
||||
:delay="150"
|
||||
@cancel="onCancel"
|
||||
@confirm="onConfirm"
|
||||
></t-cropper>
|
||||
<view class="preview">
|
||||
<image
|
||||
v-for="(item, index) in resultUrl"
|
||||
:key="item.id"
|
||||
class="images"
|
||||
@click="prviewImgae(index, item.url)"
|
||||
:src="item.url"
|
||||
/>
|
||||
</view>
|
||||
<button class="button" type="primary" @click="selectFile">选择图片</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, toRefs } from "vue";
|
||||
const model = reactive({
|
||||
imageUrl: "",
|
||||
resultUrl: [],
|
||||
});
|
||||
|
||||
const { resultUrl, imageUrl } = toRefs(model);
|
||||
|
||||
// 使用uni.compressImage压缩图片
|
||||
const compressImage = () => {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: "original",
|
||||
success: (res) => {
|
||||
console.log("original", res);
|
||||
// 示例:防止大图片文件的Image无法直接临时路径显示图片
|
||||
uni.showLoading({
|
||||
title: "处理中...",
|
||||
mask: true,
|
||||
});
|
||||
// 使用uni.compressImage压缩图片
|
||||
uni.compressImage({
|
||||
src: res.tempFilePaths[0],
|
||||
quality: 80, // 压缩质量
|
||||
success: (compressRes) => {
|
||||
model.imageUrl = compressRes.tempFilePath;
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error("图片压缩失败:", err);
|
||||
},
|
||||
complete: () => {
|
||||
uni.hideLoading(); // 关闭loading
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 使用默认压缩方式
|
||||
const defaultCompressImage = () => {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: "compressed",
|
||||
success: (res) => {
|
||||
model.imageUrl = res.tempFilePaths[0];
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*** 特别声明:在使用 uni.chooseImage 选择的大图片文件无法直接在 Image 组件中显示,通常涉及到以下可能的问题和限制。
|
||||
*** 图片大小和尺寸限制:移动设备和浏览器对于能够加载和处理的图片大小有限制,如果选择的图片文件尺寸过大,可能无法正常加载和显示。
|
||||
*** 性能问题:大图片文件可能会导致页面加载缓慢或者卡顿,尤其是在移动设备上。
|
||||
*** 内存问题:加载大图片可能会消耗大量的内存资源,特别是在移动设备上,可能导致内存不足或者页面崩溃的问题。
|
||||
*** 解决参考方案如下:
|
||||
*** 防止选择大文件图片后无法在Image中直接临时路径显示图片,导致无法在裁剪插件中显示,
|
||||
*** 根据项目需要对大尺寸图片进行压缩、对图片质量要求高的,需要提前上传至oss进行采用网络图片进行裁剪。
|
||||
*/
|
||||
const selectFile = () => {
|
||||
// 推荐使用其他压缩方式,这里只是简单大图片压缩示例-经供思路参考切勿使用
|
||||
// 示例一:uni.compressImage压缩图片
|
||||
// compressImage();
|
||||
|
||||
// 示例二:使用自带压缩图
|
||||
defaultCompressImage();
|
||||
};
|
||||
|
||||
// 关闭裁剪
|
||||
const onCancel = () => {
|
||||
model.imageUrl = "";
|
||||
};
|
||||
|
||||
// 裁剪确认
|
||||
const onConfirm = (res) => {
|
||||
// 设置url的值,显示控件
|
||||
const params = {
|
||||
id: new Date().getTime(),
|
||||
url: res.tempFilePath,
|
||||
};
|
||||
model.resultUrl.push(params);
|
||||
model.imageUrl = "";
|
||||
};
|
||||
|
||||
// 预览图片
|
||||
const prviewImgae = (index, url) => {
|
||||
uni.previewImage({
|
||||
current: index, // 当前资源下标
|
||||
urls: [url],
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.preview {
|
||||
padding: 32rpx;
|
||||
|
||||
.images {
|
||||
margin: 10rpx;
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
}
|
||||
}
|
||||
.button {
|
||||
margin: 0 20rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
### 注意
|
||||
|
||||
1.uni-app版本不断更新,插件有时无法适应新版本,感谢大家及时提交bug,但希望大家手下留情,不要轻易给差评!
|
||||
3912
.svn/pristine/12/12e3d1ab0fd1e1053a2010ad29a968c5bbb700a4.svn-base
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"uni-datetime-picker.selectDate": "select date",
|
||||
"uni-datetime-picker.selectTime": "select time",
|
||||
"uni-datetime-picker.selectDateTime": "select date and time",
|
||||
"uni-datetime-picker.startDate": "start date",
|
||||
"uni-datetime-picker.endDate": "end date",
|
||||
"uni-datetime-picker.startTime": "start time",
|
||||
"uni-datetime-picker.endTime": "end time",
|
||||
"uni-datetime-picker.ok": "ok",
|
||||
"uni-datetime-picker.clear": "clear",
|
||||
"uni-datetime-picker.cancel": "cancel",
|
||||
"uni-datetime-picker.year": "-",
|
||||
"uni-datetime-picker.month": "",
|
||||
"uni-calender.MON": "MON",
|
||||
"uni-calender.TUE": "TUE",
|
||||
"uni-calender.WED": "WED",
|
||||
"uni-calender.THU": "THU",
|
||||
"uni-calender.FRI": "FRI",
|
||||
"uni-calender.SAT": "SAT",
|
||||
"uni-calender.SUN": "SUN",
|
||||
"uni-calender.confirm": "confirm"
|
||||
}
|
||||
@@ -0,0 +1,684 @@
|
||||
<template>
|
||||
<view class="search-page">
|
||||
<!-- 顶部导航栏 - 橙色背景 -->
|
||||
<view class="nav-bar">
|
||||
<view class="nav-content">
|
||||
<view class="nav-back" @click="Service.GoPageBack()">
|
||||
<image class="back-icon" src="/static/icons/back.svg" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="search-input-wrapper" >
|
||||
<image class="search-icon" src="https://img.icons8.com/ios-glyphs/30/999999/search--v1.png"
|
||||
mode="aspectFit" />
|
||||
<input class="search-input" v-model="searchKeyword" placeholder="搜索商家或商品"
|
||||
placeholder-class="search-placeholder" @input="onSearchInput" @confirm="onSearch" focus />
|
||||
<!-- <image v-if="searchKeyword" class="clear-icon"
|
||||
src="https://img.icons8.com/ios-glyphs/30/999999/close-window--v1.png" mode="aspectFit"
|
||||
@click="clearSearch" /> -->
|
||||
</view>
|
||||
<!-- <text class="search-btn" @click="onSearch">搜索</text> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索内容区域 -->
|
||||
<view class="content-wrapper">
|
||||
<!-- 搜索历史 - 无搜索内容时显示 -->
|
||||
<view v-if="hasSearched" class="search-history-section">
|
||||
<!-- 搜索历史 -->
|
||||
<view v-if="searchHistory.length > 0" class="history-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">搜索历史</text>
|
||||
<image class="delete-icon" src="https://img.icons8.com/ios-glyphs/30/999999/delete--v1.png"
|
||||
mode="aspectFit" @click="clearHistory" />
|
||||
</view>
|
||||
<view class="history-tags">
|
||||
<view v-for="(item, index) in searchHistory" :key="index" class="history-tag"
|
||||
@click="searchFromHistory(item)">
|
||||
<text class="history-tag-text">{{ item }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 热门搜索 -->
|
||||
<view class="hot-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">热门搜索</text>
|
||||
</view>
|
||||
<view class="hot-tags">
|
||||
<view v-for="(item, index) in hotSearches" :key="index" class="hot-tag"
|
||||
@click="searchFromHistory(item)">
|
||||
<text class="hot-tag-text" :class="{ 'top-tag': index < 3 }">{{ item }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索结果 - 有搜索内容时显示 -->
|
||||
<view class="search-results-section">
|
||||
<!-- 搜索建议 - 输入时显示 -->
|
||||
<!-- <view class="suggestions-section">
|
||||
<view v-for="(item, index) in searchSuggestions" :key="index" class="suggestion-item"
|
||||
@click="searchFromHistory(item)">
|
||||
<image class="suggestion-icon" src="https://img.icons8.com/ios-glyphs/30/999999/search--v1.png"
|
||||
mode="aspectFit" />
|
||||
<text class="suggestion-text">{{ item }}</text>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 搜索结果列表 -->
|
||||
<view class="results-list">
|
||||
<!-- 商家列表 -->
|
||||
<view class="shop-list">
|
||||
<view v-for="shop in goodsList" :key="shop.merchId" class="shop-card" @click="Service.GoPage('/pages/goods/goodsDetail?merchId='+shop.merchId)">
|
||||
<!-- 左侧图片 -->
|
||||
<image class="shop-image" :src="Service.GetMateUrlByImg(shop.logo)" mode="aspectFill" />
|
||||
|
||||
<!-- 右侧信息 -->
|
||||
<view class="shop-info">
|
||||
<!-- 店名 -->
|
||||
<view class="shop-header">
|
||||
<text class="shop-name">{{ shop.name }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 评分和销量 -->
|
||||
<view class="shop-info-left">
|
||||
<view class="shop-rating">
|
||||
<text class="rating-score">{{ shop.score }}</text>
|
||||
<text class="rating-unit">分</text>
|
||||
</view>
|
||||
<text class="shop-type">堂食店</text>
|
||||
<text class="shop-sales">月售{{ shop.sale }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 距离、用时、人均价格 - 同一行 -->
|
||||
<view class="shop-info-row">
|
||||
<!-- 距离和用时 - 左侧 -->
|
||||
<view class="shop-info-left">
|
||||
<view class="distance-wrapper">
|
||||
<text class="ri-map-pin-2-line location-icon"></text>
|
||||
<text class="distance-text">{{ formatDistance(shop.distance ) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 人均价格 - 右侧 -->
|
||||
<view class="shop-avg-price-inline">
|
||||
<text class="avg-price-small">¥</text>
|
||||
<text class="avg-price-value-small">{{ shop.price }}</text>
|
||||
<text class="avg-price-text-small">人均</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部标签 -->
|
||||
<view class="shop-bottom-tags">
|
||||
<text v-for="(tag, index) in shop.tips" :key="index" class="shop-tag" style="font-size: 24rpx;"
|
||||
:class="getTagClass(tag)">
|
||||
{{ tag }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 右上角标签 -->
|
||||
<view class="shop-badges" style="top: 24rpx;" >
|
||||
<text v-if="shop.code=='Discounts'" class="badge-partner" style="font-size: 24rpx;">联盟商家</text>
|
||||
<text v-else class="badge-coop" style="font-size: 24rpx;" >合作商家</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<up-loadmore :status="status" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ref
|
||||
} from 'vue'
|
||||
import {
|
||||
onLoad
|
||||
} from '@dcloudio/uni-app'
|
||||
import { Service } from "@/Service/Service"
|
||||
import { vpMerchService } from '@/Service/vp/vpMerchService'
|
||||
|
||||
|
||||
// 搜索关键词
|
||||
const searchKeyword = ref('')
|
||||
|
||||
// 是否已搜索
|
||||
const hasSearched = ref(false)
|
||||
|
||||
// 是否显示搜索结果
|
||||
const showResults = ref(false)
|
||||
|
||||
// 搜索结果
|
||||
const searchResults = ref([])
|
||||
|
||||
// 搜索历史
|
||||
const searchHistory = ref(['汉堡', '奶茶', '炸鸡', '披萨'])
|
||||
|
||||
// 热门搜索
|
||||
const hotSearches = ref(['汉堡王', '肯德基', '麦当劳', '星巴克', '必胜客', '喜茶', '奈雪的茶', '瑞幸咖啡'])
|
||||
|
||||
// 模拟商家数据
|
||||
const mockShops = [{
|
||||
id: 1,
|
||||
name: '河南省许昌市汉堡王(宏安店)',
|
||||
cover: 'https://img.icons8.com/color/96/000000/burger.png',
|
||||
rating: '4.8',
|
||||
sales: '2000+',
|
||||
distance: '500m',
|
||||
time: '25分钟',
|
||||
avgPrice: '25.0',
|
||||
coupons: ['12%积分', '可用券'],
|
||||
badgeType: 'partner' // 只使用一个类型标签
|
||||
}
|
||||
]
|
||||
|
||||
let longitude=ref(0)
|
||||
let latitude=ref(0)
|
||||
|
||||
let status=ref('nomore')
|
||||
let goodsList=ref<Array<any>>([])
|
||||
let page=ref(1)
|
||||
|
||||
// 页面加载
|
||||
onLoad((data : any) => {
|
||||
searchKeyword.value = data.search
|
||||
getLocation()
|
||||
})
|
||||
|
||||
// 搜索建议
|
||||
const searchSuggestions = ref([])
|
||||
|
||||
const getLocation = () => {
|
||||
uni.getLocation({
|
||||
type: 'wgs84',
|
||||
success: function (res) {
|
||||
longitude.value = res.longitude
|
||||
latitude.value = res.latitude
|
||||
getdata()
|
||||
},
|
||||
fail: function (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const getdata = () => {
|
||||
goodsList.value=[]
|
||||
status.value = 'loadmore'
|
||||
page.value = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList = () => {
|
||||
|
||||
if (status.value !== 'loadmore') {
|
||||
return
|
||||
}
|
||||
status.value = 'loading'
|
||||
|
||||
|
||||
vpMerchService.GetMerchList('', searchKeyword.value, longitude.value, latitude.value, page.value).then(res => {
|
||||
goodsList.value=[...goodsList.value,...res.data.merchList]
|
||||
status.value = res.data.merchList.length == 10 ? 'loadmore' : 'nomore'
|
||||
page.value++
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 输入搜索关键词
|
||||
const onSearchInput = () => {
|
||||
if (searchKeyword.value.trim()) {
|
||||
// 模拟搜索建议
|
||||
searchSuggestions.value = hotSearches.value.filter(item =>
|
||||
item.toLowerCase().includes(searchKeyword.value.toLowerCase())
|
||||
).slice(0, 5)
|
||||
} else {
|
||||
searchSuggestions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 执行搜索
|
||||
const onSearch = () => {
|
||||
const keyword = searchKeyword.value.trim()
|
||||
|
||||
if (!keyword) {
|
||||
uni.showToast({
|
||||
title: '请输入搜索关键词',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 保存到搜索历史
|
||||
if (!searchHistory.value.includes(keyword)) {
|
||||
searchHistory.value.unshift(keyword)
|
||||
if (searchHistory.value.length > 10) {
|
||||
searchHistory.value = searchHistory.value.slice(0, 10)
|
||||
}
|
||||
}
|
||||
|
||||
// 执行搜索 - 使用模拟数据
|
||||
// hasSearched.value = true
|
||||
showResults.value = true
|
||||
|
||||
// 过滤模拟数据
|
||||
searchResults.value = mockShops.filter(shop =>
|
||||
shop.name.toLowerCase().includes(keyword.toLowerCase())
|
||||
)
|
||||
|
||||
// 如果没有匹配结果,显示所有数据(方便演示)
|
||||
if (searchResults.value.length === 0) {
|
||||
searchResults.value = mockShops
|
||||
}
|
||||
}
|
||||
|
||||
// 从历史/热门搜索
|
||||
const searchFromHistory = (keyword) => {
|
||||
searchKeyword.value = keyword
|
||||
onSearch()
|
||||
}
|
||||
|
||||
// 清除搜索
|
||||
const clearSearch = () => {
|
||||
searchKeyword.value = ''
|
||||
searchSuggestions.value = []
|
||||
showResults.value = false
|
||||
}
|
||||
|
||||
// 清除搜索历史
|
||||
const clearHistory = () => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定清空搜索历史吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
searchHistory.value = []
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取标签样式类
|
||||
const getTagClass = (tagText) => {
|
||||
const tagMap = {
|
||||
'12%积分': 'tag-points',
|
||||
'15%积分': 'tag-points',
|
||||
'可用积分': 'tag-points-available',
|
||||
'可用券': 'tag-coupon'
|
||||
}
|
||||
return tagMap[tagText] || 'tag-default'
|
||||
}
|
||||
|
||||
// 格式化距离
|
||||
const formatDistance = (distance : any) => {
|
||||
if (distance < 1000) {
|
||||
return `${distance}m`
|
||||
}
|
||||
return `${(distance / 1000).toFixed(1)}km`
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-page {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
/* 顶部导航栏 */
|
||||
.nav-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: calc(var(--status-bar-height) + 146rpx);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.nav-content {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 90rpx;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.nav-back {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.search-input-wrapper {
|
||||
width: 60%;
|
||||
background: #FFFFFF;
|
||||
border-radius: 20px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #222222;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.search-placeholder {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
font-size: 15px;
|
||||
color: #FFFFFF;
|
||||
font-weight: 500;
|
||||
flex-shrink: 0;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
/* 搜索内容区域 */
|
||||
.content-wrapper {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
background: #FAFAFA;
|
||||
min-height: calc(100vh - var(--status-bar-height) - 60px);
|
||||
}
|
||||
|
||||
/* 搜索历史和热门搜索 */
|
||||
.search-history-section {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.history-section,
|
||||
.hot-section {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.history-tags,
|
||||
.hot-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.history-tag,
|
||||
.hot-tag {
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #E0E0E0;
|
||||
border-radius: 4px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.history-tag-text,
|
||||
.hot-tag-text {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.hot-tag .top-tag {
|
||||
color: #FF6B00;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 搜索建议 */
|
||||
.suggestions-section {
|
||||
background: #FFFFFF;
|
||||
margin: 16px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.suggestion-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #F0F0F0;
|
||||
}
|
||||
|
||||
.suggestion-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.suggestion-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 12px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.suggestion-text {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
/* 搜索结果 */
|
||||
.search-results-section {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.results-list {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* 商家列表 */
|
||||
.shop-list {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.shop-card {
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #E0E0E0;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 商家图片 */
|
||||
.shop-image {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 6px;
|
||||
flex-shrink: 0;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
/* 商家信息 */
|
||||
.shop-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* 店名 */
|
||||
.shop-header {
|
||||
margin-bottom: 6px;
|
||||
padding-right: 60px;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* 左侧信息:评分、类型、销量 */
|
||||
.shop-info-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.shop-rating {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.rating-score {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #FF9800;
|
||||
}
|
||||
|
||||
.rating-unit {
|
||||
font-size: 10px;
|
||||
color: #FF9800;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.shop-info-left .shop-type {
|
||||
font-size: 12px;
|
||||
color: #4CAF50;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.shop-info-left .shop-sales {
|
||||
font-size: 12px;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
/* 距离、用时、人均价格 - 同一行 */
|
||||
.shop-info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.distance-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.location-icon {
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.distance-text {
|
||||
font-size: 12px;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.time-text {
|
||||
font-size: 12px;
|
||||
color: #666666;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
/* 人均价格 - 小号内联样式 */
|
||||
.shop-avg-price-inline {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.avg-price-small {
|
||||
font-size: 11px;
|
||||
color: #FF6B00;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.avg-price-value-small {
|
||||
font-size: 15px;
|
||||
color: #FF6B00;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.avg-price-text-small {
|
||||
font-size: 11px;
|
||||
color: #FF6B00;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
/* 底部标签 */
|
||||
.shop-bottom-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
/* 无搜索结果 */
|
||||
.no-results {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80px 20px;
|
||||
}
|
||||
|
||||
.no-results-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin-bottom: 16px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.no-results-text {
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.no-results-hint {
|
||||
font-size: 14px;
|
||||
color: #999999;
|
||||
}
|
||||
</style>
|
||||
1037
.svn/pristine/14/140ba14f185ca2ad9f501e77f81845d3df15c837.svn-base
Normal file
@@ -0,0 +1,208 @@
|
||||
<template>
|
||||
<view style="margin: 20rpx 30rpx;">
|
||||
<view class="" style="border-radius: 20rpx; background-color: #fff; padding: 20rpx; ">
|
||||
<view class="" style="font-weight: 600; font-size: 30rpx;">
|
||||
商品封面
|
||||
</view>
|
||||
<view class=""
|
||||
style=" margin-top: 20rpx; width: 100%; height: 320rpx; border: 4rpx dashed #e2e2e2; border-radius: 20rpx; overflow: hidden; ">
|
||||
<view class=""
|
||||
style="width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; ">
|
||||
<img :src="Service.GetIconImg('/static/goods/photo.png')" style="width: 80rpx; height: 80rpx;"
|
||||
alt="" />
|
||||
<view class="" style=" color: #666666; font-size: 28rpx;">
|
||||
上传图片
|
||||
</view>
|
||||
</view>
|
||||
<!-- <image :src="Service.GetMateUrlByImg('/static/dele/dele4.jpg')" mode="aspectFill" style="width: 100%; height: 100%; " alt="" /> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
<view class="" style=" margin-top: 20rpx; border-radius: 20rpx; background-color: #fff; padding: 20rpx; ">
|
||||
<up-form labelWidth='90' :labelStyle="{'font-weight': 600 }" labelPosition="top" :model="goodsInfo"
|
||||
ref="form1">
|
||||
<up-form-item label="商品名称" prop="goodsInfo.name" :borderBottom="false" ref="item1">
|
||||
<up-input :customStyle="{'border-radius': '15rpx' }" placeholder="请输入商品名称" border='surround'
|
||||
v-model="goodsInfo.name"></up-input>
|
||||
</up-form-item>
|
||||
<up-form-item label="价格" prop="goodsInfo.sex" :borderBottom="false">
|
||||
<up-input prefixIcon='rmb' :prefixIconStyle="{ 'color':'#000','font-weight': 600 }"
|
||||
:customStyle="{'border-radius': '15rpx' }" placeholder="请输入价格" v-model="goodsInfo.prince"
|
||||
border="surround"></up-input>
|
||||
</up-form-item>
|
||||
<up-form-item label="描述" prop="goodsInfo.sex" :borderBottom="false">
|
||||
<up-textarea v-model="goodsInfo.des" placeholder="请输入商品描述"></up-textarea>
|
||||
</up-form-item>
|
||||
<up-form-item label="分类" prop="goodsInfo.sex" :borderBottom="false">
|
||||
<view class=""
|
||||
style="padding: 12rpx 18rpx; border-radius: 7px; border: 1rpx solid #e2e2e2; width: 100%; ">
|
||||
<view class="" @click="showClass=true"
|
||||
style="display: flex; height: 48rpx; align-items: center; justify-content: space-between;">
|
||||
<view class="" :style="{ 'color':goodsInfo.class? '#000':'#c0c4cc' } ">
|
||||
{{ goodsInfo.class?goodsInfo.class:'请选择分类'}}
|
||||
</view>
|
||||
<view class="">
|
||||
<up-icon name="arrow-right" size="14" color='#666666' :bold='true'></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</up-form-item>
|
||||
<up-form-item label="状态" prop="goodsInfo.sex" :borderBottom="false">
|
||||
<up-radio-group v-model="goodsInfo.status" placement="row">
|
||||
<up-radio :customStyle="{marginBottom: '8px'}" v-for="(item, index) in radiolist" :key="index"
|
||||
:label="item.name" :name="item.value" @change="radioChange">
|
||||
</up-radio>
|
||||
</up-radio-group>
|
||||
</up-form-item>
|
||||
<up-form-item label="标签" :borderBottom="false">
|
||||
<view v-for="(item, index) in tabsList" @click="clickTab(index)" :key="index"
|
||||
:class="{active:!goodsInfo.tabs.includes(index),actived:goodsInfo.tabs.includes(index)}">
|
||||
{{item}}
|
||||
</view>
|
||||
</up-form-item>
|
||||
</up-form>
|
||||
</view>
|
||||
|
||||
<view class="" style="width: 100%; height: 200rpx;">
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<view class="action-buttons">
|
||||
<u-button type="primary" @click="Service.GoPageBack()" :custom-style="ButtonStyle">取消</u-button>
|
||||
<u-button type="primary" @click="save()" :custom-style="ButtonStyle">保存</u-button>
|
||||
</view>
|
||||
|
||||
|
||||
<up-picker :show="showClass" @cancel="showClass=!showClass" @confirm="confirmClass"
|
||||
:columns="columns"></up-picker>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { Service } from "@/Service/Service"
|
||||
import { ref } from "vue";
|
||||
|
||||
|
||||
let goodsInfo = ref({
|
||||
name: "",
|
||||
prince: '',
|
||||
des: "",
|
||||
class: '',
|
||||
status: '',
|
||||
tabs: []
|
||||
})
|
||||
|
||||
let showClass = ref(false)
|
||||
const columns = ref([
|
||||
['中国', '美国', '日本']
|
||||
]);
|
||||
|
||||
const radiolist = ref([
|
||||
{
|
||||
name: '上架中',
|
||||
value: '0'
|
||||
|
||||
},
|
||||
{
|
||||
name: '已下架',
|
||||
value: '1'
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
let tabsList = ref([
|
||||
'新品',
|
||||
'热销',
|
||||
'推荐',
|
||||
'折扣',
|
||||
'特惠'
|
||||
])
|
||||
|
||||
|
||||
const ButtonStyle = ref({
|
||||
backgroundColor: '#FF6600',
|
||||
borderColor: '#FF6600',
|
||||
color: '#FFFFFF',
|
||||
fontSize: '28rpx',
|
||||
height: '75rpx',
|
||||
borderRadius: '45rpx',
|
||||
marginLeft: '20rpx'
|
||||
});
|
||||
|
||||
onLoad(() => {
|
||||
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
|
||||
|
||||
const confirmClass = (e) => {
|
||||
goodsInfo.value.class = e.value[0]
|
||||
showClass.value = !showClass.value
|
||||
}
|
||||
|
||||
|
||||
const radioChange = (e) => {
|
||||
goodsInfo.value.status = e
|
||||
}
|
||||
|
||||
const clickTab = (e : any) => {
|
||||
if (goodsInfo.value.tabs.includes(e)) {
|
||||
let a = goodsInfo.value.tabs.findIndex((x) => {
|
||||
return x == e
|
||||
})
|
||||
goodsInfo.value.tabs.splice(a, 1)
|
||||
return
|
||||
}
|
||||
|
||||
goodsInfo.value.tabs.push(e)
|
||||
}
|
||||
|
||||
const save=()=>{
|
||||
Service.GoPageBack()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: #b5b5b5;
|
||||
border: 1rpx solid #b5b5b5;
|
||||
margin-right: 15rpx;
|
||||
border-radius: 23rpx;
|
||||
padding: 5rpx 16rpx;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.actived {
|
||||
color: var(--nav-mian);
|
||||
border: 1rpx solid var(--nav-mian);
|
||||
margin-right: 15rpx;
|
||||
border-radius: 23rpx;
|
||||
padding: 5rpx 16rpx;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
/* 底部操作按钮 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
padding: 30rpx 30rpx;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #ffffff;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,797 @@
|
||||
<template>
|
||||
<view class="index-page">
|
||||
<!-- 页面整体加载状态 -->
|
||||
<view v-if="loding" class="page-loading">
|
||||
<!-- 固定顶部区域 -->
|
||||
<view class="top-fixed">
|
||||
<view class="status-bar-placeholder"></view>
|
||||
<view class="top-section-fixed">
|
||||
<view class="location-box-skeleton">
|
||||
<view class="skeleton-location-icon"></view>
|
||||
<view class="skeleton-location-text"></view>
|
||||
<view class="skeleton-arrow-icon"></view>
|
||||
</view>
|
||||
<view class="search-wrapper">
|
||||
<view class="search-box-skeleton"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 状态栏占位 - 沉浸式状态栏 -->
|
||||
<view class="status-bar"></view>
|
||||
|
||||
<!-- 占位区域 - 抵消固定顶部的高度 -->
|
||||
<view class="top-placeholder"></view>
|
||||
|
||||
<!-- 分类骨架屏 -->
|
||||
<view class="category-section">
|
||||
<SkeletonCategory />
|
||||
</view>
|
||||
|
||||
<!-- 商家列表骨架屏 -->
|
||||
<view class="shop-list">
|
||||
<view class="shop-waterfall">
|
||||
<view class="waterfall-column">
|
||||
<SkeletonShopCard v-for="i in 3" :key="'left-' + i" />
|
||||
</view>
|
||||
<view class="waterfall-column">
|
||||
<SkeletonShopCard v-for="i in 3" :key="'right-' + i" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 实际内容 -->
|
||||
<view v-else>
|
||||
<!-- 固定顶部区域 -->
|
||||
<view class="top-fixed">
|
||||
<view class="status-bar-placeholder"></view>
|
||||
<view class="top-section-fixed">
|
||||
<view class="location-box" @click.stop="choooseLocation()">
|
||||
<up-icon name="map" color="#fff" size="12"></up-icon>
|
||||
<text class="location-text" style="margin: 0 4rpx;" >{{ location }}</text>
|
||||
<up-icon name="arrow-right" color="#fff" size="10"></up-icon>
|
||||
</view>
|
||||
|
||||
<view class="search-wrapper">
|
||||
<view class="search-box">
|
||||
<input type="text" v-model="searchKey"
|
||||
@confirm="Service.GoPage('/pages/goods/search?search='+searchKey)" class="search-input"
|
||||
placeholder="请输入关键字" placeholder-class="search-placeholder" />
|
||||
<view @click="Service.GoPage('/pages/goods/search?search='+searchKey)" class="search-btn">搜索
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 状态栏占位 - 沉浸式状态栏 -->
|
||||
<view class="status-bar"></view>
|
||||
|
||||
<!-- 占位区域 - 抵消固定顶部的高度 -->
|
||||
<view class="top-placeholder"></view>
|
||||
|
||||
<!-- 分类导航 - 三行五列 -->
|
||||
<view class="category-section">
|
||||
<view class="category-grid">
|
||||
<view v-for="(category, index) in tabList" :key="index" class="category-item"
|
||||
@click="Service.GoPage('/pages/goods/goodsClass?type='+category.assortId+'&name='+category.name )">
|
||||
<view class="category-icon-box" :style="{ background: category.bgColor }">
|
||||
<image :src="Service.GetMateUrlByImg(category.icon)"
|
||||
style="width: 100%; height: 100%;"></image>
|
||||
</view>
|
||||
<text class="category-name">{{ category.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商家列表 - 瀑布流布局 -->
|
||||
<view class="shop-list">
|
||||
<view class="shop-waterfall">
|
||||
<!-- 左列 -->
|
||||
<view class="waterfall-column">
|
||||
<view v-for="shop in leftGoodsList" :key="shop.merchId" class="shop-card"
|
||||
@click="Service.GoPage('/pages/community/merchantDetail?merchId='+shop.merchId)">
|
||||
<view class="shop-cover-wrapper">
|
||||
<image class="shop-cover" :src="Service.GetMateUrlByImg(shop.logo)" mode="aspectFill" />
|
||||
<!-- 浮动标签 - 左下角距离 -->
|
||||
<view class="float-tag float-distance">
|
||||
<up-icon name="map" color="#fff" size="12"></up-icon>
|
||||
<text class="tag-text">{{formatDistance(shop.distance)}}</text>
|
||||
</view>
|
||||
<!-- 右上角商家标签 -->
|
||||
<view class="shop-badges">
|
||||
<text v-if="shop.codeName=='联盟商家'" class="badge-partner">{{shop.codeName }}</text>
|
||||
<text v-else class="badge-coop">{{shop.codeName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="shop-info">
|
||||
<view class="shop-header">
|
||||
<text class="shop-name">{{ shop.name }}</text>
|
||||
</view>
|
||||
|
||||
<view class="shop-meta">
|
||||
<text class="shop-sales">月售:{{ shop.sale }}</text>
|
||||
<text class="shop-avg-price">人均{{ shop.price }}</text>
|
||||
</view>
|
||||
|
||||
<view class="shop-footer">
|
||||
<view class="shop-tags">
|
||||
|
||||
<view v-for="(coupon, idx) in shop.tips" :key="idx" :class="getTagClass(coupon)"
|
||||
style="font-size: 22rpx; margin-left: 4rpx;" class="shop-tag">
|
||||
{{ coupon }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右列 -->
|
||||
<view class="waterfall-column">
|
||||
<view v-for="shop in rightGoodsList" :key="shop.merchId" class="shop-card"
|
||||
@click="Service.GoPage('/pages/community/merchantDetail?merchId='+shop.merchId)">
|
||||
<view class="shop-cover-wrapper">
|
||||
<image class="shop-cover" :src="Service.GetMateUrlByImg(shop.logo)" mode="aspectFill" />
|
||||
<!-- 浮动标签 - 左下角距离 -->
|
||||
<view class="float-tag float-distance">
|
||||
<up-icon name="map" color="#fff" size="12"></up-icon>
|
||||
<text class="tag-text">{{formatDistance(shop.distance)}}</text>
|
||||
</view>
|
||||
<!-- 右上角商家标签 -->
|
||||
<view class="shop-badges">
|
||||
<text v-if="shop.code=='Discounts'" class="badge-partner">联盟商家</text>
|
||||
<text v-else class="badge-coop">合作商家</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="shop-info">
|
||||
<view class="shop-header">
|
||||
<text class="shop-name">{{ shop.name }}</text>
|
||||
</view>
|
||||
|
||||
<view class="shop-meta">
|
||||
<text class="shop-sales">月售:{{ shop.sale }}</text>
|
||||
<text class="shop-avg-price">人均{{ shop.price }}</text>
|
||||
</view>
|
||||
|
||||
<view class="shop-footer">
|
||||
<view class="shop-tags">
|
||||
<view v-for="(coupon, idx) in shop.tips" :key="idx" :class="getTagClass(coupon)"
|
||||
style="font-size: 22rpx;" class="shop-tag">
|
||||
{{ coupon }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<up-loadmore :status="status" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
onLoad, onReachBottom, onShareAppMessage, onShareTimeline, onShow
|
||||
} from '@dcloudio/uni-app'
|
||||
import {
|
||||
ref,
|
||||
onMounted,
|
||||
computed
|
||||
} from 'vue'
|
||||
import SkeletonCategory from '../../components/skeleton/skeleton-category.vue'
|
||||
import SkeletonShopCard from '../../components/skeleton/skeleton-shop-card.vue'
|
||||
import { vpLoginService, Service } from '@/Service/vp/vpLoginService'
|
||||
import { vpMerchService } from '@/Service/vp/vpMerchService'
|
||||
import { vpUserService } from '@/Service/vp/vpUserService'
|
||||
|
||||
// 数据
|
||||
let location = ref('')
|
||||
const categories = ref([])
|
||||
const shops = ref([])
|
||||
const currentCategory = ref('all')
|
||||
let status = ref('nomore')
|
||||
|
||||
let loding = ref(true)
|
||||
|
||||
let longitude = ref(0)
|
||||
let latitude = ref(0)
|
||||
|
||||
let searchKey = ref('')
|
||||
|
||||
let leftGoodsList = ref<Array<any>>([])
|
||||
let rightGoodsList = ref<Array<any>>([])
|
||||
let tabList = ref<Array<any>>([])
|
||||
let page = ref(1)
|
||||
let scene=ref('')
|
||||
onLoad((data:any) => {
|
||||
if(data.scene){
|
||||
scene.value=data.scene
|
||||
}
|
||||
if (data.q) {
|
||||
scene.value = decodeURIComponent(data.q).split('?')[1].split('=')[1]
|
||||
}
|
||||
getLocation()
|
||||
})
|
||||
|
||||
onShow(()=>{
|
||||
|
||||
})
|
||||
|
||||
// 分享
|
||||
onShareAppMessage((res) => {
|
||||
return {
|
||||
path: '/pages/index/index'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
onShareTimeline(() => {
|
||||
return {
|
||||
path: '/pages/index/index'
|
||||
}
|
||||
})
|
||||
|
||||
onReachBottom(()=>{
|
||||
getList()
|
||||
})
|
||||
|
||||
const getLocation = () => {
|
||||
|
||||
uni.getLocation({
|
||||
isHighAccuracy:true,
|
||||
type: 'gcj02',
|
||||
success: function (res) {
|
||||
longitude.value = res.longitude
|
||||
latitude.value = res.latitude
|
||||
getAddress()
|
||||
if (!Service.GetUserToken()) {
|
||||
login()
|
||||
return
|
||||
}
|
||||
getdata()
|
||||
},
|
||||
fail: function (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const login = () => {
|
||||
uni.getProvider({
|
||||
service: 'oauth',
|
||||
success: function (res : any) {
|
||||
uni.login({
|
||||
onlyAuthorize: true,
|
||||
provider: res.provider,
|
||||
success: function (loginRes) {
|
||||
vpLoginService.WxLogin(loginRes.code, res.provider == 'weixin' ? 1 : 3, longitude.value, latitude.value, scene.value).then(content => {
|
||||
if (content.code == 0) {
|
||||
Service.SetUserToken(content.data.accToken)
|
||||
getdata()
|
||||
} else {
|
||||
Service.Msg(content.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const getdata = () => {
|
||||
leftGoodsList.value = []
|
||||
rightGoodsList.value = []
|
||||
status.value = 'loadmore'
|
||||
page.value = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList = () => {
|
||||
|
||||
if (status.value !== 'loadmore') {
|
||||
return
|
||||
}
|
||||
status.value = 'loading'
|
||||
|
||||
|
||||
vpMerchService.GetMerchList('', '', longitude.value, latitude.value,0,0, page.value).then(res => {
|
||||
loding.value = false
|
||||
res.data.merchList.forEach((goodsItem : any, GoodsIndex : number) => {
|
||||
if (GoodsIndex % 2 == 0) {
|
||||
leftGoodsList.value.push(goodsItem)
|
||||
} else {
|
||||
rightGoodsList.value.push(goodsItem)
|
||||
}
|
||||
})
|
||||
tabList.value = res.data.assortList
|
||||
status.value = res.data.merchList.length == 10 ? 'loadmore' : 'nomore'
|
||||
page.value++
|
||||
})
|
||||
}
|
||||
|
||||
const getAddress = () => {
|
||||
vpUserService.GetAddressInfo(longitude.value, latitude.value).then(res => {
|
||||
if (res.code == 0) {
|
||||
location.value = res.data.addrName
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const choooseLocation = () => {
|
||||
uni.chooseLocation({
|
||||
success: function (res) {
|
||||
longitude.value = res.longitude
|
||||
latitude.value = res.latitude
|
||||
location.value = res.address
|
||||
getdata()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 格式化距离
|
||||
const formatDistance = (distance : any) => {
|
||||
if (distance < 1) {
|
||||
return `${Number(distance*1000).toFixed(1)}m`
|
||||
}
|
||||
return `${(distance).toFixed(1)}km`
|
||||
}
|
||||
|
||||
// 根据标签文本获取样式类
|
||||
const getTagClass = (tagText : string) => {
|
||||
const tagMap = {
|
||||
'可用积分': 'tag-points-available',
|
||||
'可用券': 'tag-coupon'
|
||||
}
|
||||
return tagMap[tagText] || 'tag-points'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.index-page {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
/* 状态栏占位 - 沉浸式 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 固定顶部区域 - 始终固定 */
|
||||
.top-fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.status-bar-placeholder {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.top-section-fixed {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
padding: 65rpx 20rpx 28rpx;
|
||||
}
|
||||
|
||||
/* 占位区域 - 抵消固定顶部的高度 */
|
||||
.top-placeholder {
|
||||
height: calc(var(--status-bar-height) + 170rpx);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.location-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.location-icon-small {
|
||||
font-size: 28rpx;
|
||||
color: #FFFFFF;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.location-text {
|
||||
font-size: 30rpx;
|
||||
color: #FFFFFF;
|
||||
font-weight: 500;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
font-size: 24rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* 搜索区域 */
|
||||
.search-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
/* 搜索框 */
|
||||
.search-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #FFFFFF;
|
||||
border-radius: 32rpx;
|
||||
padding: 8rpx 28rpx;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #222222;
|
||||
height: 48rpx;
|
||||
line-height: 48rpx;
|
||||
}
|
||||
|
||||
.search-placeholder {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
background: #FF6B00;
|
||||
color: #FFFFFF;
|
||||
font-size: 24rpx;
|
||||
padding: 6rpx 18rpx;
|
||||
border-radius: 20rpx;
|
||||
font-weight: 500;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 分类导航 - 三行五列 */
|
||||
.category-section {
|
||||
background: #FFFFFF;
|
||||
padding: 32rpx 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.category-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 32rpx 20rpx;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.category-icon-box {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.category-icon-box text {
|
||||
font-size: 44rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 24rpx;
|
||||
color: #222222;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 商家列表 - 瀑布流布局 */
|
||||
.shop-list {
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.shop-waterfall {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.waterfall-column {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.shop-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
.shop-cover-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
}
|
||||
|
||||
.shop-cover {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 浮动标签 */
|
||||
.float-tag {
|
||||
position: absolute;
|
||||
padding: 2rpx 6rpx;
|
||||
border-radius: 3rpx;
|
||||
font-size: 16rpx;
|
||||
backdrop-filter: blur(10rpx);
|
||||
-webkit-backdrop-filter: blur(10rpx);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2rpx;
|
||||
}
|
||||
|
||||
.float-distance {
|
||||
left: 6rpx;
|
||||
bottom: 6rpx;
|
||||
background: rgba(0, 0, 0, 0.65);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.float-distance .tag-icon {
|
||||
font-size: 14rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.float-distance .tag-text {
|
||||
font-size: 16rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
padding: 16rpx;
|
||||
}
|
||||
|
||||
// 右上角
|
||||
.shop-badges {
|
||||
position: absolute;
|
||||
top: 16rpx;
|
||||
right: 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
|
||||
/* 商家信息 */
|
||||
.shop-header {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #222222;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.shop-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.shop-sales {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.shop-avg-price {
|
||||
font-size: 26rpx;
|
||||
color: #FF6B00;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 商家标签 */
|
||||
.shop-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4rpx;
|
||||
}
|
||||
|
||||
/* 标签 */
|
||||
.tag-badge {
|
||||
display: inline-block;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 6rpx;
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
margin-right: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tag-coupon {
|
||||
background: #FFF8E1;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
.shop-price {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #FF4D4F;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.shop-original {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty {
|
||||
grid-column: 1 / -1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 120rpx;
|
||||
color: #CCCCCC;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
/* 底部导航栏 - 小巧精致风格 */
|
||||
.tabbar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #FFFFFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
padding: 12rpx 0 calc(12rpx + env(safe-area-inset-bottom));
|
||||
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
z-index: 999;
|
||||
height: calc(90rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.tabbar-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10rpx 0;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tabbar-icon {
|
||||
font-size: 44rpx;
|
||||
color: #CCCCCC;
|
||||
margin-bottom: 8rpx;
|
||||
transition: all 0.3s ease;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.tabbar-text {
|
||||
font-size: 22rpx;
|
||||
color: #CCCCCC;
|
||||
font-weight: 400;
|
||||
transition: all 0.3s ease;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* 激活状态 */
|
||||
.tabbar-item.active .tabbar-icon {
|
||||
color: #FF6B00;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.tabbar-item.active .tabbar-text {
|
||||
color: #FF6B00;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 首页头部骨架屏样式 */
|
||||
.location-box-skeleton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.skeleton-location-icon {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
border-radius: 4rpx;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.3) 25%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.3) 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading-white 1.5s ease-in-out infinite;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.skeleton-location-text {
|
||||
width: 200rpx;
|
||||
height: 28rpx;
|
||||
border-radius: 4rpx;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.3) 25%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.3) 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading-white 1.5s ease-in-out infinite;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.skeleton-arrow-icon {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
border-radius: 4rpx;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.3) 25%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.3) 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading-white 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.search-box-skeleton {
|
||||
width: 100%;
|
||||
height: 60rpx;
|
||||
border-radius: 32rpx;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.3) 25%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.3) 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading-white 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes loading-white {
|
||||
0% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
.page-loading {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,225 @@
|
||||
## 1.9.6.6(2024-09-25)
|
||||
- fix: 修复background-position无效的问题
|
||||
## 1.9.6.5(2024-04-14)
|
||||
- fix: 修复`nvue`无法生图的问题
|
||||
## 1.9.6.4(2024-03-10)
|
||||
- fix: 修复代理ctx导致H5不能使用ctx.save
|
||||
## 1.9.6.3(2024-03-08)
|
||||
- fix: 修复支付宝真机无法使用的问题
|
||||
## 1.9.6.2(2024-02-22)
|
||||
- fix: 修复使用render函数报错的问题
|
||||
## 1.9.6.1(2023-12-22)
|
||||
- fix: 修复字节小程序非2d字体偏移
|
||||
- fix: 修复`canvasToTempFilePathSync`会触发两次的问题
|
||||
- fix: 修复`parser`图片没有宽度的问题
|
||||
## 1.9.6(2023-12-06)
|
||||
- fix: 修复背景图受padding影响
|
||||
- fix: 修复因字节报错改了代理实现导致微信报错
|
||||
- 1.9.5.8(2023-11-16)
|
||||
- fix: 修复margin问题
|
||||
- fix: 修复borderWidth问题
|
||||
- fix: 修复textBox问题
|
||||
- fix: 修复字节开发工具报`could not be cloned.`问题
|
||||
## 1.9.5.7(2023-07-27)
|
||||
- fix: 去掉多余的方法
|
||||
- chore: 更新文档,增加自定义字体说明
|
||||
## 1.9.5.6(2023-07-21)
|
||||
- feat: 有限的支持富文本
|
||||
- feat: H5和APP 增加 `hidpi` prop,主要用于大尺寸无法生成图片时用
|
||||
- fix: 修复 钉钉小程序 缺少 `measureText` 方法
|
||||
- chore: 由于微信小程序 pc 端的 canvas 2d 时不时抽风,故不使用canvas 2d
|
||||
## 1.9.5.5(2023-06-27)
|
||||
- fix: 修复把`emoji`表情字符拆分成多个字符的情况
|
||||
## 1.9.5.4(2023-06-05)
|
||||
- fix: 修复因`canvasToTempFilePathSync`监听导致重复调用
|
||||
## 1.9.5.3(2023-05-23)
|
||||
- fix: 因isPc错写成了isPC导致小程序PC不能生成图片
|
||||
## 1.9.5.2(2023-05-22)
|
||||
- feat: 删除多余文件
|
||||
## 1.9.5.1(2023-05-22)
|
||||
- fix: 修复 文字行数与`line-clamp`相同但不满一行时也加了省略号的问题
|
||||
## 1.9.5(2023-05-14)
|
||||
- feat: 增加 `text-indent` 和 `calc` 方法
|
||||
- feat: 优化 布局时间
|
||||
## 1.9.4.4(2023-04-15)
|
||||
- fix: 修复无法匹配负值
|
||||
- fix: 修复 Nvue IOS getImageInfo `useCORS` 为 undefined
|
||||
## 1.9.4.3(2023-04-01)
|
||||
- feat: 增加支持文字描边 `text-stroke: '5rpx #fff'`
|
||||
## 1.9.4.2(2023-03-30)
|
||||
- fix: 修复 支付宝小程序 isPC 在手机也为true的问题
|
||||
- feat: 由 微信开发工具 3060 版 无法获取图片尺寸,现 微信开发工具 3220 版 修复该问题,故还原上一版的获取图片方式。
|
||||
## 1.9.4.1(2023-03-28)
|
||||
- fix: 修复固定高度不正确问题
|
||||
## 1.9.4(2023-03-17)
|
||||
- fix: nvue ios getImageInfo缺少this报错
|
||||
- fix: pathType 非2d无效问题
|
||||
- fix: 修复 小米9se 可能会存在多次init 导致画面多次放大
|
||||
- fix: 修复 border 分开写 width style无效问题
|
||||
- fix: 修复 支付宝小程序IOS 再次进入不渲染的问题
|
||||
- fix: 修复 支付宝小程序安卓Zindex排序错乱问题
|
||||
- fix: 修复 微信开发工具 3060 版 无法获取图片的问题
|
||||
- feat: 把 for in 改为 forEach
|
||||
- feat: 增加 hidden
|
||||
- feat: 根节点 box-sizing 默认 `border-box`
|
||||
- feat: 增加支持 `vw` `wh`
|
||||
- chore: pathType 取消 默认值,因为字节开发工具不能显示
|
||||
- chore: 支付宝小程序开发工具不支持 生成图片 请以真机调试为准
|
||||
- bug: 企业微信 2.20.3无法使用
|
||||
## 1.9.3.5(2022-06-29)
|
||||
- feat: justifyContent 增加 `space-around`、`space-between`
|
||||
- feat: canvas 2d 也使用`getImageInfo`
|
||||
- fix: 修复 `text`的 `text-decoration`错位
|
||||
## 1.9.3.4(2022-06-20)
|
||||
- fix: 修复 因创建节点速度问题导致顺序出错。
|
||||
- fix: 修复 微信小程序 PC 无法显示本地图片
|
||||
- fix: 修复 flex-box 对齐问题
|
||||
- feat: 增加 `text-shadow`
|
||||
- feat: 重写 `text` 对齐方式
|
||||
- chore: 更新文档
|
||||
## 1.9.3.3(2022-06-17)
|
||||
- fix: 修复 支付宝小程序 canvas 2d 存在ctx.draw问题导致报错
|
||||
- fix: 修复 支付宝小程序 toDataURL 存在权限问题改用 `toTempFilePath`
|
||||
- fix: 修复 支付宝小程序 image size 问题导致 `objectFit` 无效
|
||||
## 1.9.3.2(2022-06-14)
|
||||
- fix: 修复 image 设置背景色不生效问题
|
||||
- fix: 修复 nvue 环境判断缺少参数问题
|
||||
## 1.9.3.1(2022-06-14)
|
||||
- fix: 修复 bottom 定位不对问题
|
||||
- fix: 修复 因小数导致计算出错换行问题
|
||||
- feat: 增加 `useCORS` h5端图片跨域 在设置请求头无效果后试一下设置这个值
|
||||
- chore: 更新文档
|
||||
## 1.9.3(2022-06-13)
|
||||
- feat: 增加 `zIndex`
|
||||
- feat: 增加 `flex-box` 该功能处于原始阶段,非常简陋。
|
||||
- tips: QQ小程序 vue3 不支持, 为 uni 官方BUG
|
||||
## 1.9.2.9(2022-06-10)
|
||||
- fix: 修复`text-align`及`margin`居中问题
|
||||
## 1.9.2.8(2022-06-10)
|
||||
- fix: 修复 Nvue `canvasToTempFilePathSync` 不生效问题
|
||||
## 1.9.2.7(2022-06-10)
|
||||
- fix: 修复 margin及padding的bug
|
||||
- fix: 修复 Nvue `isCanvasToTempFilePath` 不生效问题
|
||||
## 1.9.2.6(2022-06-09)
|
||||
- fix: 修复 Nvue 不显示
|
||||
- feat: 增加支持字体渐变
|
||||
```html
|
||||
<l-painter-text
|
||||
text="水调歌头\n明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。"
|
||||
css="background: linear-gradient(,#ff971b 0%, #1989fa 100%); background-clip: text" />
|
||||
```
|
||||
## 1.9.2.5(2022-06-09)
|
||||
- chore: 更变获取父级宽度的设定
|
||||
- chore: `pathType` 在canvas 2d 默认为 `url`
|
||||
## 1.9.2.4(2022-06-08)
|
||||
- fix: 修复 `pathType` 不生效问题
|
||||
## 1.9.2.3(2022-06-08)
|
||||
- fix: 修复 `canvasToTempFilePath` 漏写 `success` 参数
|
||||
## 1.9.2.2(2022-06-07)
|
||||
- chore: 更新文档
|
||||
## 1.9.2.1(2022-06-07)
|
||||
- fix: 修复 vue3 赋值给this再传入导致image无法绘制
|
||||
- fix: 修复 `canvasToTempFilePathSync` 时机问题
|
||||
- feat: canvas 2d 更改图片生成方式 `toDataURL`
|
||||
## 1.9.2(2022-05-30)
|
||||
- fix: 修复 `canvasToTempFilePathSync` 在 vue3 下只生成一次
|
||||
## 1.9.1.7(2022-05-28)
|
||||
- fix: 修复 `qrcode`显示不全问题
|
||||
## 1.9.1.6(2022-05-28)
|
||||
- fix: 修复 `canvasToTempFilePathSync` 会重复多次问题
|
||||
- fix: 修复 `view` css `backgroundImage` 图片下载失败导致 子节点不渲染
|
||||
## 1.9.1.5(2022-05-27)
|
||||
- fix: 修正支付宝小程序 canvas 2d版本号 2.7.15
|
||||
## 1.9.1.4(2022-05-22)
|
||||
- fix: 修复字节小程序无法使用xml方式
|
||||
- fix: 修复字节小程序无法使用base64(非2D情况下工具上无法显示)
|
||||
- fix: 修复支付宝小程序 `canvasToTempFilePath` 报错
|
||||
## 1.9.1.3(2022-04-29)
|
||||
- fix: 修复vue3打包后uni对象为空后的报错
|
||||
## 1.9.1.2(2022-04-25)
|
||||
- fix: 删除多余文件
|
||||
## 1.9.1.1(2022-04-25)
|
||||
- fix: 修复图片不显示问题
|
||||
## 1.9.1(2022-04-12)
|
||||
- fix: 因四舍五入导致有些机型错位
|
||||
- fix: 修复无views报错
|
||||
- chore: nvue下因ios无法读取插件内static文件,改由下载方式
|
||||
## 1.9.0(2022-03-20)
|
||||
- fix: 因无法固定尺寸导致生成图片不全
|
||||
- fix: 特定情况下text判断无效
|
||||
- chore: 本地化APP Nvue webview
|
||||
## 1.8.9(2022-02-20)
|
||||
- fix: 修复 小程序下载最多10次并发的问题
|
||||
- fix: 修复 APP端无法获取本地图片
|
||||
- fix: 修复 APP Nvue端不执行问题
|
||||
- chore: 增加图片缓存机制
|
||||
## 1.8.8.8(2022-01-27)
|
||||
- fix: 修复 主动调用尺寸问题
|
||||
## 1.8.8.6(2022-01-26)
|
||||
- fix: 修复 nvue 下无宽度时获取父级宽度
|
||||
- fix: 修复 ios app 无法渲染问题
|
||||
## 1.8.8(2022-01-23)
|
||||
- fix: 修复 主动调用时无节点问题
|
||||
- fix: 修复 `box-shadow` 颜色问题
|
||||
- fix: 修复 `transform:rotate` 角度位置问题
|
||||
- feat: 增加 `overflow:hidden`
|
||||
## 1.8.7(2022-01-07)
|
||||
- fix: 修复 image 方向为 `right` 时原始宽高问题
|
||||
- feat: 支持 view 设置背景图 `background-image: url(xxx)`
|
||||
- chore: 去掉可选链
|
||||
## 1.8.6(2021-11-28)
|
||||
- feat: 支持`view`对`inline-block`的子集使用`text-align`
|
||||
## 1.8.5.5(2021-08-17)
|
||||
- chore: 更新文档,删除 replace
|
||||
- fix: 修复 text 值为 number时报错
|
||||
## 1.8.5.4(2021-08-16)
|
||||
- fix: 字节小程序兼容
|
||||
## 1.8.5.3(2021-08-15)
|
||||
- fix: 修复线性渐变与css现实效果不一致的问题
|
||||
- chore: 更新文档
|
||||
## 1.8.5.2(2021-08-13)
|
||||
- chore: 增加`background-image`、`background-repeat` 能力,主要用于背景纹理的绘制,并不是代替`image`。例如:大面积的重复平铺的水印
|
||||
- 注意:这个功能H5暂时无法使用,因为[官方的API有BUG](https://ask.dcloud.net.cn/question/128793),待官方修复!!!
|
||||
## 1.8.5.1(2021-08-10)
|
||||
- fix: 修复因`margin`报错问题
|
||||
## 1.8.5(2021-08-09)
|
||||
- chore: 增加margin支持`auto`,以达到居中效果
|
||||
## 1.8.4(2021-08-06)
|
||||
- chore: 增加判断缓存文件条件
|
||||
- fix: 修复css 多余空格报错问题
|
||||
## 1.8.3(2021-08-04)
|
||||
- tips: 1.6.x 以下的版本升级到1.8.x后要为每个元素都加上定位:position: 'absolute'
|
||||
- fix: 修复只有一个view子元素时不计算高度的问题
|
||||
## 1.8.2(2021-08-03)
|
||||
- fix: 修复 path-type 为 `url` 无效问题
|
||||
- fix: 修复 qrcode `text` 为空时报错问题
|
||||
- fix: 修复 image `src` 动态设置时不生效问题
|
||||
- feat: 增加 css 属性 `min-width` `max-width`
|
||||
## 1.8.1(2021-08-02)
|
||||
- fix: 修复无法加载本地图片
|
||||
## 1.8.0(2021-08-02)
|
||||
- chore 文档更新
|
||||
- 使用旧版的同学不要升级!
|
||||
## 1.8.0-beta(2021-07-30)
|
||||
- ## 全新布局方式 不兼容旧版!
|
||||
- chore: 布局方式变更
|
||||
- tips: 微信canvas 2d 不支持真机调试
|
||||
## 1.6.6(2021-07-09)
|
||||
- chore: 统一命名规范,无须主动引入组件
|
||||
## 1.6.5(2021-06-08)
|
||||
- chore: 去掉console
|
||||
## 1.6.4(2021-06-07)
|
||||
- fix: 修复 数字 为纯字符串时不转换的BUG
|
||||
## 1.6.3(2021-06-06)
|
||||
- fix: 修复 PC 端放大的BUG
|
||||
## 1.6.2(2021-05-31)
|
||||
- fix: 修复 报`adaptor is not a function`错误
|
||||
- fix: 修复 text 多行高度
|
||||
- fix: 优化 默认文字的基准线
|
||||
- feat: `@progress`事件,监听绘制进度
|
||||
## 1.6.1(2021-02-28)
|
||||
- 删除多余节点
|
||||
## 1.6.0(2021-02-26)
|
||||
- 调整为uni_modules目录规范
|
||||
- 修复:transform的rotate不能为负数问题
|
||||
- 新增:`pathType` 指定生成图片返回的路径类型,可选值有 `base64`、`url`
|
||||
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* 美团风格主题 - 参考美团外卖
|
||||
*/
|
||||
|
||||
/* 主色调 - 美团黄色 */
|
||||
$primary-color: #FFCC00; // 美团黄
|
||||
$primary-light: #FFD700; // 亮黄色
|
||||
$primary-dark: #F5A623; // 深黄色
|
||||
|
||||
/* 辅助色 */
|
||||
$secondary-bg: #FFF8E1; // 浅黄背景
|
||||
$accent-color: #FF6B00; // 橙红色
|
||||
|
||||
/* 中性色 */
|
||||
$text-primary: #0F172A; // 深色文字
|
||||
$text-secondary: #64748B; // 次要文字
|
||||
$text-tertiary: #94A3B8; // 辅助文字
|
||||
$border-color: #E2E8F0; // 边框色
|
||||
$bg-color: #F8FAFC; // 背景色
|
||||
|
||||
/* 功能色 */
|
||||
$success: #10B981; // 绿色
|
||||
$warning: #F59E0B; // 橙色
|
||||
$error: #EF4444; // 红色
|
||||
$info: #3B82F6; // 蓝色
|
||||
|
||||
/* 间距 */
|
||||
$spacing-xs: 8rpx;
|
||||
$spacing-sm: 12rpx;
|
||||
$spacing-md: 16rpx;
|
||||
$spacing-lg: 20rpx;
|
||||
$spacing-xl: 24rpx;
|
||||
|
||||
/* 圆角 */
|
||||
$radius-sm: 6rpx;
|
||||
$radius-md: 10rpx;
|
||||
$radius-lg: 16rpx;
|
||||
$radius-round: 999rpx;
|
||||
|
||||
/* 阴影 */
|
||||
$shadow-sm: 0 1rpx 3rpx rgba(0, 0, 0, 0.05);
|
||||
$shadow-md: 0 4rpx 6rpx -1rpx rgba(0, 0, 0, 0.08);
|
||||
$shadow-lg: 0 10rpx 15rpx -3rpx rgba(0, 0, 0, 0.08);
|
||||
|
||||
/* 字体大小 */
|
||||
$font-xs: 22rpx;
|
||||
$font-sm: 24rpx;
|
||||
$font-md: 26rpx;
|
||||
$font-lg: 28rpx;
|
||||
$font-xl: 32rpx;
|
||||
$font-xxl: 36rpx;
|
||||
|
||||
/* 通用类 */
|
||||
.container {
|
||||
padding: $spacing-md;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex-between {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 卡片样式 */
|
||||
.card {
|
||||
background: #FFFFFF;
|
||||
border-radius: $radius-lg;
|
||||
padding: $spacing-lg;
|
||||
box-shadow: $shadow-sm;
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, $primary-color, $primary-dark);
|
||||
color: #FFFFFF;
|
||||
border-radius: $radius-md;
|
||||
padding: $spacing-md $spacing-xl;
|
||||
font-size: $font-md;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background: #FFFFFF;
|
||||
color: $primary-color;
|
||||
border: 1rpx solid $primary-color;
|
||||
border-radius: $radius-md;
|
||||
padding: $spacing-md $spacing-xl;
|
||||
font-size: $font-md;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 标签样式 - 现代风格 */
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4rpx 10rpx;
|
||||
border-radius: $radius-sm;
|
||||
font-size: $font-xs;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tag-point {
|
||||
background: linear-gradient(135deg, #F0F9FF, #E0F2FE);
|
||||
color: #0284C7;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.tag-usable {
|
||||
background: #F1F5F9;
|
||||
color: $text-secondary;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.tag-coupon {
|
||||
background: linear-gradient(135deg, #FAF5FF, #F3E8FF);
|
||||
color: #9333EA;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.tag-distance {
|
||||
background: transparent;
|
||||
color: $text-tertiary;
|
||||
}
|
||||
|
||||
/* 分割线 */
|
||||
.divider {
|
||||
height: 1rpx;
|
||||
background-color: $border-color;
|
||||
margin: $spacing-md 0;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: $spacing-xl 0;
|
||||
color: $text-tertiary;
|
||||
font-size: $font-md;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 120rpx;
|
||||
margin-bottom: $spacing-md;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/* 商家标签 - 右上角徽章 */
|
||||
.shop-badges {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.badge-partner {
|
||||
background: linear-gradient(135deg, #FF6B35, #F7931E);
|
||||
color: #FFFFFF;
|
||||
font-size: 9px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.badge-coop {
|
||||
background: linear-gradient(135deg, #667EEA, #764BA2);
|
||||
color: #FFFFFF;
|
||||
font-size: 9px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 商家优惠券标签 */
|
||||
.shop-tag {
|
||||
display: inline-block;
|
||||
padding: 2rpx 6rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 12rpx;
|
||||
font-weight: 500;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
/* 消费得积分标签 - 蓝紫色系 */
|
||||
.shop-tag.tag-points {
|
||||
background: linear-gradient(135deg, #E3F2FD, #BBDEFB);
|
||||
color: #1565C0;
|
||||
border: 1rpx solid #64B5F6;
|
||||
}
|
||||
|
||||
/* 可用积分标签 - 绿色系 */
|
||||
.shop-tag.tag-points-available {
|
||||
background: linear-gradient(135deg, #E8F5E9, #C8E6C9);
|
||||
color: #2E7D32;
|
||||
border: 1rpx solid #81C784;
|
||||
}
|
||||
|
||||
/* 可用券标签 - 橙红色系 */
|
||||
.shop-tag.tag-coupon {
|
||||
background: linear-gradient(135deg, #FFEBEE, #FFCDD2);
|
||||
color: #D32F2F;
|
||||
border: 1rpx solid #EF5350;
|
||||
}
|
||||
|
||||
/* 默认标签 - 灰色系 */
|
||||
.shop-tag.tag-default {
|
||||
background: linear-gradient(135deg, #F5F5F5, #E0E0E0);
|
||||
color: #616161;
|
||||
border: 1rpx solid #BDBDBD;
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<view class="points-mall-page">
|
||||
<!-- 全新方案:纯 CSS 手动构建的骨架屏 -->
|
||||
<view v-if="loading" class="skeleton-wrapper">
|
||||
<view class="skeleton-item skeleton-rect" style="height: 300rpx; margin: 24rpx; border-radius: 16rpx;"></view>
|
||||
<view class="skeleton-content">
|
||||
<view class="skeleton-left-panel">
|
||||
<view v-for="i in 6" :key="i" class="skeleton-item skeleton-text" style="width: 80%; height: 60rpx; margin-bottom: 50rpx;"></view>
|
||||
</view>
|
||||
<view class="skeleton-right-panel">
|
||||
<view v-for="i in 4" :key="i" class="skeleton-card">
|
||||
<view class="skeleton-item skeleton-rect" style="width: 160rpx; height: 160rpx;"></view>
|
||||
<view style="flex:1; margin-left: 24rpx;">
|
||||
<view class="skeleton-item skeleton-text" style="width: 90%; height: 30rpx;"></view>
|
||||
<view class="skeleton-item skeleton-text" style="width: 60%; height: 32rpx; margin-top: 40rpx;"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 页面实际内容 -->
|
||||
<view v-else class="page-content">
|
||||
<!-- 1. 顶部轮播图 (原生 <swiper> 实现) -->
|
||||
<view class="swiper-section">
|
||||
<swiper class="swiper-container" circular autoplay :interval="3000" :duration="500" @change="e => swiperCurrent = e.detail.current">
|
||||
<swiper-item v-for="(item, index) in swiperList" :key="index">
|
||||
<view class="swiper-item">
|
||||
<view class="image-placeholder swiper-image">
|
||||
<image :src="item" style="width: 100%; height: 100%;" mode=""></image>
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<!-- 自定义指示点 -->
|
||||
<view class="swiper-dots">
|
||||
<view class="dot" :class="{ active: index === swiperCurrent }" v-for="(item, index) in swiperList" :key="index"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 2. 主体内容:左右联动 -->
|
||||
<view class="main-content">
|
||||
<!-- 左侧分类栏 -->
|
||||
<scroll-view class="left-panel" scroll-y>
|
||||
<view
|
||||
class="category-item"
|
||||
:class="{ active: currentCategory === index }"
|
||||
v-for="(cat, index) in categories"
|
||||
:key="cat.id"
|
||||
@click="selectCategory(index)"
|
||||
>
|
||||
<text class="name">{{ cat.name }}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 右侧商品列表 -->
|
||||
<scroll-view class="right-panel" scroll-y>
|
||||
<view class="product-list">
|
||||
<view class="product-card" v-for="product in products" :key="product.id" @click="Service.GoPage('/pages/goods/integralGoods')">
|
||||
<view class="image-placeholder product-image">
|
||||
<image :src="product.img" style="width: 100%; height: 100%; border-radius: 20rpx;" mode=""></image>
|
||||
</view>
|
||||
<view class="product-info">
|
||||
<text class="name">{{ product.name }}</text>
|
||||
<view class="price-line">
|
||||
<text class="points">{{ product.points }} 积分</text>
|
||||
<text class="original-price" v-if="product.originalPrice">+ ¥{{ product.originalPrice }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<up-loadmore status="nomore" nomoreText="没有更多商品了"></up-loadmore>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import { Service } from "@/Service/Service";
|
||||
|
||||
const loading = ref<boolean>(true);
|
||||
const currentCategory = ref(0);
|
||||
const swiperCurrent = ref(0);
|
||||
|
||||
// 轮播图数据
|
||||
const swiperList = reactive([
|
||||
'/static/dele/dele1.jpg',
|
||||
'/static/dele/dele2.jpg',
|
||||
'/static/dele/dele3.png',
|
||||
]);
|
||||
|
||||
// 分类数据
|
||||
const categories = reactive([
|
||||
{ id: 1, name: '热门兑换' }, { id: 2, name: '生活家居' }, { id: 3, name: '数码家电' },
|
||||
{ id: 4, name: '美妆个护' }, { id: 5, name: '食品饮料' }, { id: 6, name: '虚拟卡券' },
|
||||
]);
|
||||
|
||||
// 商品数据
|
||||
const products = reactive([
|
||||
{ id: 101, name: '品牌充电宝 10000mAh 金属外壳 超薄便携', img:'/static/dele/dele1.jpg', points: 2000, originalPrice: 19.9 },
|
||||
{ id: 102, name: '声波震动电动牙刷 智能计时', img:'/static/dele/dele2.jpg', points: 5000 },
|
||||
{ id: 103, name: '知名视频网站月度会员卡 直充', img:'/static/dele/dele3.png', points: 1500, originalPrice: 5 },
|
||||
{ id: 104, name: '“天命打工人”限定版帆布袋', img:'/static/dele/dele4.jpg', points: 800 },
|
||||
]);
|
||||
|
||||
const selectCategory = (index: number) => {
|
||||
if (currentCategory.value === index) return;
|
||||
currentCategory.value = index;
|
||||
console.log(`切换到分类: ${categories[index].name}`);
|
||||
};
|
||||
|
||||
onLoad(() => {
|
||||
setTimeout(() => { loading.value = false; }, 1500);
|
||||
});
|
||||
onShow(() => {});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 骨架屏样式 */
|
||||
.skeleton-wrapper {
|
||||
background-color: #f7f7f7;
|
||||
.skeleton-content { display: flex; }
|
||||
.skeleton-left-panel { width: 180rpx; padding: 30rpx 20rpx; background: #f7f7f7; }
|
||||
.skeleton-right-panel { flex: 1; padding: 30rpx; background: #fff; }
|
||||
.skeleton-card { display: flex; align-items: center; gap: 24rpx; padding-bottom: 30rpx; margin-bottom: 30rpx; border-bottom: 1rpx solid #f0f0f0;}
|
||||
}
|
||||
|
||||
.points-mall-page {
|
||||
background-color: #f7f7f7; height: 100vh;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
|
||||
.swiper-section {
|
||||
padding: 24rpx;
|
||||
position: relative;
|
||||
.swiper-container {
|
||||
height: 300rpx;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
.swiper-item {
|
||||
width: 100%; height: 100%;
|
||||
.swiper-image { width: 100%; height: 100%; }
|
||||
}
|
||||
.swiper-dots {
|
||||
position: absolute;
|
||||
bottom: 40rpx; left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex; gap: 12rpx;
|
||||
.dot {
|
||||
width: 12rpx; height: 12rpx;
|
||||
border-radius: 50%; background-color: rgba(255, 255, 255, 0.5);
|
||||
transition: all 0.3s;
|
||||
&.active { width: 30rpx; background-color: #fff; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1; display: flex; overflow: hidden;
|
||||
}
|
||||
|
||||
.left-panel {
|
||||
width: 180rpx; background-color: #f7f7f7; height: 100%;
|
||||
.category-item {
|
||||
padding: 30rpx 20rpx; font-size: 28rpx; color: #666;
|
||||
text-align: center; position: relative;
|
||||
&.active {
|
||||
background-color: #fff; color: #333; font-weight: bold;
|
||||
&::before {
|
||||
content: ''; position: absolute; left: 0; top: 50%;
|
||||
transform: translateY(-50%); width: 8rpx; height: 40rpx;
|
||||
background-color: #fa6400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
flex: 1; background-color: #fff; height: 100%; padding: 30rpx;
|
||||
.product-list { display: flex; flex-direction: column; gap: 30rpx; }
|
||||
.product-card {
|
||||
display: flex; gap: 24rpx; align-items: center;
|
||||
border-bottom: 1rpx solid #e2e2e2;
|
||||
padding-bottom: 10rpx;
|
||||
.product-image {
|
||||
width: 160rpx; height: 160rpx;
|
||||
border-radius: 12rpx; flex-shrink: 0;
|
||||
}
|
||||
.product-info {
|
||||
flex: 1; min-width: 0;
|
||||
.name {
|
||||
font-size: 28rpx; color: #333; font-weight: 500;
|
||||
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
|
||||
overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
.price-line {
|
||||
margin-top: 20rpx; display: flex; align-items: baseline;
|
||||
.points { font-size: 32rpx; font-weight: bold; color: #fa6400; }
|
||||
.original-price { font-size: 22rpx; color: #999; margin-left: 8rpx; }
|
||||
}
|
||||
}
|
||||
.exchange-btn {
|
||||
background-color: #fa6400; color: #fff;
|
||||
border-radius: 30rpx; font-size: 24rpx;
|
||||
padding: 10rpx 24rpx; margin: 0;
|
||||
height: fit-content; line-height: 1.5;
|
||||
&::after { border: none; }
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,13 @@
|
||||
// import { createPinia, defineStore } from "pinia";
|
||||
// import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
|
||||
// export class StoreAssist{
|
||||
// private pinia:any=createPinia();
|
||||
// constructor() {
|
||||
// this.pinia.use(piniaPluginPersistedstate);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// }
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
|
||||
onLaunch(() => {
|
||||
console.log("App Launch");
|
||||
});
|
||||
onShow(() => {
|
||||
console.log("App Show");
|
||||
});
|
||||
onHide(() => {
|
||||
console.log("App Hide");
|
||||
});
|
||||
|
||||
const getUpData=()=>{
|
||||
// #ifdef APP
|
||||
// plus.runtime.getProperty(plus.runtime.appid, (wgtinfo) => {
|
||||
// NvpMerchService.GetAppVersion().then(res=>{
|
||||
// console.log('wgtinfo.versionCode',wgtinfo.versionCode);
|
||||
// if (res.data.version > wgtinfo.versionCode) {
|
||||
// setTimeout(function() {
|
||||
// uni.navigateTo({
|
||||
// url: "/pages/upData/upData?info=" +
|
||||
// encodeURIComponent(
|
||||
// JSON.stringify(res.data))
|
||||
// })
|
||||
// }, 1000)
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
// #endif
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
||||
@import "uview-plus/index.scss";
|
||||
@import "colorui/main.css";
|
||||
@import "colorui/icon.css";
|
||||
page {
|
||||
--nav-mian: #FF6B35; //全局颜色
|
||||
--nav-vice: #F59D77; //副颜色
|
||||
--nav-diluted: #F2C0A3; //淡颜色
|
||||
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,497 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
|
||||
|
||||
<!-- 商品图片展示 -->
|
||||
<view class="product-image-section">
|
||||
<image :src="Service.GetMateUrlByImg(merchInfo.showImg)" mode="aspectFill" class="product-image">
|
||||
</image>
|
||||
</view>
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<view class="product-info-section">
|
||||
<view class="shop-header">
|
||||
<view class="shop-name">{{merchInfo.name}}</view>
|
||||
<view class="" style="display: flex; align-items: center; gap: 10rpx;" >
|
||||
<text class="tag" style="background-color: #FF9500;" >{{ merchInfo.code=='Discounts'?'积分可用':'积分不可用'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="display: flex; align-items: center;">
|
||||
<view class="func-detail">
|
||||
<text style="color: #FF6B35;">{{merchInfo.score}}</text>分
|
||||
</view>
|
||||
<view class="func-detail">
|
||||
月销 <text style="color: #FF6B35;">{{merchInfo.sale}}</text>
|
||||
</view>
|
||||
<view class="func-detail">
|
||||
人均 <text style="color: #FF6B35;">¥{{merchInfo.price}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商家信息卡片 -->
|
||||
<view class="shop-card-section">
|
||||
|
||||
<view class="shop-info">
|
||||
<view class="" @click="handleViewLocation()"
|
||||
style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 20rpx; ">
|
||||
<view class="info-item">
|
||||
<u-icon name="map" size="22" color="#999999" class="info-icon"></u-icon>
|
||||
<text class="info-text">{{merchInfo.address}}</text>
|
||||
</view>
|
||||
<view class="">
|
||||
<u-icon name="arrow-right" size="18" color="#999999" class="info-icon"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" @click="handleContactShop()"
|
||||
style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 20rpx; ">
|
||||
<view class="info-item">
|
||||
<u-icon name="phone" size="22" color="#999999" class="info-icon"></u-icon>
|
||||
<text class="info-text">{{merchInfo.phone}}</text>
|
||||
</view>
|
||||
<view class="">
|
||||
<u-icon name="arrow-right" size="18" color="#999999" class="info-icon"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class=""
|
||||
style="display: flex; align-items: center; justify-content: space-between; ">
|
||||
<view class="info-item">
|
||||
<u-icon name="clock" size="22" color="#999999" class="info-icon"></u-icon>
|
||||
<text class="info-text">{{merchInfo.time}}</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
<!-- <view v-if="communityInfo" class="info-item">
|
||||
<u-icon name="home" size="22" color="#999999" class="info-icon"></u-icon>
|
||||
<text class="info-text">{{communityInfo.name}}</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 同店推荐 -->
|
||||
<view v-if="recommendations.length>0" class="recommendations-section">
|
||||
<text class="section-title">店内商品</text>
|
||||
<view class="recommendations-list">
|
||||
<view @click="Service.GoPage('/pages/goods/goodsDetail?goodsId='+item.goodsId)" class="recommendation-item"
|
||||
v-for="(item, index) in recommendations" :key="index">
|
||||
<image :src="Service.GetMateUrlByImg(item.img)" mode="aspectFill" class="recommendation-image"></image>
|
||||
<view class="recommendation-info">
|
||||
<text class="recommendation-name">{{ item.name }}</text>
|
||||
<text class="recommendation-price">{{ item.price }}</text>
|
||||
</view>
|
||||
<u-icon name="arrow-right" size="28rpx" color="#999999" class="arrow-icon"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<up-loadmore v-if="recommendations.length>0" :status="status" />
|
||||
<view class="" style="width: 100%; height: 150rpx;">
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Service } from "@/Service/Service"
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { vpMerchService } from '@/Service/vp/vpMerchService'
|
||||
|
||||
|
||||
// 商家数据类型
|
||||
interface shop{
|
||||
showImg : string
|
||||
price:number
|
||||
name:string
|
||||
score:number
|
||||
tag:string
|
||||
merchId:string
|
||||
address:string
|
||||
phone:string
|
||||
sale:number
|
||||
lat:string
|
||||
lon:string
|
||||
code:string
|
||||
time:string
|
||||
}
|
||||
|
||||
|
||||
// 推荐商品数据类型
|
||||
interface Recommendation {
|
||||
goodsId: string
|
||||
name : string
|
||||
price : number
|
||||
img : string
|
||||
}
|
||||
|
||||
let merchId = ref()
|
||||
let page = ref(0)
|
||||
let status = ref('loadmore')
|
||||
|
||||
// 营业时间处理
|
||||
let weekList = ref<Array<number>>([])
|
||||
let openTime = ref()
|
||||
let closeTime = ref()
|
||||
let timefunc = ref(-1)
|
||||
|
||||
|
||||
let merchInfo=ref<shop>({
|
||||
showImg : '',
|
||||
price:0,
|
||||
name:'',
|
||||
score: 0,
|
||||
tag: '',
|
||||
merchId: '',
|
||||
address: '',
|
||||
phone: '',
|
||||
sale: 0,
|
||||
lat: '',
|
||||
lon: '',
|
||||
code:'',
|
||||
time:''
|
||||
})
|
||||
|
||||
let communityInfo=ref()
|
||||
|
||||
|
||||
// 同店推荐商品数据
|
||||
const recommendations = ref<Array<Recommendation>>([{
|
||||
goodsId:'',
|
||||
name: '',
|
||||
price: 0,
|
||||
img: '',
|
||||
}]);
|
||||
|
||||
// 联系商家按钮样式
|
||||
const contactButtonStyle = ref({
|
||||
backgroundColor: '#FF6600',
|
||||
borderColor: '#FF6600',
|
||||
color: '#FFFFFF',
|
||||
fontSize: '28rpx',
|
||||
height: '75rpx',
|
||||
borderRadius: '45rpx',
|
||||
marginRight: '20rpx'
|
||||
});
|
||||
|
||||
// 查看位置按钮样式
|
||||
const locationButtonStyle = ref({
|
||||
backgroundColor: '#FF6600',
|
||||
borderColor: '#FF6600',
|
||||
color: '#FFFFFF',
|
||||
fontSize: '28rpx',
|
||||
height: '75rpx',
|
||||
borderRadius: '45rpx',
|
||||
marginLeft: '20rpx'
|
||||
});
|
||||
|
||||
|
||||
onLoad((data : any) => {
|
||||
merchId.value = data.merchId
|
||||
getData()
|
||||
})
|
||||
|
||||
const getData = () => {
|
||||
status.value = 'loadmore'
|
||||
page.value = 1
|
||||
recommendations.value=[]
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList = () => {
|
||||
if (status.value !== 'loadmore') {
|
||||
return
|
||||
}
|
||||
status.value = 'loading'
|
||||
|
||||
vpMerchService.GetMerchInfo(merchId.value,page.value).then(res=>{
|
||||
merchInfo.value=res.data.merchInfo
|
||||
communityInfo.value=res.data.communityInfo
|
||||
recommendations.value=res.data.merchGoods
|
||||
status.value = res.data.merchGoods.length == 10 ? 'loadmore' : 'nomore'
|
||||
merchInfo.value.time=!res.data.merchInfo.busTime?'': timeCancle(res.data.merchInfo.busTime)
|
||||
page.value++
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 处理联系商家
|
||||
const handleContactShop = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: merchInfo.value.phone, // 要拨打的电话号码
|
||||
success: function () {
|
||||
console.log('拨打电话成功');
|
||||
},
|
||||
fail: function (err) {
|
||||
console.error('拨打电话失败', err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 处理查看位置
|
||||
const handleViewLocation = () => {
|
||||
wx.openLocation({
|
||||
latitude: Number(merchInfo.value.lat),
|
||||
longitude: Number(merchInfo.value.lon),
|
||||
name: merchInfo.value.name,
|
||||
address: merchInfo.value.address,
|
||||
success: function (e) {
|
||||
console.log(e);
|
||||
},
|
||||
fail: function (e) {
|
||||
console.log(e);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
const timeCancle=(busTime:string)=>{
|
||||
let data= busTime.split('_')
|
||||
openTime.value=data[1].split('-')[0]
|
||||
closeTime.value=data[1].split('-')[1]
|
||||
let timeData=data[0].split('-')
|
||||
for(let i=0;i<timeData.length;i++){
|
||||
if(timeData[i]=='0'){
|
||||
timeData[i]='7'
|
||||
}
|
||||
}
|
||||
let time=''
|
||||
timeData.sort((a:any,b:any)=>{
|
||||
return a-b
|
||||
})
|
||||
timeData.map((item:any)=>{
|
||||
weekList.value.push(item=='7'?(Number(6)):Number(item-1))
|
||||
})
|
||||
let timeIndex = weekList.value[0] - 0
|
||||
let judgment = weekList.value.findIndex((item, index) => {
|
||||
return item - index !== timeIndex
|
||||
})
|
||||
|
||||
// 1是至 /0是全显示
|
||||
if (judgment == -1) {
|
||||
timefunc.value = 1
|
||||
} else {
|
||||
timefunc.value = 0
|
||||
}
|
||||
|
||||
if (timefunc.value == 0) {
|
||||
weekList.value.map((item) => {
|
||||
time = time + '周' + chinese(item) + ' '
|
||||
})
|
||||
} else {
|
||||
time = '周' + chinese((weekList.value[0])) + '至' + '周' + chinese((weekList.value[weekList.value.length - 1]))
|
||||
}
|
||||
|
||||
time=time+' '+data[1]
|
||||
|
||||
return time
|
||||
}
|
||||
|
||||
const chinese = (item : number) => {
|
||||
if (item + 1 == 1) {
|
||||
return '一'
|
||||
}
|
||||
if (item + 1 == 2) {
|
||||
return '二'
|
||||
} if (item + 1 == 3) {
|
||||
return '三'
|
||||
} if (item + 1 == 4) {
|
||||
return '四'
|
||||
} if (item + 1 == 5) {
|
||||
return '五'
|
||||
} if (item + 1 == 6) {
|
||||
return '六'
|
||||
} if (item + 1 == 7) {
|
||||
return '日'
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* 商品图片展示 */
|
||||
.product-image-section {
|
||||
width: 100%;
|
||||
height: 500rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.product-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 商家信息 */
|
||||
|
||||
.func-detail {
|
||||
margin-right: 30rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.product-info-section {
|
||||
padding: 30rpx;
|
||||
margin: 30rpx;
|
||||
box-shadow: 0 0 10rpx 4rpx #E2e2e2;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
font-size: 36rpx;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
font-size: 40rpx;
|
||||
color: #FF6600;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.product-description {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
/* 商家信息卡片 */
|
||||
.shop-card-section {
|
||||
padding: 30rpx;
|
||||
margin: 30rpx;
|
||||
box-shadow: 0 0 10rpx 4rpx #E2e2e2;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.shop-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 38rpx;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.shop-tags {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 24rpx;
|
||||
color: #FFFFFF;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 10rpx;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tag.new-shop {
|
||||
background-color: #4CD964;
|
||||
}
|
||||
|
||||
.tag.popular {
|
||||
background-color: #FF9500;
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
font-size: 28rpx;
|
||||
margin-left: 15rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
/* 同店推荐 */
|
||||
.recommendations-section {
|
||||
padding: 0 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 36rpx;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.recommendations-list {
|
||||
gap: 30rpx;
|
||||
}
|
||||
|
||||
.recommendation-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
margin: 20rpx 0;
|
||||
box-shadow: 0 0 10rpx 4rpx #E2e2e2;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.recommendation-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.recommendation-image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 16rpx;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.recommendation-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.recommendation-name {
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
display: block;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.recommendation-price {
|
||||
font-size: 30rpx;
|
||||
color: #FF6600;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
/* 底部操作按钮 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
padding: 30rpx 30rpx;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #ffffff;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,349 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
|
||||
|
||||
<!-- 商品图片展示 -->
|
||||
<view class="product-image-section">
|
||||
<image :src="Service.GetMateUrlByImg('/static/dele/dele4.jpg')" mode="aspectFill" class="product-image">
|
||||
</image>
|
||||
</view>
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<view class="product-info-section">
|
||||
<view class="shop-header">
|
||||
<view class="shop-name">美食小铺</view>
|
||||
<view class="shop-tags">
|
||||
<text class="tag new-shop">新店</text>
|
||||
<text class="tag popular">人气店</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="display: flex; align-items: center;">
|
||||
<view class="func-detail">
|
||||
<text style="color: #FF6B35;">4.7</text>分
|
||||
</view>
|
||||
<view class="func-detail">
|
||||
月销<text style="color: #FF6B35;">4.7</text>
|
||||
</view>
|
||||
<view class="func-detail">
|
||||
人均<text style="color: #FF6B35;">¥47</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商家信息卡片 -->
|
||||
<view class="shop-card-section">
|
||||
|
||||
<view class="shop-info">
|
||||
<view class="" @click="handleViewLocation()" style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<view class="info-item">
|
||||
<u-icon name="map" size="22" color="#999999" class="info-icon"></u-icon>
|
||||
<text class="info-text">朝阳区美食街123号</text>
|
||||
</view>
|
||||
<view class="">
|
||||
<u-icon name="arrow-right" size="18" color="#999999" class="info-icon"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" @click="handleContactShop()" style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<view class="info-item">
|
||||
<u-icon name="phone" size="22" color="#999999" class="info-icon"></u-icon>
|
||||
<text class="info-text">010-12345678</text>
|
||||
</view>
|
||||
<view class="">
|
||||
<u-icon name="arrow-right" size="18" color="#999999" class="info-icon"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<u-icon name="home" size="22" color="#999999" class="info-icon"></u-icon>
|
||||
<text class="info-text">朝阳美食社区</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 同店推荐 -->
|
||||
<view class="recommendations-section">
|
||||
<text class="section-title">店内商品</text>
|
||||
<view class="recommendations-list">
|
||||
<view @click="Service.GoPage('/pages/goods/goodsDetail')" class="recommendation-item"
|
||||
v-for="(item, index) in recommendations" :key="index">
|
||||
<image :src="item.image" mode="aspectFill" class="recommendation-image"></image>
|
||||
<view class="recommendation-info">
|
||||
<text class="recommendation-name">{{ item.name }}</text>
|
||||
<text class="recommendation-price">{{ item.price }}</text>
|
||||
</view>
|
||||
<u-icon name="arrow-right" size="28rpx" color="#999999" class="arrow-icon"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="" style="width: 100%; height: 150rpx;">
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Service } from "@/Service/Service"
|
||||
|
||||
// 推荐商品数据类型
|
||||
interface Recommendation {
|
||||
id : number;
|
||||
name : string;
|
||||
price : string;
|
||||
image : string;
|
||||
}
|
||||
|
||||
// 同店推荐商品数据
|
||||
const recommendations = ref<Recommendation[]>([
|
||||
{
|
||||
id: 1,
|
||||
name: '招牌卤肉饭',
|
||||
price: '¥26',
|
||||
image: '/static/dele/dele4.jpg'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '红烧牛肉面',
|
||||
price: '¥32',
|
||||
image: '/static/dele/dele4.jpg'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '香菇三明治',
|
||||
price: '¥28',
|
||||
image: '/static/dele/dele4.jpg'
|
||||
}
|
||||
]);
|
||||
|
||||
// 联系商家按钮样式
|
||||
const contactButtonStyle = ref({
|
||||
backgroundColor: '#FF6600',
|
||||
borderColor: '#FF6600',
|
||||
color: '#FFFFFF',
|
||||
fontSize: '28rpx',
|
||||
height: '75rpx',
|
||||
borderRadius: '45rpx',
|
||||
marginRight: '20rpx'
|
||||
});
|
||||
|
||||
// 查看位置按钮样式
|
||||
const locationButtonStyle = ref({
|
||||
backgroundColor: '#FF6600',
|
||||
borderColor: '#FF6600',
|
||||
color: '#FFFFFF',
|
||||
fontSize: '28rpx',
|
||||
height: '75rpx',
|
||||
borderRadius: '45rpx',
|
||||
marginLeft: '20rpx'
|
||||
});
|
||||
|
||||
// 处理联系商家
|
||||
const handleContactShop = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: '10086', // 要拨打的电话号码
|
||||
success: function () {
|
||||
console.log('拨打电话成功');
|
||||
},
|
||||
fail: function (err) {
|
||||
console.error('拨打电话失败', err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 处理查看位置
|
||||
const handleViewLocation = () => {
|
||||
wx.chooseLocation({
|
||||
success: res => {
|
||||
|
||||
// latitude.value = res.latitude.toString();
|
||||
// longitude.value = res.longitude.toString();
|
||||
}
|
||||
})
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* 商品图片展示 */
|
||||
.product-image-section {
|
||||
width: 100%;
|
||||
height: 500rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.product-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 商家信息 */
|
||||
|
||||
.func-detail {
|
||||
margin-right: 30rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.product-info-section {
|
||||
padding: 30rpx;
|
||||
margin: 30rpx;
|
||||
box-shadow: 0 0 10rpx 4rpx #E2e2e2;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
font-size: 36rpx;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
font-size: 40rpx;
|
||||
color: #FF6600;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.product-description {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
/* 商家信息卡片 */
|
||||
.shop-card-section {
|
||||
padding: 30rpx;
|
||||
margin: 30rpx;
|
||||
box-shadow: 0 0 10rpx 4rpx #E2e2e2;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.shop-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 38rpx;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.shop-tags {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 24rpx;
|
||||
color: #FFFFFF;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.tag.new-shop {
|
||||
background-color: #4CD964;
|
||||
}
|
||||
|
||||
.tag.popular {
|
||||
background-color: #FF9500;
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.info-text {
|
||||
font-size: 28rpx;
|
||||
margin-left: 15rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
/* 同店推荐 */
|
||||
.recommendations-section {
|
||||
padding: 0 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 36rpx;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.recommendations-list {
|
||||
gap: 30rpx;
|
||||
}
|
||||
|
||||
.recommendation-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
margin: 20rpx 0;
|
||||
box-shadow: 0 0 10rpx 4rpx #E2e2e2;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.recommendation-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.recommendation-image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 16rpx;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.recommendation-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.recommendation-name {
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
display: block;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.recommendation-price {
|
||||
font-size: 30rpx;
|
||||
color: #FF6600;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
/* 底部操作按钮 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
padding: 30rpx 30rpx;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #ffffff;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,457 @@
|
||||
<template>
|
||||
<view class="promotion-page" :style="showCode?'height: 100vh; overflow: hidden;':''">
|
||||
<!-- 沉浸式状态栏 -->
|
||||
<view class="status-bar"></view>
|
||||
|
||||
<!-- 顶部导航 -->
|
||||
<view class="nav-bar">
|
||||
<image class="back-icon" src="/static/icons/back.svg" @click="goBack" mode="aspectFit" />
|
||||
<text class="nav-title">我的推广</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<view class="stats-section">
|
||||
<view class="stats-card">
|
||||
<view @click="showCode=true" class="stat-item">
|
||||
<up-icon name="share" color="#fff" size="20"></up-icon>
|
||||
<text class="stat-label">推广码</text>
|
||||
</view>
|
||||
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-value">{{ list.length }}</text>
|
||||
<text class="stat-label">推广会员</text>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-value">{{ allAward }}</text>
|
||||
<text class="stat-label">获得积分</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 会员列表 -->
|
||||
<view class="content">
|
||||
<view class="list-header">
|
||||
<text class="header-title">推广会员列表</text>
|
||||
<text class="header-count">共{{ list.length }}人</text>
|
||||
</view>
|
||||
|
||||
<view v-if="list.length > 0" class="members-list">
|
||||
<view v-for="member in list" :key="member.id" class="member-card">
|
||||
<!-- 会员信息 -->
|
||||
|
||||
<view class="member-info">
|
||||
<image class="member-avatar" :src="Service.GetMateUrlByImg(member.headImg)" mode="aspectFill" />
|
||||
<view class="member-details">
|
||||
<view class="name-tag-row">
|
||||
<text class="member-name">{{ member.nick }}</text>
|
||||
</view>
|
||||
<view class="user-id-tag">
|
||||
<image :src="Service.GetIconImg('/static/iconMent/user/viptype.png')" style="width: 20rpx; height: 20rpx; margin-right: 10rpx;"></image>
|
||||
<text class="id-text">ID: {{ member.userNo }}</text>
|
||||
</view>
|
||||
<text class="register-time">注册时间:{{ Service.formatDate(new Date(member.addTime),1) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 积分奖励 -->
|
||||
<view class="points-info">
|
||||
<text class="points-label">奖励</text>
|
||||
<text class="points-value">+{{ member.award }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-else class="empty-state">
|
||||
<text class="ri-team-line empty-icon"></text>
|
||||
<text class="empty-text">暂无推广会员</text>
|
||||
<text class="empty-desc">邀请好友加入,获得积分奖励</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<up-popup :show="showCode" round='10' mode='center' @close="showCode=false" :closeable="true"
|
||||
:safeAreaInsetTop='true' :safeAreaInsetBottom='false' >
|
||||
<view class="" style=" margin: 0 auto; padding: 20rpx; background-color: #fff; " >
|
||||
<l-painter :board="poster" ref="posterFun">
|
||||
</l-painter>
|
||||
</view>
|
||||
<view class="" style="padding: 0 20rpx 20rpx;" >
|
||||
<up-button color='var(--nav-mian)' @click="save()" text="保存图片" ></up-button>
|
||||
</view>
|
||||
</up-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { onShow, onLoad, onReachBottom } from "@dcloudio/uni-app";
|
||||
import { vpUserService, Service } from "@/Service/vp/vpUserService"
|
||||
|
||||
let list = ref<Array<any>>([])
|
||||
let status = ref<string>('loadmore')
|
||||
let pageNo = ref<number>(1)
|
||||
|
||||
let allAward = ref<Number>(0)
|
||||
let showCode=ref(false)
|
||||
let code=ref('')
|
||||
|
||||
let url = ref<string>('')
|
||||
let imgurl = ref<string>('')
|
||||
const posterFun = ref(null)
|
||||
|
||||
const poster = ref<any>({
|
||||
css: {
|
||||
// 根节点若无尺寸,自动获取父级节点
|
||||
position: ' relative',
|
||||
borderRadius: '18rpx',
|
||||
overflow: 'hidden',
|
||||
width:'550rpx'
|
||||
},
|
||||
|
||||
views: [{
|
||||
type: 'image',
|
||||
src: 'https://vp.clouds.xypays.cn/poster/poster1.png',
|
||||
css: {
|
||||
width: '550rpx',
|
||||
margin: '0 auto',
|
||||
},
|
||||
mode: "widthFix"
|
||||
}, {
|
||||
type: 'qrcode',
|
||||
text: '',
|
||||
css: {
|
||||
width: '80rpx',
|
||||
height: '80rpx',
|
||||
position: 'absolute',
|
||||
top: '870rpx',
|
||||
left: '65rpx'
|
||||
},
|
||||
}],
|
||||
|
||||
|
||||
})
|
||||
|
||||
let picture = ref<string>('')
|
||||
|
||||
|
||||
|
||||
|
||||
onLoad(() => {
|
||||
getData()
|
||||
getCode()
|
||||
})
|
||||
|
||||
onReachBottom(() => {
|
||||
getList()
|
||||
});
|
||||
|
||||
|
||||
const getData = () => {
|
||||
list.value = []
|
||||
status.value = 'loadmore'
|
||||
pageNo.value = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
|
||||
const getList = () => {
|
||||
if (status.value == 'nomore' || status.value == 'loading') {
|
||||
return
|
||||
}
|
||||
|
||||
status.value = 'loading'
|
||||
|
||||
vpUserService.GetUseRemList(pageNo.value).then(res => {
|
||||
if (res.code == 0) {
|
||||
list.value = [...list.value, ...res.data.list]
|
||||
status.value = 10 == res.data.list.length ? 'loadmore' : 'nomore'
|
||||
allAward.value = res.data.allAward
|
||||
pageNo.value++
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getCode=()=>{
|
||||
vpUserService.GetShareEwm().then(res=>{
|
||||
if(res.code==0){
|
||||
url.value=res.data.url
|
||||
imgurl.value = res.data.bgUrl
|
||||
poster.value.views[1].text = url.value
|
||||
poster.value.views[0].src = imgurl.value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 返回
|
||||
const goBack = () => {
|
||||
Service.GoPageBack()
|
||||
}
|
||||
|
||||
const save=()=>{
|
||||
Service.LoadClose('开始下载')
|
||||
posterFun.value.canvasToTempFilePathSync({
|
||||
fileType: 'jpg',
|
||||
pathType:'url',
|
||||
quality: 1,
|
||||
success: (res) => {
|
||||
picture.value = res.tempFilePath
|
||||
saveImage()
|
||||
},
|
||||
fail(e) {
|
||||
Service.Msg('下载失败')
|
||||
console.log('???????????', e)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
const saveImage = () => {
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: picture.value,
|
||||
success(res) {
|
||||
Service.Msg('保存成功!')
|
||||
},
|
||||
fail: function (err) {
|
||||
console.log(err,'===')
|
||||
Service.Msg('保存失败')
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 引入全局标签样式 */
|
||||
@import '@/styles/member-tags.scss';
|
||||
|
||||
.promotion-page {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
/* 状态栏 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 60rpx 24rpx 20rpx 24rpx;
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.nav-placeholder {
|
||||
width: 48rpx;
|
||||
}
|
||||
|
||||
/* 统计卡片 */
|
||||
.stats-section {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.stats-card {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
box-shadow: 0 4rpx 16rpx rgba(255, 107, 0, 0.3);
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
color: #FFFFFF;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.stat-divider {
|
||||
width: 1rpx;
|
||||
height: 60rpx;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.content {
|
||||
padding: 0 20rpx 20rpx;
|
||||
}
|
||||
|
||||
/* 列表头部 */
|
||||
.list-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.header-count {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
/* 会员列表 */
|
||||
.members-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.member-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.member-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.member-avatar {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
border-radius: 44rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.member-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.member-name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.name-tag-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.member-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.user-member-tag {
|
||||
transform: scale(0.92);
|
||||
transform-origin: left center;
|
||||
}
|
||||
|
||||
.user-id-tag {
|
||||
margin-bottom: 8rpx;
|
||||
width: fit-content;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.register-time {
|
||||
font-size: 22rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
/* 积分信息 */
|
||||
.points-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6rpx;
|
||||
padding: 16rpx;
|
||||
background: #FFF4E6;
|
||||
border-radius: 12rpx;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.points-label {
|
||||
font-size: 20rpx;
|
||||
color: #FF9800;
|
||||
}
|
||||
|
||||
.points-value {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
padding: 120rpx 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 120rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.empty-desc {
|
||||
font-size: 24rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,583 @@
|
||||
<template>
|
||||
<view class="settings-page">
|
||||
<!-- 沉浸式状态栏 -->
|
||||
<view class="status-bar"></view>
|
||||
|
||||
<!-- 顶部导航 -->
|
||||
<view class="nav-bar">
|
||||
<image class="back-icon" src="/static/icons/back.svg" @click="goBack" mode="aspectFit" />
|
||||
<text class="nav-title">设置</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<view class="content">
|
||||
<!-- 个人信息卡片 -->
|
||||
<view class="profile-card">
|
||||
<view class="card-title">
|
||||
<text class="ri-user-line title-icon"></text>
|
||||
<text class="title-text">个人信息</text>
|
||||
</view>
|
||||
|
||||
<!-- 头像 -->
|
||||
<view class="setting-item" @click="uploadFImg(140,140,'Avatar','headimg')">
|
||||
<view class="item-left">
|
||||
<text class="item-label">头像</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<image class="avatar-preview" :src="Service.GetMateUrlByImg(userInfo.headImg)"
|
||||
mode="aspectFill" />
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 昵称 -->
|
||||
<view class="setting-item" @click="changeNickname">
|
||||
<view class="item-left">
|
||||
<text class="item-label">昵称</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-value">{{ userInfo.nick }}</text>
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 手机号 -->
|
||||
<view class="setting-item">
|
||||
<view class="item-left">
|
||||
<text class="item-label">手机号</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-value">{{ userInfo.phone }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 会员ID -->
|
||||
<view class="setting-item">
|
||||
<view class="item-left">
|
||||
<text class="item-label">会员ID</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-value">{{ userInfo.userNo }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 其他设置 -->
|
||||
<!-- <view class="other-settings-card">
|
||||
<view class="card-title">
|
||||
<text class="ri-settings-4-line title-icon"></text>
|
||||
<text class="title-text">其他设置</text>
|
||||
</view>
|
||||
|
||||
<view class="setting-item" @click="clearCache">
|
||||
<view class="item-left">
|
||||
<text class="item-label">清除缓存</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-value">23.5MB</text>
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="setting-item" @click="checkUpdate">
|
||||
<view class="item-left">
|
||||
<text class="item-label">检查更新</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-value">当前版本 v1.0.0</text>
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="setting-item" @click="viewPrivacy">
|
||||
<view class="item-left">
|
||||
<text class="item-label">隐私政策</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="setting-item" @click="viewAgreement">
|
||||
<view class="item-left">
|
||||
<text class="item-label">用户协议</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
<!-- <view class="logout-section">
|
||||
<button class="logout-btn" @click="save()">
|
||||
<text class="btn-text">保存信息</text>
|
||||
</button>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
<!-- 修改昵称弹窗 -->
|
||||
<view v-if="showNicknameModal" class="modal-overlay" @click="closeNicknameModal">
|
||||
<view class="modal-content" @click.stop>
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">修改昵称</text>
|
||||
<text class="ri-close-line modal-close" @click="closeNicknameModal"></text>
|
||||
</view>
|
||||
<view class="modal-body">
|
||||
<input class="nickname-input" v-model="tempNickname" placeholder="请输入新昵称" maxlength="20" />
|
||||
</view>
|
||||
<view class="modal-footer">
|
||||
<button class="modal-btn cancel" @click.stop="closeNicknameModal">取消</button>
|
||||
<button class="modal-btn confirm" @click.stop="saveNickname">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { Service } from '@/Service/Service';
|
||||
import { ref } from "vue";
|
||||
import { vpUserService } from '@/Service/vp/vpUserService';
|
||||
import ImageCropperFunc from "@/components/ImageCropper";
|
||||
|
||||
const userInfo = ref<any>({
|
||||
nick: '',
|
||||
sex: '',
|
||||
phone: '',
|
||||
headImg: ''
|
||||
})
|
||||
|
||||
// 临时昵称
|
||||
const tempNickname = ref('')
|
||||
|
||||
|
||||
|
||||
onLoad(() => {
|
||||
getUserinfo()
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
|
||||
|
||||
const getUserinfo = () => {
|
||||
vpUserService.GetUserInfo().then(res => {
|
||||
userInfo.value = res.data.userInfo
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const uploadFImg = (width : any, height : any, Type : any, Name : any) => {
|
||||
uni.chooseImage({
|
||||
count: 1, // 最多选择3张图片
|
||||
sizeType: ['original', 'compressed'], // 支持原图和压缩图
|
||||
sourceType: ['album', 'camera'], // 可从相册选择或使用相机拍照
|
||||
success: function (res) {
|
||||
let path = res.tempFiles[0].path
|
||||
let arr = path.split('.')
|
||||
let name = arr[arr.length - 1]
|
||||
Service.uploadH5(path, 'Avatar', (data) => {
|
||||
userInfo.value.headImg = data
|
||||
vpUserService.UpdateUser(userInfo.value.headImg, userInfo.value.nick, userInfo.value.sex, '').then(res => {
|
||||
if (res.code == 0) {
|
||||
Service.Msg('修改成功!')
|
||||
getUserinfo()
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
fail: function (err) {
|
||||
console.error('选择失败:', err.errMsg);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 修改昵称
|
||||
const changeNickname = () => {
|
||||
tempNickname.value = userInfo.value.nick
|
||||
showNicknameModal.value = true
|
||||
}
|
||||
|
||||
// 关闭昵称弹窗
|
||||
const closeNicknameModal = () => {
|
||||
showNicknameModal.value = false
|
||||
}
|
||||
|
||||
|
||||
// 保存昵称
|
||||
const saveNickname = () => {
|
||||
if (!tempNickname.value.trim()) {
|
||||
uni.showToast({
|
||||
title: '昵称不能为空',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
vpUserService.UpdateUser(userInfo.value.headImg, tempNickname.value, userInfo.value.sex, '').then(res => {
|
||||
if (res.code == 0) {
|
||||
Service.Msg('修改成功!')
|
||||
showNicknameModal.value = false
|
||||
userInfo.value.nick = tempNickname.value
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 用户信息
|
||||
const user = ref({
|
||||
nickname: '美食达人',
|
||||
avatar: 'https://picsum.photos/200/200?random=100',
|
||||
phone: '138****8888',
|
||||
memberId: '8888888'
|
||||
})
|
||||
|
||||
// 显示昵称弹窗
|
||||
const showNicknameModal = ref(false)
|
||||
|
||||
|
||||
|
||||
// 返回
|
||||
const goBack = () => {
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
})
|
||||
}
|
||||
|
||||
// 修改头像
|
||||
const changeAvatar = () => {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
// 这里应该上传到服务器,现在暂时直接使用本地路径
|
||||
user.value.avatar = res.tempFilePaths[0]
|
||||
uni.showToast({
|
||||
title: '头像已更新',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 清除缓存
|
||||
const clearCache = () => {
|
||||
uni.showModal({
|
||||
title: '清除缓存',
|
||||
content: '确定要清除缓存吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({
|
||||
title: '清除中...'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '缓存已清除',
|
||||
icon: 'success'
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 检查更新
|
||||
const checkUpdate = () => {
|
||||
uni.showLoading({
|
||||
title: '检查中...'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '已是最新版本',
|
||||
icon: 'success'
|
||||
})
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
// 查看隐私政策
|
||||
const viewPrivacy = () => {
|
||||
uni.showToast({
|
||||
title: '隐私政策页面',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 查看用户协议
|
||||
const viewAgreement = () => {
|
||||
uni.showToast({
|
||||
title: '用户协议页面',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
const logout = () => {
|
||||
uni.showModal({
|
||||
title: '退出登录',
|
||||
content: '确定要退出登录吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({
|
||||
title: '退出中...'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '已退出登录',
|
||||
icon: 'success'
|
||||
})
|
||||
// 这里可以跳转到登录页面
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.settings-page {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 状态栏 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 60rpx 24rpx 20rpx 24rpx;
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.nav-placeholder {
|
||||
width: 48rpx;
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 32rpx 24rpx;
|
||||
}
|
||||
|
||||
/* 卡片通用样式 */
|
||||
.profile-card,
|
||||
.other-settings-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 24rpx;
|
||||
padding-bottom: 16rpx;
|
||||
border-bottom: 2rpx solid #F5F5F5;
|
||||
}
|
||||
|
||||
.title-icon {
|
||||
font-size: 32rpx;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
/* 设置项 */
|
||||
.setting-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24rpx 0;
|
||||
border-bottom: 1rpx solid #F5F5F5;
|
||||
}
|
||||
|
||||
.setting-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
font-size: 28rpx;
|
||||
color: #222222;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 26rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.item-arrow {
|
||||
font-size: 28rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
.avatar-preview {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
}
|
||||
|
||||
/* 退出登录按钮 */
|
||||
.logout-section {
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
background: #FFFFFF;
|
||||
border: 2rpx solid #F44336;
|
||||
border-radius: 44rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
/* 弹窗样式 */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 560rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
font-size: 40rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.nickname-input {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
padding: 0 20rpx;
|
||||
background: #F5F5F5;
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.modal-btn {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.modal-btn.cancel {
|
||||
background: #F5F5F5;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.modal-btn.confirm {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<view style="margin: 30rpx 50rpx;">
|
||||
<view class="" style="font-size: 26rpx;">
|
||||
提现金额
|
||||
</view>
|
||||
<view class="" style="margin: 20rpx 0;">
|
||||
<up-input :customStyle="{'padding':'12rpx 0','height':'100rpx'}" fontSize='18'
|
||||
prefixIconStyle="font-size: 28px;color: #000;font-weight:bold" placeholder="请输入提现金额" border="bottom"
|
||||
prefixIcon="rmb"></up-input>
|
||||
</view>
|
||||
<view class="" style="font-size: 26rpx; display: flex; align-items: center; ">
|
||||
<view class="" style="color: #999; ">
|
||||
当前积分余额57.28元,
|
||||
</view>
|
||||
<view class="" style="color:#5d75a9 ;">
|
||||
全部提现
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="action-buttons">
|
||||
<u-button type="primary" :custom-style="contactButtonStyle">立即提现</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { ref } from "vue";
|
||||
// 按钮样式
|
||||
const contactButtonStyle = ref({
|
||||
backgroundColor: '#FF6600',
|
||||
borderColor: '#FF6600',
|
||||
color: '#FFFFFF',
|
||||
fontSize: '28rpx',
|
||||
height: '75rpx',
|
||||
borderRadius: '45rpx',
|
||||
marginRight: '20rpx'
|
||||
});
|
||||
|
||||
onLoad(() => {
|
||||
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action-buttons{
|
||||
display: flex;
|
||||
padding: 30rpx 30rpx;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #ffffff;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Service } from '@/Service/Service';
|
||||
/*****用户*****/
|
||||
class vpUserService {
|
||||
private static GetUserInfoPath : string = '/User/GetUserInfo';
|
||||
/*****获取用户信息*****/
|
||||
static GetUserInfo() {
|
||||
var result = Service.Request(this.GetUserInfoPath, 'GET', {});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static GetUserAccInfoPath : string = '/User/GetUserAccInfo';
|
||||
/*****获取用户账户信息*****/
|
||||
static GetUserAccInfo(page : number) {
|
||||
var result = Service.Request(this.GetUserAccInfoPath, 'GET', { page });
|
||||
return result;
|
||||
}
|
||||
|
||||
private static UpdateUserPath : string = '/User/UpdateUser';
|
||||
/*****修改用户信息*****/
|
||||
static UpdateUser(headImg:string,nick:string,sex:string,phone:string) {
|
||||
var result = Service.Request(this.UpdateUserPath, 'POST', { headImg,nick,sex,phone });
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static PayMerchPath : string = '/Order/PayMerch';
|
||||
/*****支付*****/
|
||||
static PayMerch(merchId:string,amount:number,payway:string,openId:string) {
|
||||
var result = Service.Request(this.PayMerchPath, 'POST', { merchId,amount,payway,openId });
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static GetShareEwmPath : string = '/User/GetShareEwm';
|
||||
/*****获取用户二维码*****/
|
||||
static GetShareEwm() {
|
||||
var result = Service.Request(this.GetShareEwmPath, 'GET', { });
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
export { Service, vpUserService };
|
||||
@@ -0,0 +1,19 @@
|
||||
export class UploadAssist {
|
||||
static Upload(url: string, path: string, fromData: any) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
uni.uploadFile({
|
||||
url: url, //仅为示例,非真实的接口地址
|
||||
filePath: path,
|
||||
name: 'file',
|
||||
formData: fromData,
|
||||
success: (uploadFileRes) => {
|
||||
resolve(uploadFileRes);
|
||||
},
|
||||
fail: (err) => {
|
||||
reject(err);
|
||||
|
||||
},
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="" style=" width: 100%; height: 310rpx; background: linear-gradient(45deg,#FF6B35,#FF8B65);">
|
||||
|
||||
</view>
|
||||
<view class=""
|
||||
style=" margin: 0 30rpx; margin-top: -110rpx; padding: 30rpx; background-color: #fff; border-radius: 20rpx; ">
|
||||
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<view class="" style="font-size: 36rpx; font-weight: 600;">
|
||||
{{userInfo?.nick}}
|
||||
</view>
|
||||
<view @click="Service.GoPage('/pages/userFunc/setData')" class="">
|
||||
<up-icon name="arrow-right" size="18" color='#333333' :bold='true'></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class=""
|
||||
style="display: flex; align-items: center; margin-top: 40rpx; justify-content: space-between; ">
|
||||
<view @click="Service.GoPage('/pages/userFunc/integration')" class=""
|
||||
style=" width: 48%; display: flex;align-items: center;">
|
||||
<view class=""
|
||||
style=" display: flex;align-items: center;justify-content: center; width: 80rpx; height: 80rpx; background-color: #FF6B35; border-radius: 50%; ">
|
||||
<img :src="Service.GetIconImg('/static/index/user/code.png')"
|
||||
style="width: 50rpx; height: 50rpx; " alt="" />
|
||||
</view>
|
||||
<view class=""
|
||||
style=" margin-left: -20rpx; flex: 1; text-align: center; font-size: 28rpx; font-weight: 600;">
|
||||
{{accInfo?.integral}}积分
|
||||
</view>
|
||||
<view class="">
|
||||
<up-icon name="arrow-right" size="14" color='#9CA3AF' :bold='true'></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="" @click="Service.GoPage('/pages/userFunc/trade?type='+1)"
|
||||
style=" width: 48%; display: flex;align-items: center;">
|
||||
<view class=""
|
||||
style=" display: flex;align-items: center;justify-content: center; width: 80rpx; height: 80rpx; background-color: #FF6B35; border-radius: 50%; ">
|
||||
<img :src="Service.GetIconImg('/static/index/user/list.png')"
|
||||
style="width: 50rpx; height: 50rpx; " alt="" />
|
||||
</view>
|
||||
<view class=""
|
||||
style=" margin-left: -20rpx; flex: 1; text-align: center; font-size: 28rpx; font-weight: 600;">
|
||||
交易记录
|
||||
</view>
|
||||
<view class="">
|
||||
<up-icon name="arrow-right" size="14" color='#9CA3AF' :bold='true'></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 管理中心 -->
|
||||
<view class="service-section" style="margin: 40rpx 30rpx 0; border-radius: 20rpx; ">
|
||||
<text class="section-title" style="font-weight: 600;">商家管理中心</text>
|
||||
<view class="service-grid">
|
||||
<view class="service-item" @click="gotopage(controItem)" v-for="(controItem,serviceIndex) in controList"
|
||||
:key="serviceIndex">
|
||||
<view class="flex-center" style=" border-radius: 50%; padding: 20rpx; background-color: #FFF5F0; ">
|
||||
<image :src="Service.GetIconImg(controItem.img)" class="service-icon"></image>
|
||||
</view>
|
||||
<text class="service-text">{{controItem.name}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 个人服务 -->
|
||||
<view class="service-section" style="margin: 40rpx 30rpx 0; border-radius: 20rpx; ">
|
||||
<text class="section-title" style="font-weight: 600;">我的服务</text>
|
||||
<view class=""
|
||||
style=" margin-top: 40rpx; display: flex; align-items: center; justify-content: space-between;">
|
||||
<view style=" width: 45%; display: flex; align-items: center;" @click="serviceFunc(myServiceItem,serviceIndex)"
|
||||
v-for="(myServiceItem,serviceIndex) in myServiceList" :key="serviceIndex">
|
||||
<view class="flex-center" style=" border-radius: 50%; padding: 20rpx; background-color: #FFF5F0; ">
|
||||
<image :src="Service.GetIconImg(myServiceItem.img)" class="service-icon"></image>
|
||||
</view>
|
||||
<text style="margin-left: 30rpx; font-size: 28rpx; font-weight: 600; ">{{myServiceItem.name}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { Service } from "@/Service/Service"
|
||||
import { ref } from "vue";
|
||||
import { vpUserService } from '@/Service/vp/vpUserService'
|
||||
|
||||
interface accInfo{
|
||||
account:number
|
||||
integral:number
|
||||
userId:string
|
||||
}
|
||||
|
||||
interface userInfo{
|
||||
nick:string
|
||||
integral:number
|
||||
}
|
||||
|
||||
|
||||
let controList = ref([
|
||||
{
|
||||
img: '/static/index/user/analysis.png',
|
||||
name: '数据统计',
|
||||
path: '/pages/userFunc/statistics'
|
||||
},
|
||||
{
|
||||
img: '/static/index/user/shop.png',
|
||||
name: '商品管理',
|
||||
path: '/pages/goods/goodsContro'
|
||||
},
|
||||
{
|
||||
img: '/static/index/user/trad.png',
|
||||
name: '交易明细',
|
||||
path: '/pages/userFunc/trade?type=' + 0
|
||||
|
||||
},
|
||||
{
|
||||
img: '/static/index/user/store.png',
|
||||
name: '编辑店铺',
|
||||
path: '/pages/userFunc/editStore'
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
let myServiceList = ref([
|
||||
{
|
||||
img: '/static/index/user/request.png',
|
||||
name: '客服咨询',
|
||||
path: ''
|
||||
},
|
||||
{
|
||||
img: '/static/index/user/set.png',
|
||||
name: '系统设置',
|
||||
path: '/pages/userFunc/set'
|
||||
}
|
||||
])
|
||||
|
||||
let page=ref(1)
|
||||
|
||||
let accInfo=ref<accInfo>()
|
||||
let userInfo=ref<userInfo>()
|
||||
|
||||
onLoad(() => {
|
||||
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
getUserinfo()
|
||||
getUseraccInfo()
|
||||
});
|
||||
|
||||
const gotopage = (item : any) => {
|
||||
if (item.path) {
|
||||
Service.GoPage(item.path)
|
||||
}
|
||||
}
|
||||
|
||||
const serviceFunc=(item:any,index:any)=>{
|
||||
if(index==0){
|
||||
wx.openCustomerServiceChat({
|
||||
extInfo: { url: 'https://work.weixin.qq.com/kfid/kfc959c128ce7801256' },
|
||||
corpId: 'wwb1123fbb286554ab',
|
||||
success(res) { },
|
||||
fail(err) {
|
||||
console.log(err, '失败')
|
||||
// 失败回调
|
||||
}
|
||||
})
|
||||
}else{
|
||||
Service.GoPage(item.path)
|
||||
}
|
||||
}
|
||||
|
||||
const getUserinfo=()=>{
|
||||
vpUserService.GetUserInfo().then(res=>{
|
||||
userInfo.value=res.data.userInfo
|
||||
})
|
||||
}
|
||||
|
||||
const getUseraccInfo=()=>{
|
||||
vpUserService.GetUserAccInfo(page.value).then(res=>{
|
||||
accInfo.value=res.data.accInfo
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 服务区域通用样式 */
|
||||
.service-section,
|
||||
.value-added-section {
|
||||
background-color: #fff;
|
||||
margin: 20rpx 0rpx;
|
||||
overflow: hidden;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
/* 服务网格 */
|
||||
.service-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 10rpx 20rpx 30rpx;
|
||||
}
|
||||
|
||||
.service-item {
|
||||
width: 25%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.service-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.service-text {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
margin-top: 16rpx;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<view style="padding: 20rpx;">
|
||||
<view class=""
|
||||
style=" display: flex; align-items: center; justify-content: space-around; background-color: #fff; padding: 20rpx">
|
||||
<view v-for="(item, index) in tabList" @click="chooseTab(index)"
|
||||
style="display: flex; flex-direction: column; align-items: center; justify-content: center;"
|
||||
:key="index">
|
||||
<view class="" :class="{tabimgActive:index==tabCurrent,tabimg: index!=tabCurrent }"
|
||||
style=" border-radius: 50%; display: flex; align-items: center; justify-content: center; height: 100rpx; width: 100rpx; ">
|
||||
<img :src="Service.GetIconImg( index==tabCurrent? item.imged:item.img)"
|
||||
style="width: 50rpx; height: 50rpx; "></img>
|
||||
</view>
|
||||
<view :class="{tabActivefont:index==tabCurrent,tabfont:index!=tabCurrent}"
|
||||
style="font-size: 26rpx; margin-top: 15rpx;" class="">
|
||||
{{item.name}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="" style=" background-color: #fff; padding: 20rpx; " >
|
||||
|
||||
<view class="" @click="Service.GoPage('/pages/community/merchantDetail')"
|
||||
style="padding: 20rpx; margin-top: 20rpx; border-radius: 20rpx; box-shadow: 0 0 10rpx 4rpx #e2e2e2;">
|
||||
<view class="" style="display: flex; ">
|
||||
<img :src="Service.GetMateUrlByImg('/static/dele/dele1.jpg')"
|
||||
style=" border-radius: 20rpx; width: 140rpx; height: 140rpx;" alt="" />
|
||||
<view class=""
|
||||
style=" flex: 1; margin-left: 20rpx; display: flex; flex-direction: column; justify-content: space-between; ">
|
||||
<view class="" style="display: flex; align-items: center;">
|
||||
<view class="" style="font-weight: 700; font-size: 32rpx;">
|
||||
老北京炸酱面
|
||||
</view>
|
||||
<view class="tag"
|
||||
style=" margin-left: 15rpx; color: #fff; border-radius: 12rpx; background-color: #FF6B35; padding: 4rpx 20rpx; ">
|
||||
新店
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="display: flex;align-items: center;">
|
||||
<up-rate count="1" activeColor='#FF6B35' size='16' :readonly='true'></up-rate>
|
||||
<text style="color: #666666; font-size: 26rpx;">3.8</text>
|
||||
<text style="margin-left: 10rpx;color: #666666; font-size: 26rpx; ">月售892单</text>
|
||||
</view>
|
||||
<view class="" style="display: flex; align-items: center; justify-content: space-between; ">
|
||||
<view class="" style="display: flex;align-items: center;">
|
||||
<up-icon name="map" color="#666666" size="18"></up-icon>
|
||||
<text style="color: #666666; margin-left: 12rpx; font-size: 26rpx;">0.8km</text>
|
||||
</view>
|
||||
<view class="" style="margin-right: 20rpx;">
|
||||
<text style="font-size: 28rpx;font-weight: 600; color: #FF6B35; "> ¥58/人 </text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="margin: 20rpx; margin-bottom: 0; " >
|
||||
<up-scroll-list :indicator='false' >
|
||||
<view v-for="(item, index) in scrollList" :key="index" style="display: flex; margin-right: 50rpx; flex-direction: column; justify-content: center; align-items: center;" >
|
||||
<img :src="Service.GetIconImg(item.img)" alt="" style="width: 100rpx; height: 100rpx; border-radius: 20rpx; " />
|
||||
<view class="" style="font-size: 24rpx; color: #666666; margin-top: 10rpx; " >
|
||||
{{item.name}}
|
||||
</view>
|
||||
</view>
|
||||
</up-scroll-list>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { ref } from "vue";
|
||||
import {Service} from "@/Service/Service"
|
||||
|
||||
let search = ref()
|
||||
|
||||
let tabCurrent=ref(0)
|
||||
let tabList = ref(
|
||||
[
|
||||
{
|
||||
name: '美食',
|
||||
img: '/static/index/index/food.png',
|
||||
imged: '/static/index/index/fooded.png'
|
||||
},
|
||||
{
|
||||
name: '饮品',
|
||||
img: '/static/index/index/cofe.png',
|
||||
imged: '/static/index/index/cofed.png'
|
||||
},
|
||||
{
|
||||
name: '超市',
|
||||
img: '/static/index/index/shop.png',
|
||||
imged: '/static/index/index/shoped.png'
|
||||
},
|
||||
{
|
||||
name: '美妆',
|
||||
img: '/static/index/index/good.png',
|
||||
imged: '/static/index/index/gooded.png'
|
||||
},
|
||||
{
|
||||
name: '医疗',
|
||||
img: '/static/index/index/medical.png',
|
||||
imged: '/static/index/index/medicaled.png'
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
let scrollList = ref([
|
||||
{
|
||||
name: '牛肉面',
|
||||
img: '/static/dele/dele3.png'
|
||||
},
|
||||
{
|
||||
name: '牛肉面',
|
||||
img: '/static/dele/dele3.png'
|
||||
},
|
||||
{
|
||||
name: '牛肉面',
|
||||
img: '/static/dele/dele3.png'
|
||||
},
|
||||
{
|
||||
name: '牛肉面',
|
||||
img: '/static/dele/dele3.png'
|
||||
},
|
||||
{
|
||||
name: '牛肉面',
|
||||
img: '/static/dele/dele3.png'
|
||||
},
|
||||
{
|
||||
name: '牛肉面',
|
||||
img: '/static/dele/dele3.png'
|
||||
},
|
||||
{
|
||||
name: '牛肉面',
|
||||
img: '/static/dele/dele3.png'
|
||||
},
|
||||
{
|
||||
name: '牛肉面',
|
||||
img: '/static/dele/dele3.png'
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
onLoad(() => {
|
||||
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
|
||||
|
||||
const chooseTab=(e)=>{
|
||||
tabCurrent.value=e
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
|
||||
.tabimgActive{
|
||||
background-color: var(--nav-mian);
|
||||
}
|
||||
.tabimg{
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
|
||||
.tabActivefont{
|
||||
color: var(--nav-mian);
|
||||
}
|
||||
.tabfont{
|
||||
color:#333333
|
||||
}
|
||||
.tag{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: fit-content;
|
||||
font-size: 24rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,368 @@
|
||||
export const networkReg = /^(http|\/\/)/;
|
||||
export const isBase64 = (path) => /^data:image\/(\w+);base64/.test(path);
|
||||
export function sleep(delay) {
|
||||
return new Promise(resolve => setTimeout(resolve, delay))
|
||||
}
|
||||
let {platform, SDKVersion} = uni.getSystemInfoSync()
|
||||
export const isPC = /windows|mac/.test(platform)
|
||||
// 缓存图片
|
||||
let cache = {}
|
||||
export function isNumber(value) {
|
||||
return /^-?\d+(\.\d+)?$/.test(value);
|
||||
}
|
||||
export function toPx(value, baseSize, isDecimal = false) {
|
||||
// 如果是数字
|
||||
if (typeof value === 'number') {
|
||||
return value
|
||||
}
|
||||
// 如果是字符串数字
|
||||
if (isNumber(value)) {
|
||||
return value * 1
|
||||
}
|
||||
// 如果有单位
|
||||
if (typeof value === 'string') {
|
||||
const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g
|
||||
const results = reg.exec(value);
|
||||
if (!value || !results) {
|
||||
return 0;
|
||||
}
|
||||
const unit = results[3];
|
||||
value = parseFloat(value);
|
||||
let res = 0;
|
||||
if (unit === 'rpx') {
|
||||
res = uni.upx2px(value);
|
||||
} else if (unit === 'px') {
|
||||
res = value * 1;
|
||||
} else if (unit === '%') {
|
||||
res = value * toPx(baseSize) / 100;
|
||||
} else if (unit === 'em') {
|
||||
res = value * toPx(baseSize || 14);
|
||||
}
|
||||
return isDecimal ? res.toFixed(2) * 1 : Math.round(res);
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// 计算版本
|
||||
export function compareVersion(v1, v2) {
|
||||
v1 = v1.split('.')
|
||||
v2 = v2.split('.')
|
||||
const len = Math.max(v1.length, v2.length)
|
||||
while (v1.length < len) {
|
||||
v1.push('0')
|
||||
}
|
||||
while (v2.length < len) {
|
||||
v2.push('0')
|
||||
}
|
||||
for (let i = 0; i < len; i++) {
|
||||
const num1 = parseInt(v1[i], 10)
|
||||
const num2 = parseInt(v2[i], 10)
|
||||
|
||||
if (num1 > num2) {
|
||||
return 1
|
||||
} else if (num1 < num2) {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
function gte(version) {
|
||||
// #ifdef MP-ALIPAY
|
||||
SDKVersion = my.SDKVersion
|
||||
// #endif
|
||||
return compareVersion(SDKVersion, version) >= 0;
|
||||
}
|
||||
export function canIUseCanvas2d() {
|
||||
// #ifdef MP-WEIXIN
|
||||
return gte('2.9.2');
|
||||
// #endif
|
||||
// #ifdef MP-ALIPAY
|
||||
return gte('2.7.15');
|
||||
// #endif
|
||||
// #ifdef MP-TOUTIAO
|
||||
return gte('1.78.0');
|
||||
// #endif
|
||||
return false
|
||||
}
|
||||
|
||||
// #ifdef MP
|
||||
export const prefix = () => {
|
||||
// #ifdef MP-TOUTIAO
|
||||
return tt
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
return wx
|
||||
// #endif
|
||||
// #ifdef MP-BAIDU
|
||||
return swan
|
||||
// #endif
|
||||
// #ifdef MP-ALIPAY
|
||||
return my
|
||||
// #endif
|
||||
// #ifdef MP-QQ
|
||||
return qq
|
||||
// #endif
|
||||
// #ifdef MP-360
|
||||
return qh
|
||||
// #endif
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* base64转路径
|
||||
* @param {Object} base64
|
||||
*/
|
||||
export function base64ToPath(base64) {
|
||||
const [, format] = /^data:image\/(\w+);base64,/.exec(base64) || [];
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef MP
|
||||
const fs = uni.getFileSystemManager()
|
||||
//自定义文件名
|
||||
if (!format) {
|
||||
reject(new Error('ERROR_BASE64SRC_PARSE'))
|
||||
}
|
||||
const time = new Date().getTime();
|
||||
let pre = prefix()
|
||||
// #ifdef MP-TOUTIAO
|
||||
const filePath = `${pre.getEnvInfoSync().common.USER_DATA_PATH}/${time}.${format}`
|
||||
// #endif
|
||||
// #ifndef MP-TOUTIAO
|
||||
const filePath = `${pre.env.USER_DATA_PATH}/${time}.${format}`
|
||||
// #endif
|
||||
fs.writeFile({
|
||||
filePath,
|
||||
data: base64.split(',')[1],
|
||||
encoding: 'base64',
|
||||
success() {
|
||||
resolve(filePath)
|
||||
},
|
||||
fail(err) {
|
||||
console.error(err)
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
// mime类型
|
||||
let mimeString = base64.split(',')[0].split(':')[1].split(';')[0];
|
||||
//base64 解码
|
||||
let byteString = atob(base64.split(',')[1]);
|
||||
//创建缓冲数组
|
||||
let arrayBuffer = new ArrayBuffer(byteString.length);
|
||||
//创建视图
|
||||
let intArray = new Uint8Array(arrayBuffer);
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
intArray[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
resolve(URL.createObjectURL(new Blob([intArray], {
|
||||
type: mimeString
|
||||
})))
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
|
||||
bitmap.loadBase64Data(base64, () => {
|
||||
if (!format) {
|
||||
reject(new Error('ERROR_BASE64SRC_PARSE'))
|
||||
}
|
||||
const time = new Date().getTime();
|
||||
const filePath = `_doc/uniapp_temp/${time}.${format}`
|
||||
bitmap.save(filePath, {},
|
||||
() => {
|
||||
bitmap.clear()
|
||||
resolve(filePath)
|
||||
},
|
||||
(error) => {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
}, (error) => {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径转base64
|
||||
* @param {Object} string
|
||||
*/
|
||||
export function pathToBase64(path) {
|
||||
if (/^data:/.test(path)) return path
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef H5
|
||||
let image = new Image();
|
||||
image.setAttribute("crossOrigin", 'Anonymous');
|
||||
image.onload = function() {
|
||||
let canvas = document.createElement('canvas');
|
||||
canvas.width = this.naturalWidth;
|
||||
canvas.height = this.naturalHeight;
|
||||
canvas.getContext('2d').drawImage(image, 0, 0);
|
||||
let result = canvas.toDataURL('image/png')
|
||||
resolve(result);
|
||||
canvas.height = canvas.width = 0
|
||||
}
|
||||
image.src = path + '?v=' + Math.random()
|
||||
image.onerror = (error) => {
|
||||
reject(error);
|
||||
};
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
if (uni.canIUse('getFileSystemManager')) {
|
||||
uni.getFileSystemManager().readFile({
|
||||
filePath: path,
|
||||
encoding: 'base64',
|
||||
success: (res) => {
|
||||
resolve('data:image/png;base64,' + res.data)
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error({error, path})
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), (entry) => {
|
||||
entry.file((file) => {
|
||||
const fileReader = new plus.io.FileReader()
|
||||
fileReader.onload = (data) => {
|
||||
resolve(data.target.result)
|
||||
}
|
||||
fileReader.onerror = (error) => {
|
||||
reject(error)
|
||||
}
|
||||
fileReader.readAsDataURL(file)
|
||||
}, reject)
|
||||
}, reject)
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function getImageInfo(path, useCORS) {
|
||||
const isCanvas2D = this && this.canvas && this.canvas.createImage
|
||||
return new Promise(async (resolve, reject) => {
|
||||
// let time = +new Date()
|
||||
let src = path.replace(/^@\//,'/')
|
||||
if (cache[path] && cache[path].errMsg) {
|
||||
resolve(cache[path])
|
||||
} else {
|
||||
try {
|
||||
// #ifdef MP || APP-PLUS
|
||||
if (isBase64(path) && (isCanvas2D ? isPC : true)) {
|
||||
src = await base64ToPath(path)
|
||||
}
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
if(useCORS) {
|
||||
src = await pathToBase64(path)
|
||||
}
|
||||
// #endif
|
||||
} catch (error) {
|
||||
reject({
|
||||
...error,
|
||||
src
|
||||
})
|
||||
}
|
||||
// #ifndef APP-NVUE
|
||||
if(isCanvas2D && !isPC) {
|
||||
const img = this.canvas.createImage()
|
||||
img.onload = function() {
|
||||
const image = {
|
||||
path: img,
|
||||
width: img.width,
|
||||
height: img.height
|
||||
}
|
||||
cache[path] = image
|
||||
resolve(cache[path])
|
||||
}
|
||||
img.onerror = function(err) {
|
||||
reject({err,path})
|
||||
}
|
||||
img.src = src
|
||||
return
|
||||
}
|
||||
// #endif
|
||||
uni.getImageInfo({
|
||||
src,
|
||||
success: (image) => {
|
||||
const localReg = /^\.|^\/(?=[^\/])/;
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO
|
||||
image.path = localReg.test(src) ? `/${image.path}` : image.path;
|
||||
// #endif
|
||||
if(isCanvas2D) {
|
||||
const img = this.canvas.createImage()
|
||||
img.onload = function() {
|
||||
image.path = img
|
||||
cache[path] = image
|
||||
resolve(cache[path])
|
||||
}
|
||||
img.onerror = function(err) {
|
||||
reject({err,path})
|
||||
}
|
||||
img.src = src
|
||||
return
|
||||
}
|
||||
// #ifdef APP-PLUS
|
||||
// console.log('getImageInfo', +new Date() - time)
|
||||
// ios 比较严格 可能需要设置跨域
|
||||
if(uni.getSystemInfoSync().osName == 'ios' && useCORS) {
|
||||
pathToBase64(image.path).then(base64 => {
|
||||
image.path = base64
|
||||
cache[path] = image
|
||||
resolve(cache[path])
|
||||
}).catch(err => {
|
||||
console.error({err, path})
|
||||
reject({err,path})
|
||||
})
|
||||
return
|
||||
}
|
||||
// #endif
|
||||
cache[path] = image
|
||||
resolve(cache[path])
|
||||
},
|
||||
fail(err) {
|
||||
console.error({err, path})
|
||||
reject({err,path})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const getLocalFilePath = (path) => {
|
||||
if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path
|
||||
.indexOf('_downloads') === 0) {
|
||||
return path
|
||||
}
|
||||
if (path.indexOf('file://') === 0) {
|
||||
return path
|
||||
}
|
||||
if (path.indexOf('/storage/emulated/0/') === 0) {
|
||||
return path
|
||||
}
|
||||
if (path.indexOf('/') === 0) {
|
||||
const localFilePath = plus.io.convertAbsoluteFileSystem(path)
|
||||
if (localFilePath !== path) {
|
||||
return localFilePath
|
||||
} else {
|
||||
path = path.substr(1)
|
||||
}
|
||||
}
|
||||
return '_www/' + path
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"id": "qf-image-cropper",
|
||||
"displayName": "图片裁剪插件",
|
||||
"version": "2.2.5",
|
||||
"description": "图片裁剪插件,支持自定义尺寸、定点等比例缩放、拖动、图片翻转、剪切圆形/圆角图片、定制样式,功能多性能高体验好注释全。",
|
||||
"keywords": [
|
||||
"qf-image-cropper",
|
||||
"图片裁剪",
|
||||
"图片编辑",
|
||||
"头像裁剪",
|
||||
"小程序"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "插件不采集任何数据",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "n"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "u"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "u",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "n",
|
||||
"百度": "n",
|
||||
"字节跳动": "n",
|
||||
"QQ": "u",
|
||||
"钉钉": "n",
|
||||
"快手": "n",
|
||||
"飞书": "n",
|
||||
"京东": "n"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "n",
|
||||
"联盟": "n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
|
||||
<!-- 设置列表 -->
|
||||
<view class="settings-list">
|
||||
<!-- 账户安全 -->
|
||||
<!-- <view class="settings-section">
|
||||
<view class="section-title">账户安全</view>
|
||||
<view @click="Service.GoPage(accountItem.path)" v-for="(accountItem,accountIndex) in account" :key="accountIndex" class="section-item">
|
||||
<view class="" style="display: flex; align-items: center;">
|
||||
<view class="icon" style=" ">
|
||||
<img class="icon-img" :src="Service.GetIconImg(accountItem.icon)" alt="" />
|
||||
</view>
|
||||
<text style="margin-left: 14rpx; font-weight: 500; ">{{accountItem.name}}</text>
|
||||
</view>
|
||||
<view class="" style="display: flex; align-items: baseline; font-size: 24rpx; color: #9CA3AF; ">
|
||||
<text v-if="accountItem.name=='实名认证'" style="margin-right: 10rpx;">未认证</text>
|
||||
<up-icon name="arrow-right" size="12"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 支持与帮助 -->
|
||||
<view class="settings-section">
|
||||
<view class="section-title">支持与帮助</view>
|
||||
<view v-for="(helpItem,helpIndex) in help" :key="helpIndex" class="section-item">
|
||||
<view class="" style="display: flex; align-items: center;">
|
||||
<view class="icon" style=" ">
|
||||
<img class="icon-img" :src="Service.GetIconImg(helpItem.icon)" alt="" />
|
||||
</view>
|
||||
<text style="margin-left: 14rpx; font-weight: 500; ">{{helpItem.name}}</text>
|
||||
</view>
|
||||
<view class="" style="display: flex; align-items: baseline; font-size: 24rpx; color: #9CA3AF; ">
|
||||
<up-icon name="arrow-right" size="12"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Service } from '@/Service/Service'
|
||||
|
||||
|
||||
|
||||
|
||||
const account = ref([
|
||||
{
|
||||
name: '绑定手机号',
|
||||
icon: '/static/userFunc/set/security.png',
|
||||
path:'/pages/userFunc/bind'
|
||||
}, {
|
||||
name: '修改支付密码',
|
||||
icon: '/static/userFunc/set/password.png',
|
||||
path:'/pages/userFunc/password'
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
const help = ref([
|
||||
{
|
||||
name: '用户协议',
|
||||
icon: '/static/userFunc/set/agreement.png'
|
||||
},
|
||||
{
|
||||
name: '隐私政策',
|
||||
icon: '/static/userFunc/set/privacy.png'
|
||||
},
|
||||
{
|
||||
name: '联系客服',
|
||||
icon: '/static/userFunc/set/service.png'
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.page {
|
||||
background-color: #f5f5f5;
|
||||
overflow: hidden;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
|
||||
// 图标
|
||||
.icon {
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #FFEDD5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon-img {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
|
||||
/* 用户信息卡片 */
|
||||
.user-card {
|
||||
margin: 30rpx;
|
||||
border-radius: 20rpx;
|
||||
background: linear-gradient(to right, #F97316, #FB923C);
|
||||
padding: 40rpx 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
background-color: #FFFFFF;
|
||||
padding: 10rpx;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
margin-left: 30rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 32rpx;
|
||||
color: #FFFFFF;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.user-level {
|
||||
width: fit-content;
|
||||
font-size: 18rpx;
|
||||
color: #fff;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
padding: 4rpx 20rpx;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
/* 设置列表 */
|
||||
.settings-list {
|
||||
padding: 0 30rpx;
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 30rpx;
|
||||
overflow: hidden;
|
||||
padding: 30rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
color: #6B7280;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.section-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #e2e2e2;
|
||||
|
||||
// &:last-child{
|
||||
// border-bottom: none;
|
||||
// }
|
||||
}
|
||||
|
||||
.u-cell {
|
||||
height: 100rpx;
|
||||
font-size: 32rpx;
|
||||
--u-cell-value-color: #999999;
|
||||
--u-cell-title-color: #333333;
|
||||
--u-cell-arrow-size: 40rpx;
|
||||
}
|
||||
|
||||
/* 版本信息 */
|
||||
.version-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 60rpx;
|
||||
|
||||
}
|
||||
|
||||
.version-text {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.version-number {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.check-update {
|
||||
font-size: 24rpx;
|
||||
color: #FF6600;
|
||||
background-color: #FFF7ED;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
|
||||
/* 退出登录按钮 */
|
||||
.logout-btn {
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,585 @@
|
||||
<template>
|
||||
<view class="settings-page">
|
||||
<!-- 沉浸式状态栏 -->
|
||||
<view v-if="!isZFB" class="status-bar"></view>
|
||||
|
||||
<!-- 顶部导航 -->
|
||||
<view v-if="!isZFB" class="nav-bar">
|
||||
<image class="back-icon" src="/static/icons/back.svg" @click="goBack" mode="aspectFit" />
|
||||
<text class="nav-title">设置</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<view class="content">
|
||||
<!-- 个人信息卡片 -->
|
||||
<view class="profile-card">
|
||||
<view class="card-title">
|
||||
<text class="ri-user-line title-icon"></text>
|
||||
<text class="title-text">个人信息</text>
|
||||
</view>
|
||||
|
||||
<!-- 头像 -->
|
||||
<view class="setting-item" @click="uploadFImg(140,140,'Avatar','headimg')">
|
||||
<view class="item-left">
|
||||
<text class="item-label">头像</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<image class="avatar-preview" :src="Service.GetMateUrlByImg(userInfo.headImg)"
|
||||
mode="aspectFill" />
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 昵称 -->
|
||||
<view class="setting-item" @click="changeNickname">
|
||||
<view class="item-left">
|
||||
<text class="item-label">昵称</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-value">{{ userInfo.nick }}</text>
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 手机号 -->
|
||||
<view class="setting-item">
|
||||
<view class="item-left">
|
||||
<text class="item-label">手机号</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-value">{{ userInfo.phone }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 会员ID -->
|
||||
<view class="setting-item">
|
||||
<view class="item-left">
|
||||
<text class="item-label">会员ID</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-value">{{ userInfo.userNo }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 其他设置 -->
|
||||
<!-- <view class="other-settings-card">
|
||||
<view class="card-title">
|
||||
<text class="ri-settings-4-line title-icon"></text>
|
||||
<text class="title-text">其他设置</text>
|
||||
</view>
|
||||
|
||||
<view class="setting-item" @click="clearCache">
|
||||
<view class="item-left">
|
||||
<text class="item-label">清除缓存</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-value">23.5MB</text>
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="setting-item" @click="checkUpdate">
|
||||
<view class="item-left">
|
||||
<text class="item-label">检查更新</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-value">当前版本 v1.0.0</text>
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="setting-item" @click="viewPrivacy">
|
||||
<view class="item-left">
|
||||
<text class="item-label">隐私政策</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="setting-item" @click="viewAgreement">
|
||||
<view class="item-left">
|
||||
<text class="item-label">用户协议</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="ri-arrow-right-s-line item-arrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
<!-- <view class="logout-section">
|
||||
<button class="logout-btn" @click="save()">
|
||||
<text class="btn-text">保存信息</text>
|
||||
</button>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
<!-- 修改昵称弹窗 -->
|
||||
<view v-if="showNicknameModal" class="modal-overlay" @click="closeNicknameModal">
|
||||
<view class="modal-content" @click.stop>
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">修改昵称</text>
|
||||
<text class="ri-close-line modal-close" @click="closeNicknameModal"></text>
|
||||
</view>
|
||||
<view class="modal-body">
|
||||
<input class="nickname-input" v-model="tempNickname" placeholder="请输入新昵称" maxlength="20" />
|
||||
</view>
|
||||
<view class="modal-footer">
|
||||
<button class="modal-btn cancel" @click.stop="closeNicknameModal">取消</button>
|
||||
<button class="modal-btn confirm" @click.stop="saveNickname">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { Service } from '@/Service/Service';
|
||||
import { ref } from "vue";
|
||||
import { vpUserService } from '@/Service/vp/vpUserService';
|
||||
import ImageCropperFunc from "@/components/ImageCropper";
|
||||
import {
|
||||
inject
|
||||
} from 'vue';
|
||||
const isZFB = inject('isZFB');
|
||||
|
||||
const userInfo = ref<any>({
|
||||
nick: '',
|
||||
sex: '',
|
||||
phone: '',
|
||||
headImg: ''
|
||||
})
|
||||
|
||||
// 临时昵称
|
||||
const tempNickname = ref('')
|
||||
|
||||
|
||||
|
||||
onLoad(() => {
|
||||
getUserinfo()
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
|
||||
|
||||
const getUserinfo = () => {
|
||||
vpUserService.GetUserInfo().then(res => {
|
||||
userInfo.value = res.data.userInfo
|
||||
})
|
||||
}
|
||||
const uploadFImg = (width : any, height : any, Type : any, Name : any) => {
|
||||
uni.chooseImage({
|
||||
count: 1, // 最多选择3张图片
|
||||
sizeType: ['original', 'compressed'], // 支持原图和压缩图
|
||||
sourceType: ['album', 'camera'], // 可从相册选择或使用相机拍照
|
||||
success: function (res) {
|
||||
let path = res.tempFiles[0].path
|
||||
let arr = path.split('.')
|
||||
let name = arr[arr.length - 1]
|
||||
Service.uploadH5(path, 'Avatar', (data) => {
|
||||
userInfo.value.headImg = data
|
||||
vpUserService.UpdateUser(userInfo.value.headImg, userInfo.value.nick, userInfo.value.sex, '').then(res => {
|
||||
if (res.code == 0) {
|
||||
Service.Msg('修改成功!')
|
||||
getUserinfo()
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
fail: function (err) {
|
||||
console.error('选择失败:', err.errMsg);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 修改昵称
|
||||
const changeNickname = () => {
|
||||
tempNickname.value = userInfo.value.nick
|
||||
showNicknameModal.value = true
|
||||
}
|
||||
|
||||
// 关闭昵称弹窗
|
||||
const closeNicknameModal = () => {
|
||||
showNicknameModal.value = false
|
||||
}
|
||||
|
||||
|
||||
// 保存昵称
|
||||
const saveNickname = () => {
|
||||
if (!tempNickname.value.trim()) {
|
||||
uni.showToast({
|
||||
title: '昵称不能为空',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
vpUserService.UpdateUser(userInfo.value.headImg, tempNickname.value, userInfo.value.sex, '').then(res => {
|
||||
if (res.code == 0) {
|
||||
Service.Msg('修改成功!')
|
||||
showNicknameModal.value = false
|
||||
userInfo.value.nick = tempNickname.value
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 用户信息
|
||||
const user = ref({
|
||||
nickname: '美食达人',
|
||||
avatar: 'https://picsum.photos/200/200?random=100',
|
||||
phone: '138****8888',
|
||||
memberId: '8888888'
|
||||
})
|
||||
|
||||
// 显示昵称弹窗
|
||||
const showNicknameModal = ref(false)
|
||||
|
||||
|
||||
|
||||
// 返回
|
||||
const goBack = () => {
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
})
|
||||
}
|
||||
|
||||
// 修改头像
|
||||
const changeAvatar = () => {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
// 这里应该上传到服务器,现在暂时直接使用本地路径
|
||||
user.value.avatar = res.tempFilePaths[0]
|
||||
uni.showToast({
|
||||
title: '头像已更新',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 清除缓存
|
||||
const clearCache = () => {
|
||||
uni.showModal({
|
||||
title: '清除缓存',
|
||||
content: '确定要清除缓存吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({
|
||||
title: '清除中...'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '缓存已清除',
|
||||
icon: 'success'
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 检查更新
|
||||
const checkUpdate = () => {
|
||||
uni.showLoading({
|
||||
title: '检查中...'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '已是最新版本',
|
||||
icon: 'success'
|
||||
})
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
// 查看隐私政策
|
||||
const viewPrivacy = () => {
|
||||
uni.showToast({
|
||||
title: '隐私政策页面',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 查看用户协议
|
||||
const viewAgreement = () => {
|
||||
uni.showToast({
|
||||
title: '用户协议页面',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
const logout = () => {
|
||||
uni.showModal({
|
||||
title: '退出登录',
|
||||
content: '确定要退出登录吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({
|
||||
title: '退出中...'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '已退出登录',
|
||||
icon: 'success'
|
||||
})
|
||||
// 这里可以跳转到登录页面
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.settings-page {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 状态栏 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 60rpx 24rpx 20rpx 24rpx;
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.nav-placeholder {
|
||||
width: 48rpx;
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 32rpx 24rpx;
|
||||
}
|
||||
|
||||
/* 卡片通用样式 */
|
||||
.profile-card,
|
||||
.other-settings-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 24rpx;
|
||||
padding-bottom: 16rpx;
|
||||
border-bottom: 2rpx solid #F5F5F5;
|
||||
}
|
||||
|
||||
.title-icon {
|
||||
font-size: 32rpx;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
/* 设置项 */
|
||||
.setting-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24rpx 0;
|
||||
border-bottom: 1rpx solid #F5F5F5;
|
||||
}
|
||||
|
||||
.setting-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
font-size: 28rpx;
|
||||
color: #222222;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 26rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.item-arrow {
|
||||
font-size: 28rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
.avatar-preview {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
}
|
||||
|
||||
/* 退出登录按钮 */
|
||||
.logout-section {
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
background: #FFFFFF;
|
||||
border: 2rpx solid #F44336;
|
||||
border-radius: 44rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
/* 弹窗样式 */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 560rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
font-size: 40rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.nickname-input {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
padding: 0 20rpx;
|
||||
background: #F5F5F5;
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.modal-btn {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.modal-btn.cancel {
|
||||
background: #F5F5F5;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.modal-btn.confirm {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,408 @@
|
||||
<template>
|
||||
<view class="coupon-page">
|
||||
<!-- 沉浸式状态栏 -->
|
||||
<view class="status-bar"></view>
|
||||
|
||||
<!-- 顶部导航 -->
|
||||
<view class="nav-bar">
|
||||
<image class="back-icon" src="/static/icons/back.svg" @click="goBack" mode="aspectFit" />
|
||||
<text class="nav-title">优惠券</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
|
||||
<!-- Tab 切换 - 简约文字Tab -->
|
||||
<view class="tab-bar">
|
||||
<view v-for="(tab, index) in tabs" :key="index" class="tab-item" :class="{ active: currentTab === index }"
|
||||
@click="switchTab(index)">
|
||||
<text class="tab-text">{{ tab.label }}</text>
|
||||
<text v-if="tab.count > 0" class="tab-count">({{ tab.count }})</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 优惠券列表 - 简约卡片 -->
|
||||
<view class="coupon-list">
|
||||
<view v-for="coupon in coupons" :key="coupon.id" class="coupon-card" :class="`coupon-${coupon.status}`">
|
||||
<!-- 左侧金额 -->
|
||||
<view class="coupon-left">
|
||||
<!-- <view v-if="coupon.code === 'discount'" class="coupon-amount">
|
||||
<text class="amount-number">{{ coupon.deductMoney }}</text>
|
||||
<text class="amount-unit">折</text>
|
||||
</view> -->
|
||||
<view v-if="coupon.code === 'Special'" class="coupon-amount">
|
||||
<text class="amount-symbol">¥</text>
|
||||
<text class="amount-number">{{ coupon.deductMoney }}</text>
|
||||
</view>
|
||||
<view v-else class="coupon-amount">
|
||||
<text class="amount-gift">礼品</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<view class="coupon-divider">
|
||||
<view class="divider-circle top"></view>
|
||||
<view class="divider-line"></view>
|
||||
<view class="divider-circle bottom"></view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧信息 -->
|
||||
<view class="coupon-right">
|
||||
<view class="coupon-title">{{ coupon.code=='Special'?'满减券':'' }}</view>
|
||||
<view class="coupon-condition">
|
||||
<text v-if="coupon.needMoney > 0">满{{ coupon.needMoney }}元可用</text>
|
||||
<text v-else>无门槛</text>
|
||||
</view>
|
||||
<view class="coupon-expire">有效期至 {{ coupon.endTime }}</view>
|
||||
|
||||
<!-- 状态标识 -->
|
||||
<view v-if="coupon.status === 1" class="coupon-status used">
|
||||
<text>已使用</text>
|
||||
</view>
|
||||
<view v-else-if="coupon.status === 2" class="coupon-status expired">
|
||||
<text>已过期</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-if="coupons.length === 0" class="empty">
|
||||
<text class="empty-text">{{ emptyText }}</text>
|
||||
</view>
|
||||
<view v-else class="empty">
|
||||
<up-loadmore :status="status" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad, onReachBottom } from "@dcloudio/uni-app";
|
||||
import { Service } from "@/Service/Service"
|
||||
import { ref, computed } from "vue";
|
||||
import { vpDiscountService } from "@/Service/vp/vpDiscountService"
|
||||
|
||||
|
||||
let coupons = ref<Array<any>>([])
|
||||
let status = ref<string>('loadmore')
|
||||
let pageNo = ref<number>(1)
|
||||
|
||||
// 数据
|
||||
const currentTab = ref(0)
|
||||
|
||||
const tabs = ref([
|
||||
{ label: '未使用', value: 'unused', count: 0 ,type:1 },
|
||||
{ label: '已使用', value: 'used', count: 0 , type:2 },
|
||||
{ label: '已过期', value: 'expired', count: 0 ,type:3}
|
||||
])
|
||||
|
||||
// 空状态文本
|
||||
const emptyText = computed(() => {
|
||||
const textMap = ['暂无可用优惠券', '暂无已使用的优惠券', '暂无已过期的优惠券']
|
||||
return textMap[currentTab.value]
|
||||
})
|
||||
|
||||
onLoad(() => {
|
||||
getData()
|
||||
})
|
||||
|
||||
onReachBottom(() => {
|
||||
getList()
|
||||
});
|
||||
|
||||
|
||||
const getData = () => {
|
||||
coupons.value = []
|
||||
status.value = 'loadmore'
|
||||
pageNo.value = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
|
||||
const getList = () => {
|
||||
if (status.value == 'nomore' || status.value == 'loading') {
|
||||
return
|
||||
}
|
||||
|
||||
status.value = 'loading'
|
||||
|
||||
vpDiscountService.GetUserDiscountList( tabs.value[currentTab.value].type, pageNo.value).then(res => {
|
||||
if (res.code == 0) {
|
||||
coupons.value = [...coupons.value, ...res.data.list]
|
||||
status.value = 10 == res.data.list.length ? 'loadmore' : 'nomore'
|
||||
tabs.value[0].count =res.data.wsy
|
||||
tabs.value[1].count =res.data.ysy
|
||||
tabs.value[2].count =res.data.ygq
|
||||
pageNo.value++
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 切换Tab
|
||||
const switchTab = (index : number) => {
|
||||
currentTab.value = index
|
||||
getData()
|
||||
}
|
||||
|
||||
// 返回我的页面
|
||||
const goBack = () => {
|
||||
Service.GoPageBack()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.coupon-page {
|
||||
min-height: 100vh;
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
/* 状态栏 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 60rpx 24rpx 20rpx 24rpx;
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.nav-placeholder {
|
||||
width: 48rpx;
|
||||
}
|
||||
|
||||
/* Tab栏 - 简约文字Tab */
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
background: #FFFFFF;
|
||||
padding: 20rpx 20rpx 0;
|
||||
border-bottom: 1rpx solid #E5E5E5;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-bottom: 20rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tab-item.active .tab-text {
|
||||
color: #222;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tab-item.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 32rpx;
|
||||
height: 4rpx;
|
||||
background: #FF6B00;
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.tab-count {
|
||||
font-size: 24rpx;
|
||||
color: inherit;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
|
||||
/* 优惠券列表 */
|
||||
.coupon-list {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.coupon-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 12rpx;
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 未使用 - 橙色边框 */
|
||||
.coupon-unused {
|
||||
border: 2rpx solid #FF6B00;
|
||||
}
|
||||
|
||||
/* 已使用 - 灰色 */
|
||||
.coupon-used {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* 已过期 - 灰色 */
|
||||
.coupon-expired {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* 左侧金额区 */
|
||||
.coupon-left {
|
||||
flex-shrink: 0;
|
||||
width: 200rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #FFF4E6, #FFE0B2);
|
||||
padding: 32rpx 20rpx;
|
||||
}
|
||||
|
||||
.coupon-amount {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.amount-symbol {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
.amount-number {
|
||||
font-size: 56rpx;
|
||||
font-weight: 600;
|
||||
color: #FF6B00;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.amount-unit {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #FF6B00;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
|
||||
.amount-gift {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
/* 分割线 */
|
||||
.coupon-divider {
|
||||
position: relative;
|
||||
width: 4rpx;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
.divider-circle {
|
||||
position: absolute;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: #F5F5F5;
|
||||
border-radius: 50%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.divider-circle.top {
|
||||
top: -10rpx;
|
||||
}
|
||||
|
||||
.divider-circle.bottom {
|
||||
bottom: -10rpx;
|
||||
}
|
||||
|
||||
.divider-line {
|
||||
position: absolute;
|
||||
top: 10rpx;
|
||||
bottom: 10rpx;
|
||||
left: 50%;
|
||||
width: 2rpx;
|
||||
background: repeating-linear-gradient(to bottom,
|
||||
#F5F5F5 0rpx,
|
||||
#F5F5F5 6rpx,
|
||||
transparent 6rpx,
|
||||
transparent 12rpx);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
/* 右侧信息 */
|
||||
.coupon-right {
|
||||
flex: 1;
|
||||
padding: 24rpx 20rpx;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.coupon-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.coupon-condition {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.coupon-expire {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 状态标识 */
|
||||
.coupon-status {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 20rpx;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.coupon-status text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.coupon-status.used text {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.coupon-status.expired text {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"id": "t-cropper",
|
||||
"displayName": "t-cropper图片裁剪插件",
|
||||
"version": "1.0.8",
|
||||
"description": "vue3图片裁剪插件,头像裁剪、支持自定义尺寸、等比例缩放、拖动、图片翻转、剪切圆形/圆角图片,高性能裁剪图片插件",
|
||||
"keywords": [
|
||||
"图片裁剪",
|
||||
",头像裁剪,缩放,旋转,拖动",
|
||||
""
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0",
|
||||
"uni-app": "^3.1.0",
|
||||
"uni-app-x": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "",
|
||||
"darkmode": "-",
|
||||
"i18n": "-",
|
||||
"widescreen": "-"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "√",
|
||||
"aliyun": "√",
|
||||
"alipay": "x"
|
||||
},
|
||||
"client": {
|
||||
"uni-app": {
|
||||
"vue": {
|
||||
"vue2": "-",
|
||||
"vue3": "-"
|
||||
},
|
||||
"web": {
|
||||
"safari": "-",
|
||||
"chrome": "-"
|
||||
},
|
||||
"app": {
|
||||
"vue": "-",
|
||||
"nvue": "-",
|
||||
"android": "-",
|
||||
"ios": "-",
|
||||
"harmony": "-"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "-",
|
||||
"alipay": "-",
|
||||
"toutiao": "-",
|
||||
"baidu": "-",
|
||||
"kuaishou": "-",
|
||||
"jd": "-",
|
||||
"harmony": "-",
|
||||
"qq": "-",
|
||||
"lark": "-"
|
||||
},
|
||||
"quickapp": {
|
||||
"huawei": "-",
|
||||
"union": "-"
|
||||
}
|
||||
},
|
||||
"uni-app-x": {
|
||||
"web": {
|
||||
"safari": "-",
|
||||
"chrome": "-"
|
||||
},
|
||||
"app": {
|
||||
"android": "-",
|
||||
"ios": "-",
|
||||
"harmony": "-"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "-"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 328 B |
@@ -0,0 +1,431 @@
|
||||
<template>
|
||||
<view class="" style="padding: 30rpx;">
|
||||
<view class="" style="position: relative;">
|
||||
|
||||
<image v-if="!storeData.photo" :src="Service.GetMateUrlByImg('/static/userFunc/null.png')"
|
||||
style="border-radius: 20rpx; width: 100%; height: 350rpx; " mode="aspectFit" alt="" />
|
||||
<image v-else :src="Service.GetMateUrlByImg(storeData.photo)"
|
||||
style="border-radius: 20rpx; width: 100%; height: 350rpx; " mode="aspectFill" alt="" />
|
||||
<view @click="uploaduserImg()"
|
||||
style=" display: flex; align-items: center; justify-content: center; border-radius: 50%; width: 70rpx; height: 70rpx; position: absolute; bottom: 24rpx; right: 12rpx; background-color: #F97316; ">
|
||||
<img :src="Service.GetIconImg('/static/userFunc/photo-store.png')"
|
||||
style="width: 40rpx; height: 40rpx; " alt="" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class=""
|
||||
style=" margin-top: 20rpx; padding: 20rpx;border-radius: 20rpx; box-shadow: 0 0 10rpx 4rpx #E2e2e2; ">
|
||||
<up-form labelWidth='180rpx' :labelStyle="{'font-weight':600}" labelPosition="top" :model="storeData" :
|
||||
ref="form1">
|
||||
<up-form-item label="店铺名称: " :borderBottom="true" ref="item1">
|
||||
<up-input v-model="storeData.name" placeholder="请输入店铺名称" border="none"></up-input>
|
||||
</up-form-item>
|
||||
<up-form-item label="店铺地址: " :borderBottom="true" ref="item1">
|
||||
<view class="">
|
||||
<view v-if="!storeData.address" @click="chooseAddress()" class=""
|
||||
style="display: flex; align-items: center;">
|
||||
<text style="font-size: 26rpx; color: #c0c4cc; margin-right: 10rpx;">请选择地址 </text>
|
||||
<up-icon name="arrow-right" color="#c0c4cc" size="12"></up-icon>
|
||||
</view>
|
||||
<view v-else @click="chooseAddress()" class="">
|
||||
{{storeData.address}}
|
||||
</view>
|
||||
</view>
|
||||
</up-form-item>
|
||||
<up-form-item label="联系电话: " :borderBottom="true" ref="item1">
|
||||
<up-input v-model="storeData.phone" placeholder="请输入您的联系电话" border="none"></up-input>
|
||||
</up-form-item>
|
||||
<up-form-item label="营业时间: " :borderBottom="true" ref="item1">
|
||||
<view class="">
|
||||
<view v-if="!storeData.time" @click="showDate=!showDate" class=""
|
||||
style="display: flex; align-items: center;">
|
||||
<text style="font-size: 26rpx; color: #c0c4cc; margin-right: 10rpx;">请选择营业时间 </text>
|
||||
<up-icon name="arrow-right" color="#c0c4cc" size="12"></up-icon>
|
||||
</view>
|
||||
<view v-else @click="showDate=!showDate" class="">
|
||||
{{storeData.time}}
|
||||
</view>
|
||||
</view>
|
||||
</up-form-item>
|
||||
|
||||
</up-form>
|
||||
</view>
|
||||
|
||||
<view class=""
|
||||
style="margin-top: 20rpx; padding: 20rpx;border-radius: 20rpx; box-shadow: 0 0 10rpx 4rpx #E2e2e2; ">
|
||||
<view style="display: flex; align-items: center; justify-content: space-between;" class="">
|
||||
<view class="" style="font-weight: 600;" >
|
||||
店铺标签
|
||||
</view>
|
||||
<view class="">
|
||||
<up-icon @click="showTagfunc()" name="plus" color="#000" size="16" :bold='true'></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="display: grid; grid-template-columns: repeat(4,1fr); ">
|
||||
<view class="tag" v-for="(item,index) in tagList" :key="index"
|
||||
style=" margin-top: 15rpx; padding: 10rpx 20rpx; border-radius: 8rpx; margin-right: 15rpx; background-color: var(--nav-mian); color: #fff; ">
|
||||
{{item}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class=""
|
||||
style="margin-top: 20rpx; padding: 20rpx;border-radius: 20rpx; box-shadow: 0 0 10rpx 4rpx #E2e2e2; ">
|
||||
<view style="display: flex; align-items: center; justify-content: space-between;" class="">
|
||||
<view class="" style="font-weight: 600;" >
|
||||
所属社区
|
||||
</view>
|
||||
<view class="">
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="padding: 15rpx 0;" >
|
||||
<view v-if="!storeData.community" @click="showCommunity=!showCommunity" class=""
|
||||
style="display: flex; align-items: center;">
|
||||
<text style="font-size: 26rpx; color: #c0c4cc; margin-right: 10rpx;">请选择所属社区 </text>
|
||||
<up-icon name="arrow-right" color="#c0c4cc" size="12"></up-icon>
|
||||
</view>
|
||||
<view v-else @click="showCommunity=!showCommunity" class="">
|
||||
{{storeData.community}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class=""
|
||||
style="margin-top: 20rpx; padding: 20rpx;border-radius: 20rpx; box-shadow: 0 0 10rpx 4rpx #E2e2e2; ">
|
||||
<view style="display: flex; align-items: center; justify-content: space-between;" class="">
|
||||
<view class="" style="font-weight: 600;" >
|
||||
商品管理
|
||||
</view>
|
||||
<view class="">
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="padding: 20rpx 0;" >
|
||||
<view @click="Service.GoPage('/pages/goods/goodsDetail')" class="" style="display: flex; align-items: center; justify-content: space-between; " >
|
||||
<view class="" style=" flex: 1; display: flex; align-items: center;" >
|
||||
<image :src="Service.GetMateUrlByImg('/static/dele/dele3.png')" style="width: 150rpx; height: 150rpx; " mode="aspectFit" alt="" />
|
||||
<view class="" style="margin-left: 20rpx;" >
|
||||
<view class="" style="font-weight: 600;" >
|
||||
招牌牛肉面
|
||||
</view>
|
||||
<view class="" style=" margin-top: 15rpx; color: #F97316; font-weight: 600; font-size: 28rpx; " >
|
||||
¥28
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="">
|
||||
<up-icon name="arrow-right" color="#c0c4cc" size="26"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="tag" @click="Service.GoPage('/pages/goods/addGoods')" style=" color: #F97316; margin-top: 20rpx; width: 100%; border-radius: 15rpx; border: 5rpx dashed #F97316; padding: 20rpx; text-align: center; " >
|
||||
<up-icon name="plus" color="#F97316" size="16" :bold='true'></up-icon>
|
||||
<text style="margin-left: 20rpx;" >添加商品</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="width: 100%; height: 200rpx; " >
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="" style="width: 100%; background-color: #fff; position: fixed; bottom: 0; left: 0; padding: 20rpx; ">
|
||||
<up-button shape='circle' color='#FF6B35' text="保存修改"></up-button>
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
<!-- 弹窗 -->
|
||||
<up-popup :show="showDate">
|
||||
<view style="width: 100%; padding: 30rpx; ">
|
||||
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<view class="">
|
||||
|
||||
</view>
|
||||
<up-icon @click="showDate=!showDate" name="close" color="#000" size="18"></up-icon>
|
||||
</view>
|
||||
<up-form labelWidth='180rpx' :labelStyle="{'font-weight':600}" labelPosition="top" :model="storeData" :
|
||||
ref="form1">
|
||||
<up-form-item label="营业时间: ">
|
||||
<view class="" style=" width: 100%; display: grid; grid-template-columns: repeat(4,1fr);">
|
||||
<view v-for="(item,index) in 7" @click="chooseWeek(index)"
|
||||
style=" margin-top: 15rpx; font-size: 32rpx; padding: 10rpx 30rpx;border-radius: 14rpx;"
|
||||
:class="{ tab:weekList.findIndex(item => item == index)===-1,tabActive: weekList.findIndex(item => item == index)!=-1 }"
|
||||
:key="index" class="tag">
|
||||
周{{chinese(index)}}
|
||||
</view>
|
||||
</view>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="开业时间: " labelPosition="left">
|
||||
<view v-if="!openTime" @click="showOpenTime=!showOpenTime" class=""
|
||||
style="display: flex; align-items: center;">
|
||||
<text style="font-size: 26rpx; color: #c0c4cc; margin-right: 10rpx;">请选择开业时间 </text>
|
||||
<up-icon name="arrow-right" color="#c0c4cc" size="12"></up-icon>
|
||||
</view>
|
||||
<view v-else @click="showOpenTime=!showOpenTime" class="">
|
||||
{{openTime}}
|
||||
</view>
|
||||
</up-form-item>
|
||||
<up-form-item label="结业时间: " labelPosition="left">
|
||||
<view v-if="!closeTime" @click="showCloseTime=!showCloseTime" class=""
|
||||
style="display: flex; align-items: center;">
|
||||
<text style="font-size: 26rpx; color: #c0c4cc; margin-right: 10rpx;">请选择开业时间 </text>
|
||||
<up-icon name="arrow-right" color="#c0c4cc" size="12"></up-icon>
|
||||
</view>
|
||||
<view v-else @click="showCloseTime=!showCloseTime" class="">
|
||||
{{closeTime}}
|
||||
</view>
|
||||
</up-form-item>
|
||||
|
||||
</up-form>
|
||||
<view class="" style="margin-top: 20rpx;">
|
||||
<up-button @click="dateconfiom" text="确认"></up-button>
|
||||
</view>
|
||||
<up-datetime-picker @confirm='showOpenTime=!showOpenTime' @cancel="showOpenTime=!showOpenTime"
|
||||
:show="showOpenTime" v-model="openTime" mode="time"></up-datetime-picker>
|
||||
<up-datetime-picker @confirm='showCloseTime=!showCloseTime' @cancel="showCloseTime=!showCloseTime"
|
||||
:show="showCloseTime" v-model="closeTime" mode="time"></up-datetime-picker>
|
||||
|
||||
</view>
|
||||
</up-popup>
|
||||
<up-popup :show="showTag">
|
||||
<view style="width: 100%; padding: 30rpx; ">
|
||||
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<view class="">
|
||||
|
||||
</view>
|
||||
<up-icon @click="showTag=!showTag" name="close" color="#000" size="18"></up-icon>
|
||||
</view>
|
||||
<view class="">
|
||||
<view style="display: flex; align-items: center; justify-content: space-between;" class="">
|
||||
<view class="">
|
||||
店铺标签
|
||||
</view>
|
||||
<view class="">
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="margin: 20rpx 0;">
|
||||
<up-input placeholder="请输入标签" border="bottom" v-model="tag"></up-input>
|
||||
</view>
|
||||
<view class="" style="margin-top: 20rpx;">
|
||||
<up-button @click="addTag()" text="确认"></up-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</up-popup>
|
||||
<!-- 社区 -->
|
||||
<up-picker :show="showCommunity" @confirm="communityFunc" @cancel='showCommunity=!showCommunity' :columns="columns"></up-picker>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { Service } from "@/Service/Service"
|
||||
import { ref } from "vue";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
interface data {
|
||||
photo : string,
|
||||
name : string,
|
||||
address : string,
|
||||
phone : string,
|
||||
time : string,
|
||||
community : string
|
||||
}
|
||||
|
||||
|
||||
|
||||
let storeData = ref<data>({
|
||||
photo: '',
|
||||
name: '',
|
||||
address: '',
|
||||
phone: '',
|
||||
time: '',
|
||||
community: ''
|
||||
})
|
||||
|
||||
|
||||
// 时间
|
||||
let showDate = ref(false)
|
||||
let weekList = ref<Array<number>>([])
|
||||
let openTime = ref()
|
||||
let showOpenTime = ref(false)
|
||||
let closeTime = ref()
|
||||
let showCloseTime = ref(false)
|
||||
// 时间方式
|
||||
let timefunc = ref(-1)
|
||||
|
||||
|
||||
// 标签
|
||||
let showTag = ref(false)
|
||||
let tag = ref()
|
||||
let tagList = ref<Array<string>>([])
|
||||
|
||||
// 社区
|
||||
let showCommunity = ref(false)
|
||||
const columns = ref([
|
||||
['平安街', '镇魂街', '美食街']
|
||||
]);
|
||||
|
||||
onLoad(() => {
|
||||
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
// 社区
|
||||
const communityFunc=(e:any)=>{
|
||||
showCommunity.value=!showCommunity.value
|
||||
storeData.value.community=e.value[0]
|
||||
}
|
||||
|
||||
|
||||
// 标签
|
||||
const showTagfunc = () => {
|
||||
showTag.value = !showTag.value
|
||||
tag.value = ''
|
||||
}
|
||||
|
||||
const addTag = () => {
|
||||
if (!tag.value) {
|
||||
Service.Msg('请输入标签内容')
|
||||
return
|
||||
}
|
||||
tagList.value.push(tag.value)
|
||||
showTag.value = !showTag.value
|
||||
}
|
||||
|
||||
// 选择照片
|
||||
const uploaduserImg = () => {
|
||||
uni.chooseImage({
|
||||
count: 1, // 最多选择1张图片
|
||||
sizeType: ['original', 'compressed'], // 支持原图和压缩图
|
||||
sourceType: ['album', 'camera'], // 可从相册选择或使用相机拍照
|
||||
success: function (res) {
|
||||
storeData.value.photo = res.tempFiles[0].path
|
||||
// let path = res.tempFiles[0].path
|
||||
// Service.uploadH5(path, 'Avatar', data => {
|
||||
// userInfo.value.userPhoto = data.split(',')[2].split(':')[1].split('"')[1]
|
||||
// })
|
||||
},
|
||||
fail: function (err) {
|
||||
console.error('选择失败:', err.errMsg);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 地址
|
||||
const chooseAddress = () => {
|
||||
wx.chooseLocation({
|
||||
success: res => {
|
||||
storeData.value.address = res.address
|
||||
// latitude.value = res.latitude.toString();
|
||||
// longitude.value = res.longitude.toString();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const chooseWeek = (index : number) => {
|
||||
if (weekList.value.findIndex(item => item == index) != -1) {
|
||||
let i = weekList.value.findIndex(item => item == index)
|
||||
weekList.value.splice(i, 1)
|
||||
return
|
||||
}
|
||||
weekList.value.push(index)
|
||||
}
|
||||
|
||||
const chinese = (item : number) => {
|
||||
if (item + 1 == 1) {
|
||||
return '一'
|
||||
}
|
||||
if (item + 1 == 2) {
|
||||
return '二'
|
||||
} if (item + 1 == 3) {
|
||||
return '三'
|
||||
} if (item + 1 == 4) {
|
||||
return '四'
|
||||
} if (item + 1 == 5) {
|
||||
return '五'
|
||||
} if (item + 1 == 6) {
|
||||
return '六'
|
||||
} if (item + 1 == 7) {
|
||||
return '日'
|
||||
}
|
||||
}
|
||||
|
||||
const dateconfiom = () => {
|
||||
storeData.value.time = ''
|
||||
if (weekList.value.length == 0) {
|
||||
Service.Msg('请选择营业时间')
|
||||
return
|
||||
}
|
||||
|
||||
if (!openTime.value) {
|
||||
Service.Msg('请选择开业时间')
|
||||
return
|
||||
}
|
||||
if (!closeTime.value) {
|
||||
Service.Msg('请选择结业时间')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (openTime.value > closeTime.value) {
|
||||
Service.Msg('开业时间不得小于结业时间')
|
||||
return
|
||||
}
|
||||
|
||||
weekList.value.sort((a, b) => {
|
||||
return a - b
|
||||
})
|
||||
|
||||
let timeIndex = weekList.value[0] - 0
|
||||
let judgment = weekList.value.findIndex((item, index) => {
|
||||
return item - index !== timeIndex
|
||||
})
|
||||
|
||||
// 1是至 /0是全显示
|
||||
if (judgment == -1) {
|
||||
timefunc.value = 1
|
||||
} else {
|
||||
timefunc.value = 0
|
||||
}
|
||||
|
||||
if (timefunc.value == 0) {
|
||||
weekList.value.map((item) => {
|
||||
storeData.value.time = storeData.value.time + '周' + chinese(item) + ' '
|
||||
})
|
||||
} else {
|
||||
storeData.value.time = '周' + chinese((weekList.value[0])) + '至' + '周' + chinese((weekList.value[weekList.value.length - 1]))
|
||||
}
|
||||
storeData.value.time = storeData.value.time + ' ' + openTime.value + '-' + closeTime.value
|
||||
|
||||
showDate.value = !showDate.value
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: fit-content;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.tabActive {
|
||||
background-color: var(--nav-mian);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tab {
|
||||
background-color: #F5F5F5;
|
||||
color: #333333
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,531 @@
|
||||
<template>
|
||||
<view class="order-detail-page">
|
||||
<view class="detail-container">
|
||||
<!-- 沉浸式状态栏 -->
|
||||
<view class="status-bar"></view>
|
||||
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<image class="back-icon" src="/static/icons/back.svg" @click="goBack" mode="aspectFit" />
|
||||
<text class="nav-title">订单详情</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
|
||||
<!-- 店铺信息卡片 -->
|
||||
<view class="shop-info-card" @click="goToShop">
|
||||
<view @click="Service.GoPage('/pages/community/merchantDetail?merchId='+merchInfo.merchId)"
|
||||
class="shop-header">
|
||||
<image class="shop-avatar" :src="Service.GetMateUrlByImg(merchInfo.logo)" mode="aspectFill" />
|
||||
<view class="shop-info">
|
||||
<text class="shop-name">{{ merchInfo.name }}</text>
|
||||
<text class="order-time">{{ Service.formatDate(merchInfo.addTime,2) }}</text>
|
||||
</view>
|
||||
|
||||
<up-icon name="arrow-right" bold="true" size="22"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单信息卡片 -->
|
||||
<view class="order-info-card">
|
||||
<view class="card-title">
|
||||
<text class="title-icon">📋</text>
|
||||
<text class="title-text">订单信息</text>
|
||||
</view>
|
||||
|
||||
<view class="info-section">
|
||||
<view class="info-row">
|
||||
<text class="info-label">订单号</text>
|
||||
<text class="info-value">{{ orderInfo.orderId }}</text>
|
||||
</view>
|
||||
|
||||
<view class="info-row">
|
||||
<text class="info-label">订单金额</text>
|
||||
<text class="info-value amount">¥{{ orderInfo.amount }}</text>
|
||||
</view>
|
||||
|
||||
<view class="info-row">
|
||||
<text class="info-label">订单状态</text>
|
||||
<view class="status-tag .status-completed">
|
||||
<text class="status-tag-text">已完成</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="orderInfo.getIntegral > 0" class="info-row">
|
||||
<text class="info-label">获得积分</text>
|
||||
<view class="points-badge earned">
|
||||
<text class="ri-coin-line points-icon"></text>
|
||||
<text class="points-text">+{{ orderInfo.getIntegral }}分</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="orderInfo.useIntegral > 0" class="info-row">
|
||||
<text class="info-label">使用积分</text>
|
||||
<view class="points-badge used">
|
||||
<text class="ri-wallet-3-line points-icon"></text>
|
||||
<text class="points-text">-{{ orderInfo.useIntegral }}分</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="orderInfo.discount" class="info-row">
|
||||
<text class="info-label">优惠券</text>
|
||||
<view class="coupon-badge">
|
||||
<text
|
||||
class="coupon-text">{{ '满'+JSON.parse(orderInfo.discount).needMoney+'减'+JSON.parse(orderInfo.discount).deductMoney }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="info-row">
|
||||
<text class="info-label">付款金额</text>
|
||||
<text class="info-value amount">¥{{ orderInfo.payAmount }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">支付方式</text>
|
||||
<text class="info-value">{{ orderInfo.payway=='wx'?'微信':'支付宝' }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">下单时间</text>
|
||||
<text class="info-value">{{ orderInfo.payTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<!-- <view v-if="order.statusType === 'completed'" class="bottom-actions">
|
||||
<button class="action-btn secondary" @click="contactService">
|
||||
<text class="ri-customer-service-2-line btn-icon"></text>
|
||||
<text class="btn-text">联系客服</text>
|
||||
</button>
|
||||
<button class="action-btn primary" @click="writeReview">
|
||||
<text class="ri-edit-line btn-icon"></text>
|
||||
<text class="btn-text">评价订单</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view v-else-if="order.statusType === 'pending'" class="bottom-actions">
|
||||
<button class="action-btn secondary" @click="contactService">
|
||||
<text class="ri-customer-service-2-line btn-icon"></text>
|
||||
<text class="btn-text">联系客服</text>
|
||||
</button>
|
||||
<button class="action-btn primary" @click="writeReview">
|
||||
<text class="ri-star-line btn-icon"></text>
|
||||
<text class="btn-text">评价订单</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view v-else-if="order.statusType === 'refunding'" class="bottom-actions">
|
||||
<button class="action-btn warning" @click="checkRefundStatus">
|
||||
<text class="ri-time-line btn-icon"></text>
|
||||
<text class="btn-text">查看退款进度</text>
|
||||
</button>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<!-- <view v-else class="loading">
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ref
|
||||
} from 'vue'
|
||||
import {
|
||||
onLoad
|
||||
} from '@dcloudio/uni-app'
|
||||
import {
|
||||
vpOrderService,
|
||||
Service
|
||||
} from '@/Service/vp/vpOrderService'
|
||||
|
||||
|
||||
|
||||
let merchInfo = ref<any>({})
|
||||
let orderInfo = ref<any>({})
|
||||
let orderId = ref('')
|
||||
// 页面加载
|
||||
onLoad((options) => {
|
||||
if (options.orderId) {
|
||||
orderId.value = options.orderId
|
||||
getData()
|
||||
}
|
||||
})
|
||||
|
||||
const getData = () => {
|
||||
vpOrderService.GetOrderInfo(orderId.value).then(res => {
|
||||
if (res.code == 0) {
|
||||
merchInfo.value = res.data.merchInfo
|
||||
orderInfo.value = res.data.orderInfo
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 跳转到商家详情
|
||||
const goToShop = () => {
|
||||
if (!order.value) return
|
||||
// 使用商家ID跳转,这里使用order.id作为商家ID
|
||||
uni.navigateTo({
|
||||
url: `/pages/shop/shop?id=${order.value.id}`
|
||||
})
|
||||
}
|
||||
|
||||
// 返回
|
||||
const goBack = () => {
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
})
|
||||
}
|
||||
|
||||
// 联系客服
|
||||
const contactService = () => {
|
||||
uni.showToast({
|
||||
title: '正在联系客服...',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 评价订单
|
||||
const writeReview = () => {
|
||||
uni.showToast({
|
||||
title: '跳转到评价页面',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 查看退款进度
|
||||
const checkRefundStatus = () => {
|
||||
uni.showToast({
|
||||
title: '退款处理中,请耐心等待',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-detail-page {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
.detail-container {
|
||||
padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
/* 状态栏 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 50rpx 24rpx 20rpx;
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.nav-placeholder {
|
||||
width: 48rpx;
|
||||
}
|
||||
|
||||
/* 店铺信息卡片 */
|
||||
.shop-info-card {
|
||||
background: #FFFFFF;
|
||||
margin: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.shop-info-card:active {
|
||||
transform: scale(0.98);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.shop-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.shop-arrow {
|
||||
font-size: 32rpx;
|
||||
color: #CCCCCC;
|
||||
margin-left: auto;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.shop-avatar {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 16rpx;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.order-time {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.order-status-badge {
|
||||
padding: 12rpx 24rpx;
|
||||
border-radius: 24rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.order-status-badge.status-completed {
|
||||
background: #E8F5E9;
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.order-status-badge.status-pending {
|
||||
background: #FFF4E6;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
.order-status-badge.status-refunding {
|
||||
background: #E3F2FD;
|
||||
color: #2196F3;
|
||||
}
|
||||
|
||||
.order-status-badge.status-refunded {
|
||||
background: #FFEBEE;
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
/* 订单信息卡片 */
|
||||
.order-info-card {
|
||||
background: #FFFFFF;
|
||||
margin: 0 20rpx 20rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
margin-bottom: 24rpx;
|
||||
padding-bottom: 16rpx;
|
||||
border-bottom: 1rpx solid #F5F5F5;
|
||||
}
|
||||
|
||||
.title-icon {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 26rpx;
|
||||
color: #222222;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.info-value.amount {
|
||||
font-size: 36rpx;
|
||||
color: #FF6B00;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-tag.status-completed {
|
||||
background: #E8F5E9;
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.status-tag.status-pending {
|
||||
background: #FFF4E6;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
.status-tag.status-refunding {
|
||||
background: #E3F2FD;
|
||||
color: #2196F3;
|
||||
}
|
||||
|
||||
.status-tag.status-refunded {
|
||||
background: #FFEBEE;
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
/* 积分徽章 */
|
||||
.points-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.points-badge.earned {
|
||||
background: #E8F5E9;
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.points-badge.used {
|
||||
background: #FFF4E6;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
.points-icon {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.points-text {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
/* 优惠券徽章 */
|
||||
.coupon-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
background: linear-gradient(135deg, #FFF4E6, #FFE0B2);
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.coupon-icon {
|
||||
font-size: 20rpx;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
.coupon-text {
|
||||
font-size: 24rpx;
|
||||
color: #FF6B00;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 底部操作栏 */
|
||||
.bottom-actions {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #FFFFFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16rpx 20rpx;
|
||||
padding-bottom: calc(16rpx + env(safe-area-inset-bottom));
|
||||
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.action-btn.secondary {
|
||||
background: linear-gradient(135deg, #F5F5F5, #EEEEEE);
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.action-btn.primary {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.action-btn.warning {
|
||||
background: linear-gradient(135deg, #2196F3, #42A5F5);
|
||||
color: #FFFFFF;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 26rpx;
|
||||
color: #999999;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
## 1.1.7(2024-10-29)
|
||||
修复底部露出部分组件bug
|
||||
## 1.1.6(2024-08-20)
|
||||
更新本地数据源为最新数据源
|
||||
## 1.1.5(2024-06-12)
|
||||
使用说明文档优化
|
||||
## 1.1.4(2024-06-12)
|
||||
增加问题反馈描述
|
||||
## 1.1.3(2024-02-29)
|
||||
更新使用文档
|
||||
## 1.1.2(2024-01-16)
|
||||
解决Vue3项目导入导出报错问题
|
||||
## 1.1.1(2023-12-06)
|
||||
defaultValue可以传入defaultValue:['河北省','唐山市','丰南区']数组类型以及defaultValue: '420103'地区编码字符串类型
|
||||
## 1.1.0(2023-12-05)
|
||||
即默认值传入地区编码,也支持传入中文省市区数组
|
||||
## 1.0.9(2023-12-04)
|
||||
优化
|
||||
## 1.0.8(2023-10-24)
|
||||
修复东菀市和中山市下各镇的行政编码错误问题
|
||||
## 1.0.4(2023-09-15)
|
||||
改为uni_modules规范
|
||||
1039
.svn/pristine/1c/1cf1e550652211b82161ecb824f0ca915fab7846.svn-base
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Service } from '@/Service/Service';
|
||||
/*****积分商城*****/
|
||||
class vpGoodsService {
|
||||
private static GetGoodsListPath : string = '/Goods/GetGoodsList';
|
||||
/*****获取商品列表*****/
|
||||
static GetGoodsList(typeId:string,page:number) {
|
||||
var result = Service.Request(this.GetGoodsListPath, 'GET', {typeId,page});
|
||||
return result;
|
||||
}
|
||||
|
||||
private static GetGoodsInfoPath : string = '/Goods/GetGoodsInfo';
|
||||
/*****获取商品详情*****/
|
||||
static GetGoodsInfo(goodsId:string) {
|
||||
var result = Service.Request(this.GetGoodsInfoPath, 'GET', {goodsId});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static AddGoodsOrderPath : string = '/Goods/AddGoodsOrder';
|
||||
/*****生成订单*****/
|
||||
static AddGoodsOrder(goodsId:string,count:number) {
|
||||
var result = Service.Request(this.AddGoodsOrderPath, 'POST', {goodsId,count});
|
||||
return result;
|
||||
}
|
||||
|
||||
private static GetOrderInfoPath : string = '/Goods/GetOrderInfo';
|
||||
/*****订单详情*****/
|
||||
static GetOrderInfo(orderId:string) {
|
||||
var result = Service.Request(this.GetOrderInfoPath, 'GET', {orderId});
|
||||
return result;
|
||||
}
|
||||
|
||||
private static UpdateOrderAddressPath : string = '/Goods/UpdateOrderAddress';
|
||||
/*****修改订单地址*****/
|
||||
static UpdateOrderAddress(orderId:string,addressId:string) {
|
||||
var result = Service.Request(this.UpdateOrderAddressPath, 'POST', {orderId,addressId});
|
||||
return result;
|
||||
}
|
||||
|
||||
private static PayOrderPath : string = '/Goods/PayOrder';
|
||||
/*****支付订单*****/
|
||||
static PayOrder(orderId:string) {
|
||||
var result = Service.Request(this.PayOrderPath, 'POST', {orderId});
|
||||
return result;
|
||||
}
|
||||
|
||||
private static GetUserOrderListPath : string = '/Goods/GetUserOrderList';
|
||||
/*****获取用户订单列表*****/
|
||||
static GetUserOrderList(state:string,page:number) {
|
||||
var result = Service.Request(this.GetUserOrderListPath, 'GET', {state,page});
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
export { Service, vpGoodsService };
|
||||
|
After Width: | Height: | Size: 405 B |
@@ -0,0 +1,29 @@
|
||||
import { Service } from '@/Service/Service';
|
||||
/*****社区*****/
|
||||
class vpMerchService {
|
||||
private static GetMerchListPath: string = '/Merch/GetMerchList';
|
||||
/*****商家列表*****/
|
||||
static GetMerchList(assId: string,comId:string,lon:number,lat:number,page:number) {
|
||||
var result = Service.Request(this.GetMerchListPath, 'GET', {assId,comId,lon,lat,page});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static GetMerchInfoPath: string = '/Merch/GetMerchInfo';
|
||||
/*****店铺详情*****/
|
||||
static GetMerchInfo(merchId: string,page:number) {
|
||||
var result = Service.Request(this.GetMerchInfoPath, 'GET', {merchId,page});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static GetGoodsInfoPath: string = '/Merch/GetGoodsInfo';
|
||||
/*****商品详情*****/
|
||||
static GetGoodsInfo(goodsId: string,page:number) {
|
||||
var result = Service.Request(this.GetGoodsInfoPath, 'GET', {goodsId,page});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
export { Service, vpMerchService };
|
||||
1097
.svn/pristine/1e/1ec38289d3278199a61db07aadd5ff06132f3dc1.svn-base
Normal file
@@ -0,0 +1,312 @@
|
||||
<template>
|
||||
<view class="favorites-page">
|
||||
<!-- 沉浸式状态栏 -->
|
||||
<view v-if="!isZFB" class="status-bar"></view>
|
||||
|
||||
<!-- 顶部导航栏 -->
|
||||
<view v-if="!isZFB" class="nav-bar">
|
||||
<image class="back-icon" src="/static/icons/back.svg" @click="goBack" mode="aspectFit" />
|
||||
<text class="nav-title">我的收藏</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
|
||||
<!-- 收藏列表 -->
|
||||
<view class="content">
|
||||
<view v-if="collectList.length > 0" class="favorites-list">
|
||||
<view v-for="item in collectList" :key="item.merchId" class="shop-card" @click="Service.GoPage('/pages/community/merchantDetail?merchId='+item.merchId)">
|
||||
<!-- 店铺图片 -->
|
||||
<image class="shop-image" :src="Service.GetMateUrlByImg(item.logo)" mode="aspectFill" />
|
||||
|
||||
<!-- 店铺信息 -->
|
||||
<view class="shop-info">
|
||||
<text class="shop-name">{{ item.name }}</text>
|
||||
<view class="shop-meta">
|
||||
<text class="ri-star-fill rating-icon"></text>
|
||||
<text class="rating-text">{{ Number(item.score ).toFixed(1)}}分</text>
|
||||
<text class="sales-text">月售{{ item.sale }}</text>
|
||||
</view>
|
||||
<view class="shop-tags">
|
||||
<view v-for="(coupon, idx) in item.tips" :key="idx" :class="getTagClass(coupon)" style="font-size: 24rpx;"
|
||||
class="shop-tag">
|
||||
{{ coupon }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 取消收藏按钮 -->
|
||||
<view class="cancel-btn" @click.stop="cancelFavorite(item)">
|
||||
<up-icon name="heart-fill" color="#fc5151" :bold="true" size="18"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
<up-loadmore :status="status" />
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-else class="empty-state">
|
||||
<text class="ri-heart-line empty-icon"></text>
|
||||
<text class="empty-text">暂无收藏店铺</text>
|
||||
<text class="empty-desc">去首页逛逛,收藏喜欢的店铺吧</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {onShow,onLoad,onReachBottom} from "@dcloudio/uni-app";
|
||||
import {Service} from "@/Service/Service"
|
||||
import { ref, computed } from "vue";
|
||||
import { vpDiscountService } from "@/Service/vp/vpDiscountService"
|
||||
import { vpUserService } from "@/Service/vp/vpUserService"
|
||||
import { inject } from 'vue';
|
||||
const isZFB = inject('isZFB');
|
||||
|
||||
let collectList = ref<Array<any>>([])
|
||||
let status = ref<string>('loadmore')
|
||||
let pageNo = ref<number>(1)
|
||||
let longitude=ref(0)
|
||||
let latitude=ref(0)
|
||||
|
||||
|
||||
onLoad(()=>{
|
||||
getLocation()
|
||||
})
|
||||
onReachBottom(()=>{
|
||||
getList()
|
||||
})
|
||||
|
||||
// 返回
|
||||
const goBack = () => {
|
||||
Service.GoPageBack()
|
||||
}
|
||||
|
||||
|
||||
const getLocation = () => {
|
||||
uni.getLocation({
|
||||
type: 'wgs84',
|
||||
success: function (res) {
|
||||
longitude.value = res.longitude
|
||||
latitude.value = res.latitude
|
||||
getData()
|
||||
},
|
||||
fail: function (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const getData=()=>{
|
||||
pageNo.value=1
|
||||
status.value='loadmore'
|
||||
collectList.value=[]
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList=()=>{
|
||||
if (status.value !== 'loadmore') {
|
||||
return
|
||||
}
|
||||
status.value = 'loading'
|
||||
vpUserService.GetUserCollectList(longitude.value,latitude.value,pageNo.value).then(res=>{
|
||||
if(res.code==0){
|
||||
collectList.value=[...collectList.value,...res.data.collectList]
|
||||
status.value=res.data.collectList.length==10?'loadmore':'nomore'
|
||||
pageNo.value++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 根据标签文本获取样式类
|
||||
const getTagClass = (tagText : string) => {
|
||||
const tagMap = {
|
||||
'可用积分': 'tag-points-available',
|
||||
'可用券': 'tag-coupon'
|
||||
}
|
||||
return tagMap[tagText] || 'tag-points'
|
||||
}
|
||||
|
||||
|
||||
// 取消收藏
|
||||
const cancelFavorite = (item:any) => {
|
||||
uni.showModal({
|
||||
title: '取消收藏',
|
||||
content: `确定取消收藏吗?`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
vpUserService.CollectMerch(item.merchId).then(res=>{
|
||||
if(res.code==0){
|
||||
getData()
|
||||
}else{
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.favorites-page {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
.action-pill-icon {
|
||||
font-size: 28rpx;
|
||||
color: #FF6B00;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 状态栏 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 60rpx 24rpx 20rpx 24rpx;
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.nav-placeholder {
|
||||
width: 48rpx;
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.content {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
/* 收藏列表 */
|
||||
.favorites-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.shop-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.shop-image {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 12rpx;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.shop-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.rating-icon {
|
||||
font-size: 24rpx;
|
||||
color: #FFB800;
|
||||
}
|
||||
|
||||
.rating-text {
|
||||
font-size: 24rpx;
|
||||
color: #FF6B00;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.sales-text {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.shop-tags {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 20rpx;
|
||||
color: #666666;
|
||||
background: #F5F5F5;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background: #FFEBEE;
|
||||
border-radius: 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cancel-icon {
|
||||
font-size: 32rpx;
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 200rpx 0;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 120rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.empty-desc {
|
||||
font-size: 24rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,231 @@
|
||||
<template>
|
||||
<view style="display: flex; flex-direction: column; height: 100vh; ">
|
||||
<view class="merchant-info">
|
||||
<view class="">
|
||||
<text class="section-title">付款给商户</text>
|
||||
<text class="merchant-name">星巴克 · 北京国贸店</text>
|
||||
</view>
|
||||
<image :src="Service.GetMateUrlByImg('/static/dele/dele4.jpg')" mode="aspectFill" class="merchant-icon">
|
||||
</image>
|
||||
</view>
|
||||
|
||||
<view class=""
|
||||
style=" padding: 30rpx 40rpx; flex: 1; background-color: #fff; width: 100%; border-top-right-radius: 30rpx; border-top-left-radius: 30rpx; ">
|
||||
<view class="" style="font-size: 24rpx; font-weight: 600;">
|
||||
余额
|
||||
</view>
|
||||
<view class="" style="margin: 20rpx 0; padding: 20rpx 0; border-bottom: 1rpx solid #e2e2e2; ">
|
||||
<!-- <up-input prefixIcon='rmb' :prefixIconStyle="{ 'color':'#000','font-weight': 600,'font-size':'60rpx' }"
|
||||
fontSize='50rpx'
|
||||
auto-blur="false"
|
||||
@focus="focusFunc"
|
||||
:customStyle="{'color':'#000', height: '90rpx', 'padding-left': 0, 'font-weight': 600,'padding-bottom':'20rpx' }"
|
||||
border="bottom" v-model="account"></up-input> -->
|
||||
<view class="" style="display: flex; align-items: center; width: 100%; " >
|
||||
<view class="" style="height: 70rpx; display: flex; align-items: center; " >
|
||||
<up-icon name="rmb" bold='true' size='50rpx' color="#000" ></up-icon>
|
||||
</view>
|
||||
<view class="" style=" height: 70rpx; line-height: 70rpx; font-weight: 600;font-size: 70rpx; " >
|
||||
{{account}}
|
||||
</view>
|
||||
<view class="" v-if="isShow" style="margin: 0 10rpx; width: 4rpx; height: 70rpx; background-color: var(--nav-mian); " >
|
||||
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
</view>
|
||||
<view class="" style="font-size: 24rpx; color: #6B6B6B; ">
|
||||
<text>我的积分 1,250 积分</text>
|
||||
<text style="margin-left: 10rpx;">可抵扣 ¥12.50</text>
|
||||
</view>
|
||||
<view class="" style="font-size: 28rpx; margin-top: 10rpx; ">
|
||||
<text :style="{'margin-right':!des?'':'15rpx'}">{{des}}</text>
|
||||
<text @click="showDes=true" style="font-weight: 600; color: #586B95;">添加备注</text>
|
||||
</view>
|
||||
<view class=""
|
||||
style="background-color: #f5f5f5; padding: 20rpx; position: fixed; bottom: 0; left: 0; width: 100%; padding-top: 25rpx; padding-bottom: 20rpx; ">
|
||||
<view class="" style="display: grid; grid-template-columns: repeat(4,1fr); ">
|
||||
<view class="button" @click="input(item)" v-for="(item,index) in 3" :key="index">
|
||||
{{item}}
|
||||
</view>
|
||||
<view @click="deleInput()" class="button">
|
||||
<up-icon name="backspace" :bold='true' size="26"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="display: grid; grid-template-columns: 3fr 1fr; ">
|
||||
<view class="">
|
||||
<view class="" style="display: grid; grid-template-columns: repeat(3,1fr); ">
|
||||
<view class="button" @click="input(item+3)" v-for="(item,index) in 3" :key="index">
|
||||
{{item+3}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="display: grid; grid-template-columns: repeat(3,1fr); ">
|
||||
<view class="button" @click="input(item+6)" v-for="(item,index) in 3" :key="index">
|
||||
{{item+6}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="display: grid; grid-template-columns: 2fr 1fr; ">
|
||||
<view @click="input(0)" class="button" >
|
||||
0
|
||||
</view>
|
||||
<view @click="input('.')" class="button" >
|
||||
.
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view @click="save()" class="button" style="background-color: var(--nav-mian); color: #fff; " >
|
||||
付款
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
|
||||
<up-popup :show="showDes">
|
||||
<view style="width: 100%; padding: 50rpx 30rpx; ">
|
||||
<view class="">
|
||||
<text style="font-size: 28rpx; font-weight: 600;">添加备注</text>
|
||||
</view>
|
||||
<view class=""
|
||||
style=" margin-top: 30rpx; padding: 20rpx 0; border-bottom: 1rpx solid #e2e2e2; border-top: 1rpx solid #e2e2e2; ">
|
||||
<up-input placeholder="请输入内容" border="none" v-model="des"></up-input>
|
||||
</view>
|
||||
|
||||
<view class=""
|
||||
style=" margin: 0 110rpx; margin-top: 50rpx; display: flex; align-items: center; justify-content: space-between; ">
|
||||
<view class="" @click="showDes=false,des=''"
|
||||
style=" background-color: #f2f2f2; color: #000; padding: 20rpx 80rpx;border-radius: 20rpx; display: flex; align-items: center; justify-content: center; ">
|
||||
取消
|
||||
</view>
|
||||
<view class="" @click="showDes=false"
|
||||
style=" background-color: #07c160; color: #fff; padding: 20rpx 80rpx;border-radius: 20rpx; display: flex; align-items: center; justify-content: center; ">
|
||||
确定
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</up-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { Service } from "@/Service/Service"
|
||||
import { onUnmounted, ref } from "vue";
|
||||
|
||||
|
||||
let account = ref('')
|
||||
let showDes = ref(false)
|
||||
let des = ref()
|
||||
let isShow=ref(false)
|
||||
let timeOut=ref()
|
||||
|
||||
onLoad(() => {
|
||||
focusFunc()
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
|
||||
onUnmounted(()=>{
|
||||
clearInterval(timeOut.value)
|
||||
})
|
||||
|
||||
|
||||
const input=(val:any)=>{
|
||||
|
||||
if(account.value.split('').length>8){
|
||||
return
|
||||
}
|
||||
|
||||
if(val=='.'){
|
||||
let arr=account.value.split('').filter((item=>item==val))
|
||||
if(arr.length>0){
|
||||
return
|
||||
}
|
||||
|
||||
if(!account.value){
|
||||
account.value='0.'
|
||||
return
|
||||
}
|
||||
}
|
||||
account.value=account.value+val
|
||||
|
||||
|
||||
|
||||
}
|
||||
const deleInput=()=>{
|
||||
let arr=account.value.split('')
|
||||
arr.pop()
|
||||
account.value=arr.join('')
|
||||
}
|
||||
|
||||
const save=()=>{
|
||||
|
||||
}
|
||||
|
||||
const focusFunc=()=>{
|
||||
timeOut.value=setInterval(()=>{
|
||||
isShow.value=!isShow.value
|
||||
},1000)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
// 按键
|
||||
.button {
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 18rpx 0;
|
||||
border-radius: 10rpx;
|
||||
font-weight: 700;
|
||||
margin: 8rpx;
|
||||
font-size: 38rpx;
|
||||
}
|
||||
|
||||
.button:active {
|
||||
background-color: #ababab;
|
||||
}
|
||||
|
||||
/* 商户信息 */
|
||||
.merchant-info {
|
||||
padding: 30rpx 40rpx;
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.merchant-name {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
display: block;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
|
||||
.merchant-icon {
|
||||
width: 95rpx;
|
||||
height: 95rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Service } from '@/Service/Service';
|
||||
|
||||
|
||||
class vpLoginService {
|
||||
private static GetOpenIdByWeixinPath: string = '/Login/GetOpenIdByWeixin';
|
||||
/*****获取openid*****/
|
||||
static GetOpenIdByWeixin(code: string,type:number) {
|
||||
var result = Service.Request(this.GetOpenIdByWeixinPath, 'GET', {code,type});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static WxLoginPath: string = '/Login/WxLogin';
|
||||
/*****获取openid*****/
|
||||
static WxLogin(code: string,type:number,lon:number,lat:number) {
|
||||
var result = Service.Request(this.WxLoginPath, 'GET', {code,type,lon,lat});
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export { vpLoginService }
|
||||
@@ -0,0 +1,993 @@
|
||||
<template>
|
||||
<view class="category-page">
|
||||
<!-- 页面加载中 - 显示骨架屏 -->
|
||||
<view v-if="shopLoading" class="page-loading-wrapper">
|
||||
<!-- 顶部导航栏骨架屏 -->
|
||||
<view v-if="!isZFB" class="nav-bar">
|
||||
<view class="nav-content">
|
||||
<view class="skeleton-nav-back"></view>
|
||||
<view class="skeleton-nav-title"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索框骨架屏 -->
|
||||
<view class="search-section">
|
||||
<view class="search-box-skeleton"></view>
|
||||
</view>
|
||||
|
||||
<!-- 分类标签栏骨架屏 -->
|
||||
<view class="category-tabs">
|
||||
<view class="skeleton-tabs-list">
|
||||
<view class="skeleton-tab-item" v-for="i in 5" :key="i">
|
||||
<view class="skeleton-tab-icon"></view>
|
||||
<view class="skeleton-tab-text"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 筛选栏骨架屏 -->
|
||||
<view class="filter-section">
|
||||
<view class="skeleton-filter-row">
|
||||
<view class="skeleton-filter-sort"></view>
|
||||
<view class="skeleton-filter-all"></view>
|
||||
</view>
|
||||
<view class="skeleton-quick-filters">
|
||||
<view class="skeleton-quick-filter-item" v-for="i in 4" :key="i"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商家列表骨架屏 -->
|
||||
<view class="shop-list">
|
||||
<SkeletonShopCardHorizontal v-for="i in 5" :key="i" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 页面加载完成 - 显示实际内容 -->
|
||||
<view v-else>
|
||||
<!-- 顶部导航栏 - 橙色背景 -->
|
||||
<view v-if="!isZFB" class="nav-bar">
|
||||
<view class="nav-content">
|
||||
<view class="nav-back" @click="Service.GoPageBack()">
|
||||
<image class="back-icon" src="/static/icons/back.svg" mode="aspectFit" />
|
||||
</view>
|
||||
<text class="nav-title">{{ pageTitle }}</text>
|
||||
<view class="" style="width: 64rpx; height: 64rpx;">
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索框及内容区域 -->
|
||||
<view class="content-wrapper" :style="{'margin-top':isZFB?'0':'-85px'}" >
|
||||
<!-- 固定搜索框 -->
|
||||
<view class="search-fixed" :class="{ show: isSearchFixed }">
|
||||
<view class="search-box">
|
||||
<image class="search-icon" src="https://img.icons8.com/ios-glyphs/30/999999/search--v1.png"
|
||||
mode="aspectFit" />
|
||||
<input v-model="search" @blur="getdata()" class="search-input" placeholder="请输入商家或商品名称"
|
||||
placeholder-class="search-placeholder" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 原搜索框占位 -->
|
||||
<view class="search-section">
|
||||
<view class="search-box">
|
||||
<image class="search-icon" src="https://img.icons8.com/ios-glyphs/30/999999/search--v1.png"
|
||||
mode="aspectFit" />
|
||||
<input @confirm="getdata()" v-model="search" class="search-input" placeholder="请输入商家或商品名称"
|
||||
placeholder-class="search-placeholder" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 筛选栏 -->
|
||||
<view class="filter-section">
|
||||
<!-- 排序选项 -->
|
||||
<view class="filter-row">
|
||||
<view class="filter-sort" @click="toggleSortDropdown">
|
||||
<text class="filter-label">{{ currentSort }}</text>
|
||||
<text class="ri-arrow-down-s-line arrow-icon" :class="{ rotate: showSortDropdown }"></text>
|
||||
<!-- 排序下拉菜单 -->
|
||||
<view v-if="showSortDropdown" class="dropdown-menu sort-dropdown">
|
||||
<view v-for="(option, index) in sortOptions" :key="index" class="dropdown-item"
|
||||
@click.stop="selectSort(index)">
|
||||
<text class="dropdown-text">{{ option }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="filter-all" @click="toggleFilterDropdown">
|
||||
<text class="filter-all-text">{{ currentFilter }}</text>
|
||||
<text class="ri-arrow-down-s-line filter-all-arrow"
|
||||
:class="{ rotate: showFilterDropdown }"></text>
|
||||
<!-- 筛选下拉菜单 -->
|
||||
<view v-if="showFilterDropdown" class="dropdown-menu filter-dropdown">
|
||||
<view v-for="(option, index) in filterOptions" :key="index" class="dropdown-item"
|
||||
@click.stop="selectFilter(index)">
|
||||
<text class="dropdown-text">{{ option }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快捷筛选按钮 -->
|
||||
<!-- <scroll-view scroll-x class="quick-filters" show-scrollbar="false">
|
||||
<view class="quick-filter-list">
|
||||
<view v-for="(filter, index) in quickFilters" :key="index" class="quick-filter-item"
|
||||
:class="{ active: filter.active }" @click="toggleFilter(index)">
|
||||
<text class="filter-text">{{ filter.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view> -->
|
||||
</view>
|
||||
|
||||
<!-- 商家列表 -->
|
||||
<view class="shop-list">
|
||||
<view v-for="shop in merchList" :key="shop.merchId" class="shop-card"
|
||||
@click="Service.GoPage('/pages/community/merchantDetail?merchId='+shop.merchId)">
|
||||
<!-- 左侧图片 -->
|
||||
<image class="shop-image" :src="Service.GetMateUrlByImg(shop.logo)" mode="aspectFill" />
|
||||
|
||||
<!-- 右侧信息 -->
|
||||
<view class="shop-info">
|
||||
<!-- 店名 -->
|
||||
<view class="shop-header">
|
||||
<text class="shop-name">{{ shop.name }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 评分和销量 -->
|
||||
<view class="shop-info-left">
|
||||
<view class="shop-rating">
|
||||
<text class="rating-score">{{ Number(shop.score).toFixed(1) }}</text>
|
||||
<text class="rating-unit">分</text>
|
||||
</view>
|
||||
<text class="shop-sales">月售{{ shop.sale }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 距离、用时、人均价格 - 同一行 -->
|
||||
<view class="shop-info-row">
|
||||
<!-- 距离和用时 - 左侧 -->
|
||||
<view class="shop-info-left">
|
||||
<view class="distance-wrapper">
|
||||
<up-icon name="map" color="#000" size="12"></up-icon>
|
||||
<text class="distance-text">{{ formatDistance(shop.distance) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 人均价格 - 右侧 -->
|
||||
<view v-if="shop.avgPrice" class="shop-avg-price-inline">
|
||||
<text class="avg-price-small">¥</text>
|
||||
<text class="avg-price-value-small">{{ shop.price }}</text>
|
||||
<text class="avg-price-text-small">人均</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部标签 -->
|
||||
<view class="shop-bottom-tags">
|
||||
<text v-for="(tag, index) in shop.tips" :key="index" class="shop-tag"
|
||||
style="font-size: 24rpx; margin-left: 4rpx;" :class="getTagClass(tag)">
|
||||
{{ tag }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 右上角标签 -->
|
||||
<view class="shop-badges" style="top: 24rpx;">
|
||||
<text v-if="shop.code=='Discounts'" class="badge-partner"
|
||||
style="font-size: 24rpx;">联盟商家</text>
|
||||
<text v-else class="badge-coop" style="font-size: 24rpx;">合作商家</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 空状态 -->
|
||||
<view v-if="merchList.length== 0" class="no-results" style="text-align: center;">
|
||||
<text class="no-results-text">暂无商家</text>
|
||||
</view>
|
||||
<up-loadmore v-else :status="status" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ref,
|
||||
computed
|
||||
} from 'vue'
|
||||
import {
|
||||
onLoad,
|
||||
onPageScroll,
|
||||
onReachBottom
|
||||
} from '@dcloudio/uni-app'
|
||||
import SkeletonShopCardHorizontal from '../../components/skeleton/skeleton-shop-card-horizontal.vue'
|
||||
import { Service } from "@/Service/Service"
|
||||
import { vpMerchService } from '@/Service/vp/vpMerchService'
|
||||
import { inject } from 'vue';
|
||||
const isZFB = inject('isZFB');
|
||||
// 页面数据
|
||||
const pageTitle = ref('')
|
||||
|
||||
// 固定搜索框状态
|
||||
const isSearchFixed = ref(false)
|
||||
const categoryType = ref('food')
|
||||
const shops = ref<any>([])
|
||||
const currentCategory = ref(0)
|
||||
|
||||
// 加载状态
|
||||
const shopLoading = ref(true)
|
||||
|
||||
// 下拉菜单状态
|
||||
const showSortDropdown = ref(false)
|
||||
const showFilterDropdown = ref(false)
|
||||
|
||||
// 当前选择的排序和筛选
|
||||
let currentSort = ref('距离最近')
|
||||
let sortIndex = ref(0)
|
||||
let currentFilter = ref('全部')
|
||||
let filterIndex = ref(0)
|
||||
|
||||
// 排序选项
|
||||
const sortOptions = ['距离最近', '销量最高', '评分最高']
|
||||
|
||||
// 筛选选项
|
||||
const filterOptions = ['全部', '联盟商家', '可用积分', '可用券', '合作商家']
|
||||
|
||||
|
||||
|
||||
// 快捷筛选
|
||||
const quickFilters = ref([{
|
||||
name: '15分钟',
|
||||
active: false
|
||||
},
|
||||
{
|
||||
name: '品质必点',
|
||||
active: false
|
||||
},
|
||||
{
|
||||
name: '堂食店',
|
||||
active: false
|
||||
},
|
||||
{
|
||||
name: '联盟商家',
|
||||
active: true
|
||||
}
|
||||
])
|
||||
|
||||
let search = ref('')
|
||||
let longitude = ref(0)
|
||||
let latitude = ref(0)
|
||||
let merchList = ref<Array<any>>([])
|
||||
let status = ref('nomore')
|
||||
let page = ref(1)
|
||||
// 页面加载
|
||||
onLoad((options : any) => {
|
||||
// 接收从首页传递的分类类型
|
||||
if (options.name) {
|
||||
pageTitle.value = options.name
|
||||
categoryType.value = options.type
|
||||
getLocation()
|
||||
}
|
||||
})
|
||||
|
||||
onReachBottom(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
// 页面滚动监听 - 控制固定搜索框显示
|
||||
onPageScroll((e) => {
|
||||
// 当滚动超过100px时显示固定搜索框
|
||||
isSearchFixed.value = e.scrollTop > 100
|
||||
})
|
||||
|
||||
const getLocation = () => {
|
||||
uni.getLocation({
|
||||
type: 'wgs84',
|
||||
success: function (res) {
|
||||
longitude.value = res.longitude
|
||||
latitude.value = res.latitude
|
||||
getdata()
|
||||
},
|
||||
fail: function (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const getdata = () => {
|
||||
merchList.value = []
|
||||
status.value = 'loadmore'
|
||||
page.value = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList = () => {
|
||||
|
||||
if (status.value !== 'loadmore') {
|
||||
return
|
||||
}
|
||||
status.value = 'loading'
|
||||
vpMerchService.GetMerchList(categoryType.value, search.value, longitude.value, latitude.value, sortIndex.value, filterIndex.value, page.value).then(res => {
|
||||
if (res.code == 0) {
|
||||
shopLoading.value = false
|
||||
merchList.value = [...merchList.value, ...res.data.merchList]
|
||||
status.value = res.data.merchList.length == 10 ? 'loadmore' : 'nomore'
|
||||
page.value++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 获取标签样式类
|
||||
const getTagClass = (tagText) => {
|
||||
const tagMap = {
|
||||
'12%积分': 'tag-points',
|
||||
'15%积分': 'tag-points',
|
||||
'可用积分': 'tag-points-available',
|
||||
'可用券': 'tag-coupon'
|
||||
}
|
||||
return tagMap[tagText] || 'tag-points'
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 切换筛选
|
||||
const toggleFilter = (index) => {
|
||||
quickFilters.value[index].active = !quickFilters.value[index].active
|
||||
}
|
||||
|
||||
// 切换排序下拉菜单
|
||||
const toggleSortDropdown = () => {
|
||||
showSortDropdown.value = !showSortDropdown.value
|
||||
showFilterDropdown.value = false
|
||||
}
|
||||
|
||||
// 切换筛选下拉菜单
|
||||
const toggleFilterDropdown = () => {
|
||||
showFilterDropdown.value = !showFilterDropdown.value
|
||||
showSortDropdown.value = false
|
||||
}
|
||||
|
||||
// 选择排序方式
|
||||
const selectSort = (index : number) => {
|
||||
currentSort.value = sortOptions[index]
|
||||
sortIndex.value = index
|
||||
showSortDropdown.value = false
|
||||
getdata()
|
||||
}
|
||||
|
||||
// 选择筛选条件
|
||||
const selectFilter = (index : number) => {
|
||||
currentFilter.value = filterOptions[index]
|
||||
showFilterDropdown.value = false
|
||||
if (index > 0 && index <= 3) {
|
||||
filterIndex.value = 1
|
||||
}
|
||||
if (index == 0) {
|
||||
filterIndex.value = index
|
||||
}
|
||||
if (index == 4) {
|
||||
filterIndex.value = 2
|
||||
}
|
||||
getdata()
|
||||
}
|
||||
|
||||
// 点击其他地方关闭下拉菜单
|
||||
const closeDropdowns = () => {
|
||||
showSortDropdown.value = false
|
||||
showFilterDropdown.value = false
|
||||
}
|
||||
|
||||
// 格式化距离
|
||||
const formatDistance = (distance : any) => {
|
||||
if (distance < 1) {
|
||||
return `${Number(distance).toFixed(1)}m`
|
||||
}
|
||||
return `${(distance).toFixed(1)}km`
|
||||
}
|
||||
|
||||
// 返回首页
|
||||
const goBackHome = () => {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.category-page {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
|
||||
/* 顶部导航栏 */
|
||||
.nav-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: calc(var(--status-bar-height) + 320rpx);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 导航内容区域 - 固定位置 */
|
||||
.nav-content {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 80rpx;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.nav-back {
|
||||
margin-left: 20rpx;
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* 搜索框及内容区域 */
|
||||
.content-wrapper {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
margin-top: -85px;
|
||||
background: #FAFAFA;
|
||||
border-top-left-radius: 25px;
|
||||
border-top-right-radius: 25px;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
/* 搜索框 */
|
||||
.search-section {
|
||||
background: transparent;
|
||||
padding: 8px 16px 12px;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
background: #F5F5F5;
|
||||
border-radius: 10px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #222222;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.search-placeholder {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
/* 固定搜索框 - 滚动时显示在顶部 */
|
||||
.search-fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
background: #FAFAFA;
|
||||
padding: 8px 16px 12px;
|
||||
transform: translateY(-100%);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.search-fixed.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* 分类标签栏 */
|
||||
.category-tabs {
|
||||
background: #FFFFFF;
|
||||
padding: 16px 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tabs-scroll {
|
||||
white-space: nowrap;
|
||||
/* 隐藏滚动条 - 兼容不同平台 */
|
||||
-ms-overflow-style: none;
|
||||
/* IE and Edge */
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
}
|
||||
|
||||
/* 隐藏滚动条 - Webkit浏览器 */
|
||||
.tabs-scroll ::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.tabs-list {
|
||||
display: inline-flex;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
min-width: 70px;
|
||||
height: auto;
|
||||
min-height: 70px;
|
||||
}
|
||||
|
||||
.tab-item.active .tab-text {
|
||||
color: #FF5722;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tab-item.active {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.tab-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.tab-item.active .tab-icon {
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
/* 筛选栏 */
|
||||
.filter-section {
|
||||
background: #FFFFFF;
|
||||
padding: 10px 16px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
// margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.filter-sort {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.filter-label {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
font-size: 12px;
|
||||
color: #333333;
|
||||
margin-left: 4px;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.arrow-icon.rotate {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.filter-all {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 10px;
|
||||
background: transparent;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.filter-all-text {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.filter-all-arrow {
|
||||
font-size: 12px;
|
||||
color: #333333;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.filter-all-arrow.rotate {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* 下拉菜单 */
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
background: #FFFFFF;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
z-index: 10;
|
||||
overflow: hidden;
|
||||
animation: dropdownSlide 0.2s ease-out;
|
||||
}
|
||||
|
||||
@keyframes dropdownSlide {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #F0F0F0;
|
||||
}
|
||||
|
||||
.dropdown-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.dropdown-text {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sort-dropdown {
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.filter-dropdown {
|
||||
min-width: 120px;
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.quick-filters {
|
||||
white-space: nowrap;
|
||||
/* 隐藏滚动条 - 兼容不同平台 */
|
||||
-ms-overflow-style: none;
|
||||
/* IE and Edge */
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
}
|
||||
|
||||
/* 隐藏滚动条 - Webkit浏览器 */
|
||||
.quick-filters ::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.quick-filter-list {
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.quick-filter-item {
|
||||
padding: 4px 10px;
|
||||
background: #F5F5F5;
|
||||
border-radius: 4px;
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.quick-filter-item.active {
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #FF6B00;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.quick-filter-item.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: #FF6B00;
|
||||
}
|
||||
|
||||
.filter-text {
|
||||
font-size: 12px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.quick-filter-item.active .filter-text {
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
/* 商家列表 */
|
||||
.shop-list {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.shop-card {
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #E0E0E0;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 商家图片 */
|
||||
.shop-image {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 6px;
|
||||
flex-shrink: 0;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
/* 商家信息 */
|
||||
.shop-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* 店名 */
|
||||
.shop-header {
|
||||
margin-bottom: 6px;
|
||||
padding-right: 60px;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* 左侧信息:评分、类型、销量 */
|
||||
.shop-info-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.shop-rating {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.rating-score {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #FF9800;
|
||||
}
|
||||
|
||||
.rating-unit {
|
||||
font-size: 10px;
|
||||
color: #FF9800;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.shop-info-left .shop-type {
|
||||
font-size: 12px;
|
||||
color: #4CAF50;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.shop-info-left .shop-sales {
|
||||
font-size: 12px;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
/* 距离、用时、人均价格 - 同一行 */
|
||||
.shop-info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.distance-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.location-icon {
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.distance-text {
|
||||
font-size: 12px;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.time-text {
|
||||
font-size: 12px;
|
||||
color: #666666;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
/* 人均价格 - 小号内联样式 */
|
||||
.shop-avg-price-inline {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.avg-price-small {
|
||||
font-size: 11px;
|
||||
color: #FF6B00;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.avg-price-value-small {
|
||||
font-size: 15px;
|
||||
color: #FF6B00;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.avg-price-text-small {
|
||||
font-size: 11px;
|
||||
color: #FF6B00;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
/* 底部标签 */
|
||||
.shop-bottom-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
/* 分类页头部骨架屏样式 */
|
||||
.page-loading-wrapper {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
.skeleton-nav-back {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 8rpx;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.3) 25%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.3) 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading-white 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.skeleton-nav-title {
|
||||
flex: 1;
|
||||
height: 36rpx;
|
||||
border-radius: 4rpx;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.3) 25%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.3) 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading-white 1.5s ease-in-out infinite;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.search-box-skeleton {
|
||||
background: #F5F5F5;
|
||||
border-radius: 10px;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.skeleton-tabs-list {
|
||||
display: inline-flex;
|
||||
padding: 0 16px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.skeleton-tab-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.skeleton-tab-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 8rpx;
|
||||
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.skeleton-tab-text {
|
||||
width: 60rpx;
|
||||
height: 24rpx;
|
||||
border-radius: 4rpx;
|
||||
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.skeleton-filter-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.skeleton-filter-sort,
|
||||
.skeleton-filter-all {
|
||||
height: 32rpx;
|
||||
width: 140rpx;
|
||||
border-radius: 4rpx;
|
||||
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.skeleton-quick-filters {
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.skeleton-quick-filter-item {
|
||||
padding: 4px 10px;
|
||||
background: #F5F5F5;
|
||||
border-radius: 4px;
|
||||
width: 100rpx;
|
||||
height: 32rpx;
|
||||
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: loading 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loading-white {
|
||||
0% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,55 @@
|
||||
@mixin radius($r,$d:null ,$important: false){
|
||||
$radius-value:map-get($uni-radius, $r) if($important, !important, null);
|
||||
// Key exists within the $uni-radius variable
|
||||
@if (map-has-key($uni-radius, $r) and $d){
|
||||
@if $d == t {
|
||||
border-top-left-radius:$radius-value;
|
||||
border-top-right-radius:$radius-value;
|
||||
}@else if $d == r {
|
||||
border-top-right-radius:$radius-value;
|
||||
border-bottom-right-radius:$radius-value;
|
||||
}@else if $d == b {
|
||||
border-bottom-left-radius:$radius-value;
|
||||
border-bottom-right-radius:$radius-value;
|
||||
}@else if $d == l {
|
||||
border-top-left-radius:$radius-value;
|
||||
border-bottom-left-radius:$radius-value;
|
||||
}@else if $d == tl {
|
||||
border-top-left-radius:$radius-value;
|
||||
}@else if $d == tr {
|
||||
border-top-right-radius:$radius-value;
|
||||
}@else if $d == br {
|
||||
border-bottom-right-radius:$radius-value;
|
||||
}@else if $d == bl {
|
||||
border-bottom-left-radius:$radius-value;
|
||||
}
|
||||
}@else{
|
||||
border-radius:$radius-value;
|
||||
}
|
||||
}
|
||||
|
||||
@each $key, $child in $uni-radius {
|
||||
@if($key){
|
||||
.uni-radius-#{"" + $key} {
|
||||
@include radius($key)
|
||||
}
|
||||
}@else{
|
||||
.uni-radius {
|
||||
@include radius($key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@each $direction in t, r, b, l,tl, tr, br, bl {
|
||||
@each $key, $child in $uni-radius {
|
||||
@if($key){
|
||||
.uni-radius-#{"" + $direction}-#{"" + $key} {
|
||||
@include radius($key,$direction,false)
|
||||
}
|
||||
}@else{
|
||||
.uni-radius-#{$direction} {
|
||||
@include radius($key,$direction,false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<view class="">
|
||||
<view class="" style="padding: 0rpx 30rpx; ">
|
||||
<up-form labelPosition="top" :model="userData" labelWidth='200rpx' ref="form1">
|
||||
<up-form-item label="姓名" prop="userInfo.name" ref="item1">
|
||||
<up-input v-model="userData.name" prefixIcon="account" placeholder="请输入姓名"
|
||||
:customStyle="{padding: '20rpx','background-color':'#F8F9FA','margin-top':'15rpx','border-radius':'16rpx'}"
|
||||
:prefixIconStyle="{fontSize:'50rpx' }" border="none"></up-input>
|
||||
</up-form-item>
|
||||
<up-form-item label="手机号" prop="userInfo.name" ref="item1">
|
||||
<up-input v-model="userData.phone"
|
||||
:customStyle="{padding: '20rpx','background-color':'#F8F9FA','margin-top':'15rpx','border-radius':'16rpx'}"
|
||||
prefix-icon="phone" type='number' placeholder="请输入手机号" :prefixIconStyle="{fontSize:'45rpx'}"
|
||||
border="none"></up-input>
|
||||
</up-form-item>
|
||||
<up-form-item label="所在地区" prop="userInfo.name" ref="item1">
|
||||
<view class="" @click="location()"
|
||||
style=" width: 100%; display: flex; align-items: center; justify-content: space-between; border-radius: 16rpx; margin-top: 15rpx; background-color: #F8F9FA;padding: 20rpx;">
|
||||
<view class="" style="display: flex;align-items: center;">
|
||||
<up-icon name="map" color="#606266" size="22"></up-icon>
|
||||
<view v-if="!userData.province" class="" style="color: #c0c4cc; margin-left: 10rpx; ">
|
||||
请选择地址
|
||||
</view>
|
||||
<view v-else class="" style=" margin-left: 10rpx; ">
|
||||
{{ userData.primaryAddress }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="">
|
||||
<up-icon name="arrow-right" color="#606266" size="20"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
</up-form-item>
|
||||
<up-form-item label="详细地址" prop="userInfo.name" ref="item1">
|
||||
<up-textarea v-model="userData.address" style=" margin-top: 15rpx; background-color: #F8F9FA;"
|
||||
placeholder="请输入内容"></up-textarea>
|
||||
</up-form-item>
|
||||
</up-form>
|
||||
<view class=""
|
||||
style=" margin-top: 15rpx; display: flex; align-items: center; justify-content: space-between;">
|
||||
<view class="">
|
||||
设为默认地址
|
||||
</view>
|
||||
<view class="">
|
||||
<up-switch v-model="userData.isDefault" size='18' activeColor='var(--nav-mian)'
|
||||
@change="switchChange"></up-switch>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 新增地址按钮 -->
|
||||
<view class=""
|
||||
style="position: fixed; bottom: 0; width: 100%; padding: 20rpx; border-top: 1rpx solid #e2e2e2; background-color: #fff; ">
|
||||
<up-button @click="save()" color='var(--nav-mian)' shape='circle' style="width: 90%; margin: 15rpx auto 0; "
|
||||
:text="userData.addressId?' 修改地址':'保存地址'"></up-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { vpAddressService, Service } from '@/Service/vp/vpAddressService'
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
let userData = ref({
|
||||
addressId: '',
|
||||
phone: '',
|
||||
name: '',
|
||||
address: '',
|
||||
isDefault: false,
|
||||
province: '',
|
||||
city: '',
|
||||
region: '',
|
||||
primaryAddress: ''
|
||||
})
|
||||
|
||||
|
||||
onLoad((data : any) => {
|
||||
if (data.addressId) {
|
||||
userData.value.addressId = data.addressId
|
||||
uni.setNavigationBarTitle({
|
||||
title: '修改地址'
|
||||
})
|
||||
getData()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const getData = () => {
|
||||
vpAddressService.GetUserAddressInfo(userData.value.addressId).then(res => {
|
||||
if (res.code == 0) {
|
||||
userData.value = res.data.addressInfo
|
||||
userData.value.isDefault = res.data.addressInfo.isDefault == 1 ? true : false
|
||||
userData.value.primaryAddress = res.data.addressInfo.province + res.data.addressInfo.city + res.data.addressInfo.region
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const location = () => {
|
||||
uni.getLocation({
|
||||
type: 'wgs84',
|
||||
success: function (res) {
|
||||
uni.chooseLocation({
|
||||
latitude: res.latitude,
|
||||
longitude: res.longitude,
|
||||
success: function (res) {
|
||||
console.log(res);
|
||||
userData.value.province = res.address.split('省')[0] + '省'
|
||||
userData.value.city = res.address.split('省')[1].split('市')[0] + '市'
|
||||
userData.value.region = res.address.split('省')[1].split('市')[1].split('区')[0] + '区'
|
||||
userData.value.primaryAddress = userData.value.province + userData.value.city + userData.value.region
|
||||
userData.value.address = res.name
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
const switchChange = () => {
|
||||
|
||||
}
|
||||
|
||||
|
||||
const save = () => {
|
||||
if (userData.value.name == '') {
|
||||
Service.Msg('请输入姓名')
|
||||
return
|
||||
}
|
||||
if (userData.value.phone == '') {
|
||||
Service.Msg('请输入电话号')
|
||||
return
|
||||
}
|
||||
if (userData.value.province == '') {
|
||||
Service.Msg('请选择地址')
|
||||
return
|
||||
}
|
||||
if (userData.value.address == '') {
|
||||
Service.Msg('请输入详细地址')
|
||||
return
|
||||
}
|
||||
|
||||
let obj = {
|
||||
addressId: userData.value.addressId,
|
||||
name: userData.value.name,
|
||||
phone: userData.value.phone,
|
||||
province: userData.value.province,
|
||||
city: userData.value.city,
|
||||
region: userData.value.region,
|
||||
address: userData.value.address,
|
||||
isDefault: userData.value.isDefault ? 1 : 0
|
||||
}
|
||||
vpAddressService.UpdateAddress(obj).then(res => {
|
||||
if (res.code == 0) {
|
||||
Service.Msg( userData.value.addressId?'修改成功!': '添加成功!')
|
||||
setTimeout(() => {
|
||||
Service.GoPageBack()
|
||||
}, 1000)
|
||||
} else {
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<view style=" margin: 20rpx; padding: 20rpx;">
|
||||
|
||||
<view @click="uploadFImg()" class=""
|
||||
style=" display: flex; flex-direction: column; justify-content: center; align-items: center; ">
|
||||
<img v-if="userInfo.headImg!=''" :src="Service.GetMateUrlByImg(userInfo.headImg)" style="width: 140rpx; height: 140rpx; border-radius: 50%; " alt="" />
|
||||
<view v-else class=""
|
||||
style="background-color: #EBEBEB; width: 140rpx; height: 140rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; ">
|
||||
<img :src="Service.GetIconImg('/static/userFunc/photo.png')" style="width: 50rpx; height: 50rpx; "
|
||||
alt="" />
|
||||
</view>
|
||||
<view class="" style="margin-top: 15rpx; font-size: 26rpx; color: #999999; ">
|
||||
点击更换头像
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="" style=" margin-top: 30rpx; ">
|
||||
<up-form labelWidth='90' labelPosition="left" :model="userInfo" ref="form1">
|
||||
<up-form-item label="昵称" prop="userInfo.name" :borderBottom="true" ref="item1">
|
||||
<up-input inputAlign='right' v-model="userInfo.nick" border="none"></up-input>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="性别" prop="userInfo.sex" :borderBottom="true">
|
||||
<view class="" style="position: relative;" >
|
||||
<view class="" style=" position: absolute; top: -8rpx; right: 0; ">
|
||||
<up-radio-group v-model="userInfo.sex" placement="row">
|
||||
<up-radio v-for="(item, index) in radiolist1" activeColor='#FF6A00' :key="index"
|
||||
iconPlacement="right" :label="item.name" :name="item.name">
|
||||
</up-radio>
|
||||
</up-radio-group>
|
||||
</view>
|
||||
</view>
|
||||
</up-form-item>
|
||||
<up-form-item label="生日" prop="userInfo.sex" :borderBottom="true" style="position: relative;" >
|
||||
<view @click="showDate=true" class="" style=" position: absolute; top: 25rpx; right: 0; display: flex; align-items: center; ">
|
||||
{{Service.formatDate(userInfo.date,2)}}
|
||||
<u-icon name="arrow-right" size="24rpx" color="#000"></u-icon>
|
||||
</view>
|
||||
</up-form-item>
|
||||
<up-form-item label="手机号" prop="userInfo.sex" :borderBottom="true">
|
||||
<up-input inputAlign='right' v-model="userInfo.phone" border="none"></up-input>
|
||||
</up-form-item>
|
||||
<up-form-item label="邮箱" prop="userInfo.sex" :borderBottom="true">
|
||||
<up-input inputAlign='right' v-model="userInfo.age" border="none"></up-input>
|
||||
</up-form-item>
|
||||
</up-form>
|
||||
</view>
|
||||
|
||||
<up-datetime-picker :show="showDate" @cancel="showDate=!showDate" @confirm="dateConfirm" v-model="userInfo.date" mode="date"></up-datetime-picker>
|
||||
<view class="" style="width: 100%; height: 200rpx;">
|
||||
|
||||
</view>
|
||||
<view class="" style=" width: 100% ; background-color: #fff; position: fixed; bottom: 15rpx; left: 0; ">
|
||||
<view class=""
|
||||
style=" margin: 0 20rpx; padding: 20rpx 0; color: #fff; display: flex; align-items: center; justify-content: center; border-radius: 10rpx; background-color: #FF6A00;">
|
||||
保存信息
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { Service } from '@/Service/Service';
|
||||
import { ref } from "vue";
|
||||
|
||||
|
||||
let showDate=ref(false)
|
||||
const userInfo = ref({
|
||||
headImg: '',
|
||||
age: '1',
|
||||
sex: '',
|
||||
phone: '1',
|
||||
date:Date.now(),
|
||||
nick: '大大怪将军'
|
||||
})
|
||||
|
||||
|
||||
const radiolist1 = ref([
|
||||
{
|
||||
name: '男',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
name: '女',
|
||||
disabled: false,
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
onLoad(() => {
|
||||
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
});
|
||||
|
||||
const dateConfirm=(e)=>{
|
||||
showDate.value=!showDate.value
|
||||
userInfo.value.date=e.value
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const uploadFImg = () => {
|
||||
uni.chooseImage({
|
||||
count: 1, // 最多选择3张图片
|
||||
sizeType: ['original', 'compressed'], // 支持原图和压缩图
|
||||
sourceType: ['album', 'camera'], // 可从相册选择或使用相机拍照
|
||||
success: function (res) {
|
||||
let path = res.tempFiles[0].path
|
||||
userInfo.value.headImg=path
|
||||
// Service.uploadH5(path, 'Avatar', data => {
|
||||
// userInfo.value.headImg = data.split(',')[2].split(':')[1].split('"')[1]
|
||||
// })
|
||||
|
||||
},
|
||||
fail: function (err) {
|
||||
console.error('选择失败:', err.errMsg);
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,24 @@
|
||||
@mixin get-styles($k,$c) {
|
||||
@if $k == size or $k == weight{
|
||||
font-#{$k}:#{$c}
|
||||
}@else{
|
||||
#{$k}:#{$c}
|
||||
}
|
||||
}
|
||||
|
||||
@each $key, $child in $uni-headings {
|
||||
/* #ifndef APP-NVUE */
|
||||
.uni-#{$key} {
|
||||
@each $k, $c in $child {
|
||||
@include get-styles($k,$c)
|
||||
}
|
||||
}
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
.container .uni-#{$key} {
|
||||
@each $k, $c in $child {
|
||||
@include get-styles($k,$c)
|
||||
}
|
||||
}
|
||||
/* #endif */
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
<template>
|
||||
<view class="favorites-page">
|
||||
<!-- 沉浸式状态栏 -->
|
||||
<view class="status-bar"></view>
|
||||
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="nav-bar">
|
||||
<image class="back-icon" src="/static/icons/back.svg" @click="goBack" mode="aspectFit" />
|
||||
<text class="nav-title">我的收藏</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
|
||||
<!-- 收藏列表 -->
|
||||
<view class="content">
|
||||
<view v-if="collectList.length > 0" class="favorites-list">
|
||||
<view v-for="item in collectList" :key="item.merchId" class="shop-card" @click="Service.GoPage('/pages/community/merchantDetail?merchId='+item.merchId)">
|
||||
<!-- 店铺图片 -->
|
||||
<image class="shop-image" :src="Service.GetMateUrlByImg(item.logo)" mode="aspectFill" />
|
||||
|
||||
<!-- 店铺信息 -->
|
||||
<view class="shop-info">
|
||||
<text class="shop-name">{{ item.name }}</text>
|
||||
<view class="shop-meta">
|
||||
<text class="ri-star-fill rating-icon"></text>
|
||||
<text class="rating-text">{{ Number(item.score ).toFixed(1)}}分</text>
|
||||
<text class="sales-text">月售{{ item.sale }}</text>
|
||||
</view>
|
||||
<view class="shop-tags">
|
||||
<view v-for="(coupon, idx) in item.tips" :key="idx" :class="getTagClass(coupon)" style="font-size: 24rpx;"
|
||||
class="shop-tag">
|
||||
{{ coupon }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 取消收藏按钮 -->
|
||||
<view class="cancel-btn" @click.stop="cancelFavorite(item)">
|
||||
<up-icon name="heart-fill" color="#fc5151" :bold="true" size="18"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
<up-loadmore :status="status" />
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-else class="empty-state">
|
||||
<text class="ri-heart-line empty-icon"></text>
|
||||
<text class="empty-text">暂无收藏店铺</text>
|
||||
<text class="empty-desc">去首页逛逛,收藏喜欢的店铺吧</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {onShow,onLoad,onReachBottom} from "@dcloudio/uni-app";
|
||||
import {Service} from "@/Service/Service"
|
||||
import { ref, computed } from "vue";
|
||||
import { vpDiscountService } from "@/Service/vp/vpDiscountService"
|
||||
import { vpUserService } from "@/Service/vp/vpUserService"
|
||||
|
||||
|
||||
let collectList = ref<Array<any>>([])
|
||||
let status = ref<string>('loadmore')
|
||||
let pageNo = ref<number>(1)
|
||||
let longitude=ref(0)
|
||||
let latitude=ref(0)
|
||||
|
||||
|
||||
onLoad(()=>{
|
||||
getLocation()
|
||||
})
|
||||
onReachBottom(()=>{
|
||||
getList()
|
||||
})
|
||||
|
||||
// 返回
|
||||
const goBack = () => {
|
||||
Service.GoPageBack()
|
||||
}
|
||||
|
||||
|
||||
const getLocation = () => {
|
||||
uni.getLocation({
|
||||
type: 'wgs84',
|
||||
success: function (res) {
|
||||
longitude.value = res.longitude
|
||||
latitude.value = res.latitude
|
||||
getData()
|
||||
},
|
||||
fail: function (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const getData=()=>{
|
||||
pageNo.value=1
|
||||
status.value='loadmore'
|
||||
collectList.value=[]
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList=()=>{
|
||||
if (status.value !== 'loadmore') {
|
||||
return
|
||||
}
|
||||
status.value = 'loading'
|
||||
vpUserService.GetUserCollectList(longitude.value,latitude.value,pageNo.value).then(res=>{
|
||||
if(res.code==0){
|
||||
collectList.value=[...collectList.value,...res.data.collectList]
|
||||
status.value=res.data.collectList.length==10?'loadmore':'nomore'
|
||||
pageNo.value++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 根据标签文本获取样式类
|
||||
const getTagClass = (tagText : string) => {
|
||||
const tagMap = {
|
||||
'可用积分': 'tag-points-available',
|
||||
'可用券': 'tag-coupon'
|
||||
}
|
||||
return tagMap[tagText] || 'tag-points'
|
||||
}
|
||||
|
||||
|
||||
// 取消收藏
|
||||
const cancelFavorite = (item:any) => {
|
||||
uni.showModal({
|
||||
title: '取消收藏',
|
||||
content: `确定取消收藏吗?`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
vpUserService.CollectMerch(item.merchId).then(res=>{
|
||||
if(res.code==0){
|
||||
getData()
|
||||
}else{
|
||||
Service.Msg(res.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.favorites-page {
|
||||
min-height: 100vh;
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
.action-pill-icon {
|
||||
font-size: 28rpx;
|
||||
color: #FF6B00;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 状态栏 */
|
||||
.status-bar {
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 60rpx 24rpx 20rpx 24rpx;
|
||||
background: linear-gradient(135deg, #FF6B00, #FF9500);
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.nav-placeholder {
|
||||
width: 48rpx;
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.content {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
/* 收藏列表 */
|
||||
.favorites-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.shop-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.shop-image {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 12rpx;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.shop-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.rating-icon {
|
||||
font-size: 24rpx;
|
||||
color: #FFB800;
|
||||
}
|
||||
|
||||
.rating-text {
|
||||
font-size: 24rpx;
|
||||
color: #FF6B00;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.sales-text {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.shop-tags {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 20rpx;
|
||||
color: #666666;
|
||||
background: #F5F5F5;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background: #FFEBEE;
|
||||
border-radius: 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cancel-icon {
|
||||
font-size: 32rpx;
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 200rpx 0;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 120rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.empty-desc {
|
||||
font-size: 24rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
</style>
|
||||