Merge branch 'master' into mrr_sj_develop_丁杰_20260609_自定义服务发布

This commit is contained in:
丁杰 2026-06-22 14:43:11 +08:00
commit 3e39ff1140
246 changed files with 32811 additions and 20573 deletions

83
App.vue
View File

@ -1,24 +1,27 @@
<script>
// #ifdef APP-PLUS
import {
// #ifdef APP-PLUS
import {
isNotificationsEnabled,
permissions,
registPush
} from '@/utils/AS-NotifyTheAuthority.js'
import {
} from '@/utils/AS-NotifyTheAuthority.js'
import {
permissionListener
} from './utils/permissionTips.js'
// import { appUpdateListener } from '@/common/utils/appUpdateTips.js'
import locationService from './utils/locationService.js'
// import uploadApp from './utils/uploadApp.js'
// #endif
import request from './utils/request'
import {
} from './utils/permissionTips.js'
// import { appUpdateListener } from '@/common/utils/appUpdateTips.js'
import locationService from './utils/locationService.js'
// import uploadApp from './utils/uploadApp.js'
// #endif
import request from './utils/request'
import {
setVersion
} from './utils/version.js'
//
const aLiSDKModule = uni.requireNativePlugin('AliCloud-NirvanaPns');
export default {
} from './utils/version.js'
import {
APP_BACK_BASE_URL
} from './utils/appBackBase.js'
//
const aLiSDKModule = uni.requireNativePlugin('AliCloud-NirvanaPns');
export default {
globalData: {
artisanType: 2,
latitude: '38.366667',
@ -52,7 +55,7 @@
}
}
},
onLaunch: async function() {
onLaunch: async function () {
// #ifdef APP-PLUS
uni.hideTabBar();
if (uni.getStorageSync('accessToken')) {
@ -66,38 +69,13 @@
// await setVersion()
//访
// GET
let version = uni.getStorageSync('setVersion')
let baseUrl
await uni.request({
url: 'https://api.mrrwlkj.top/api/openPuc/getSjApiUrl', //
method: 'GET', // GET
header: {
'Content-Type': 'application/json', //
'VersionCode': version.VersionCode,
'DeviceBrand': version.DeviceBrand,
'DeviceType': version.DeviceType
},
success: (res) => {
if (res.data.code == 200) {
// #ifndef MP-WEIXIN
uni.setStorageSync('baseUrl', res.data.data.url)
uni.setStorageSync('baseUrl', APP_BACK_BASE_URL)
// #endif
// #ifdef MP-WEIXIN
wx.setStorageSync('baseUrl', res.data.data.url)
wx.setStorageSync('baseUrl', APP_BACK_BASE_URL)
// #endif
this.$store.commit("setLocation", true)
}
console.log('请求成功', res.data);
},
fail: (err) => {
console.log('请求失败', err);
},
complete: () => {
console.log('请求完成(无论成功失败都会执行)');
}
});
// #ifdef APP-PLUS
//
@ -180,11 +158,12 @@
}
// #endif
},
onShow: function() {
onShow: function () {
uni.hideTabBar();
console.log('App Show')
uni.$emit('appShowRefresh')
},
onHide: function() {
onHide: function () {
console.log('App Hide')
},
methods: {
@ -267,10 +246,18 @@
uni.setStorageSync('userAdrees', this.globalData)
},
}
}
}
</script>
<style>
/*每个页面公共css */
@import '@/static/css/common.css';
/*每个页面公共css */
@import '@/static/css/common.css';
/* App.vue全局style */
page,
html,
body {
-webkit-text-size-adjust: 100% !important;
text-size-adjust: 100% !important;
}
</style>

92
CHANGE_RECORD.md Normal file
View File

@ -0,0 +1,92 @@
# 改动记录
## 2026-06-12
### 后台长时间运行后首页登录态不同步
问题现象:
- APP 后台运行较长时间后再次进入。
- 商家首页显示未登录或无法及时获取商家信息。
- “我的”页面仍显示已登录。
- 从“我的”页面切回首页后,首页才能正常获取商家信息。
涉及文件:
- `App.vue`
- `pages/home/home.vue`
- `utils/request.js`
改动内容:
- `App.vue`
- 在全局 `onShow` 中新增 `uni.$emit('appShowRefresh')`
- APP 从后台恢复前台时主动通知页面刷新状态,避免只依赖页面级 `onShow`
- `pages/home/home.vue`
- 新增 `refreshPageState` 方法,统一处理首页登录态刷新。
- `onShow`、下拉刷新、APP 前台恢复事件都调用同一套刷新逻辑。
- `onLoad` 注册 `appShowRefresh` 事件。
- `onUnload` 解除 `appShowRefresh` 事件,避免重复监听。
- 有 token 时主动重新请求 `/sj/user/getUser`
- 无 token 时清空 `userInfo`、`authDetails`、`sjId`,并重置 `isUserInfoLoaded`
- `getUserInfo` 请求成功后显式设置 `isLogin = true``isUserInfoLoaded = true`
- `getUserInfo` 请求失败时,如果本地 token 已被清理,则同步首页为未登录状态。
- `utils/request.js`
- `refreshToken` 兼容后端返回结构:
- 支持 token 在 `res.access_token`
- 支持 token 在 `res.data.access_token`
- 刷新 token 成功后同步更新:
- `accessToken`
- `refreshToken`
- `refresh_token_expries`
- 如果刷新接口成功但没有返回 `access_token`,主动抛错,避免写入 `undefined` token。
验证情况:
- 已检查相关 diff未做全文件重构。
- `pages/home/home.vue` 原文件为 CRLF 行尾;为避免整文件行尾改造造成大面积 diff本次保留原行尾。
- 项目 `package.json` 未配置 build/lint 脚本,未执行完整构建校验。
### 订单详情顾客头像被挤压
问题现象:
- `pages/shop/SellerDetail` 页面“顾客信息”一栏中,客户头像在部分图片比例下显示被拉伸或挤压。
涉及文件:
- `pages/shop/SellerDetail.vue`
改动内容:
- 给顾客头像 `<image>` 增加 `mode="aspectFill"`,按头像容器比例裁剪填充,避免原图比例不一致时被拉伸。
- 为 `.shop_photo` 增加 `min-width``min-height`,在 flex 布局中进一步锁定头像为固定正方形尺寸。
验证情况:
- 已执行 `pages/shop/SellerDetail.vue``CHANGE_RECORD.md` 的空白格式检查。
### 核销成功后工时/工位订单详情跳转错误
问题现象:
- 商家扫码核销或输入核销码核销成功后,工时订单和工位订单跳转到详情页时参数或页面类型不正确。
- 工时订单会进入工位详情链路,工位订单会落到普通订单详情链路。
涉及文件:
- `pages/shop/verify/verify-order-detail.vue`
改动内容:
- 新增 `getOrderDetailUrl`,统一按 `order_type` 生成核销成功后的详情页地址。
- `order_type=3` 工时订单跳转到 `/pages/shop/SellerDetail?order_id=...&identity=1`,使用手艺人工时订单详情链路。
- `order_type=4` 工位订单跳转到 `/pages/shop/SellerDetail?order_id=...`,使用商家工位订单详情链路。
- `order_type=6` 自营订单跳转普通订单详情并携带 `serviceType=2`
- 其他普通订单跳转普通订单详情,不再误携带自营参数。
验证情况:
- 已执行 `pages/shop/verify/verify-order-detail.vue``CHANGE_RECORD.md` 的空白格式检查。

View File

@ -95,7 +95,7 @@ export default {
color: #999;
}
.tab-text.active {
color: #E8101E; /* 选中颜色 */
color: #FF4767; /* 选中颜色 */
}
.tab-badge {
position: absolute;

View File

@ -43,7 +43,7 @@ export default {
//
color: {
type: String,
default: '#E8101E'
default: '#FF4767'
},
//
backgroundColor: {

View File

@ -402,7 +402,7 @@ export default {
}
.time-option.active .time-text {
color: #E8101E;
color: #FF4767;
font-weight: 500;
}
@ -413,7 +413,7 @@ export default {
.time-discount {
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
padding: 4rpx 12rpx;
background-color: #fff5f6;
border-radius: 20rpx;
@ -444,7 +444,7 @@ export default {
color: #666;
}
.active {
color: #E8101E; /* 选中态主题色 */
color: #FF4767; /* 选中态主题色 */
}
/* 服务时间按钮专属:控制下拉图标尺寸 */

View File

@ -52,7 +52,7 @@ export default {
// props
activeColor: {
type: String,
default: "#E8101E",
default: "#FF4767",
},
// props
inactiveColor: {

View File

@ -59,7 +59,7 @@ export default {
//
activeColor: {
type: String,
default: "#E8101E",
default: "#FF4767",
},
//
inactiveColor: {
@ -129,7 +129,7 @@ export default {
left: 0;
right: 0;
bottom: 0;
border: 2rpx solid #E8101E;
border: 2rpx solid #FF4767;
border-radius: 48rpx;
}
@ -143,6 +143,6 @@ export default {
}
.icon-item.active .icon-text {
color: #E8101E;
color: #FF4767;
}
</style>

View File

@ -208,7 +208,7 @@ export default {
padding-left: 30rpx;
position: relative;
&__point {
background-color: #E8101E;
background-color: #FF4767;
height: 6rpx;
width: 6rpx;
border-radius: 50%;
@ -221,7 +221,7 @@ export default {
}
.active {
background: #ffffff;
color: #E8101E;
color: #FF4767;
}
}
&__right {
@ -295,17 +295,17 @@ export default {
height: 82rpx;
font-weight: 400;
font-size: 30rpx;
color: #E8101E;
color: #FF4767;
line-height: 42rpx;
text-align: left;
font-style: normal;
}
&__sure {
border-radius: 41rpx;
border: 2rpx solid #E8101E;
border: 2rpx solid #FF4767;
}
&__reset {
background: #E8101E;
background: #FF4767;
border-radius: 41rpx;
color: #ffffff;
}

View File

@ -114,8 +114,8 @@ export default {
}
.active {
background: rgba(252, 67, 124, 0.1);
color: #E8101E;
border: 1rpx solid #E8101E;
color: #FF4767;
border: 1rpx solid #FF4767;
}
}
</style>

View File

@ -548,7 +548,7 @@
width: 690rpx;
height: 68rpx;
background: #FFFFFF;
border: 2rpx solid #E8101E;
border: 2rpx solid #FF4767;
background-color: #fafafa;
display: flex;
align-items: center;

View File

@ -121,7 +121,7 @@ export default {
border: 1rpx solid #fba9bb;
font-weight: 500;
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
text-align: left;
font-style: normal;
}
@ -197,7 +197,7 @@ export default {
margin-top: 10rpx;
font-weight: 500;
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
line-height: 33rpx;
text-align: left;
font-style: normal;

View File

@ -59,7 +59,7 @@ export default {
.loginBtn {
width: 262rpx;
height: 86rpx;
background: #e8101e;
background: #FF4767;
border-radius: 43rpx;
text-align: center;
line-height: 86rpx;

View File

@ -408,11 +408,11 @@ export default {
.isActive {
background: rgba(252, 67, 124, 0.08);
color: #e8101e !important;
color: #FF4767 !important;
}
.isActive .time-picker-content__time__list__item__text {
color: #e8101e !important;
color: #FF4767 !important;
}
/* 选中样式前后弧度 */
@ -433,7 +433,7 @@ border-radius: 0rpx 51rpx 51rpx 0rpx}
.time-picker-content__time__sure__box {
width: 648rpx;
height: 94rpx;
background: #E8101E;
background: #FF4767;
border-radius: 48rpx;
display: flex;
align-items: center;

View File

@ -467,8 +467,8 @@
.isActive {
background: rgba(252, 67, 124, 0.08);
border-radius: 10rpx;
border: 1rpx solid #E8101E;
color: #E8101E;
border: 1rpx solid #FF4767;
color: #FF4767;
/* 选中状态 */
}
@ -493,7 +493,7 @@
&__box {
width: 648rpx;
height: 94rpx;
background: linear-gradient(140deg, #fa4e9a 0%, #E8101E 100%);
background: linear-gradient(140deg, #fa4e9a 0%, #FF4767 100%);
border-radius: 48rpx;
display: flex;
align-items: center;

View File

@ -1,30 +1,19 @@
<template>
<view class="service-code-modal" v-if="show" :style="{bottom:`${popBotom}px`}">
<view class="service-code-modal" v-if="show" :style="{ bottom: `${popBotom}px` }">
<view class="modal-mask" @touchmove.stop.prevent></view>
<view class="modal-content">
<view class="modal-title">开始服务</view>
<view class="modal-subtitle">请输入服务码</view>
<view class="code-box">
<view
v-for="(item, idx) in codeLength"
:key="idx"
<view v-for="(item, idx) in codeLength" :key="idx"
:class="['code-cell', idx === currentIndex ? 'active' : '', code[idx] ? 'filled' : '']">
<text v-if="code[idx]">{{ code[idx] }}</text>
</view>
<!-- 隐藏真实输入框 -->
<input
:adjust-position="false"
class="real-input"
type="number"
:maxlength="codeLength"
v-model="inputValue"
:focus="true"
@input="onInput"
@focus="onFocus"
@keyboardheightchange="onkeyboardheightchange"
@blur="onBlur"
cursor-spacing="100"
ref="realInput" />
<input :adjust-position="false" class="real-input" type="number" :maxlength="codeLength"
v-model="inputValue" :focus="true" @input="onInput" @focus="onFocus"
@keyboardheightchange="onkeyboardheightchange" @blur="onBlur" cursor-spacing="100"
cursor-color="transparent" ref="realInput" />
</view>
<button class="confirm-btn" :disabled="code.length < codeLength" @click="onConfirm">确定</button>
<view class="close-btn" @click="close">×</view>
@ -33,7 +22,7 @@
</template>
<script>
export default {
export default {
name: 'serviceCodeInput',
props: {
show: {
@ -50,7 +39,7 @@
inputValue: '',
isFocus: true,
keyboardHeight: 0,
popBotom:0,
popBotom: 0,
}
},
computed: {
@ -88,7 +77,7 @@
this.isFocus = true
this.inputValue = ''
},
onkeyboardheightchange(e){
onkeyboardheightchange(e) {
this.popBotom = e.target.height || 0
},
onBlur() {
@ -104,11 +93,11 @@
this.$emit('close')
}
}
}
}
</script>
<style lang="scss" scoped>
.service-code-modal {
.service-code-modal {
position: fixed;
left: 0;
top: 0;
@ -117,12 +106,13 @@
z-index: 999999;
.modal-mask {
position: absolute;
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.15);
background: rgba(0, 0, 0, 0.5);
z-index: -1;
}
.modal-content {
@ -197,6 +187,7 @@
height: 100%;
opacity: 0;
z-index: 2;
caret-color: transparent;
}
}
@ -228,9 +219,9 @@
font-weight: bold;
line-height: 1;
}
}
}
@keyframes modalUp {
@keyframes modalUp {
from {
transform: translateY(100%);
}
@ -238,5 +229,5 @@
to {
transform: translateY(0);
}
}
}
</style>

View File

@ -181,7 +181,7 @@
position: absolute;
top: 0rpx;
left: 0rpx;
background-color: #E8101E;
background-color: #FF4767;
color: #FFFFFF;
font-size: 24rpx;
padding: 6rpx 14rpx;
@ -247,7 +247,7 @@
.price-symbol {
font-weight: 500;
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
line-height: 33rpx;
text-align: left;
font-style: normal;
@ -256,7 +256,7 @@
.price-value {
font-weight: 500;
font-size: 36rpx;
color: #E8101E;
color: #FF4767;
line-height: 33rpx;
text-align: left;
font-style: normal;
@ -332,12 +332,12 @@
.price-symbol {
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
}
.price-value {
font-size: 32rpx;
color: #E8101E;
color: #FF4767;
font-weight: 500;
margin-right: 12rpx;
}

View File

@ -336,7 +336,7 @@
.price-text{
font-weight: 500;
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
line-height: 33rpx;
text-align: left;
font-style: normal;
@ -345,7 +345,7 @@
.price-symbol {
font-weight: 500;
font-size: 20rpx;
color: #E8101E;
color: #FF4767;
line-height: 33rpx;
text-align: left;
font-style: normal;
@ -354,7 +354,7 @@
.price-value {
font-weight: 500;
font-size: 30rpx;
color: #E8101E;
color: #FF4767;
line-height: 33rpx;
text-align: left;
font-style: normal;

View File

@ -1,63 +1,54 @@
<template>
<view class="step-tab">
<view class="step-container">
<!-- 步骤1: 入驻协议 -->
<view class="step-item" :class="getStepClass(0)">
<view class="step-icon">
<image
v-if="currentStep === 0"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/55a51333-318f-4ae4-8402-aca265bd499d"
v-if="mappedStep === 0"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/d62a1d01-52cb-418e-ba5b-afef513061b9.png"
class="step-img active-circle"
mode="aspectFit"
/>
<view v-else-if="currentStep > 0" class="step-done">1</view>
<view v-else-if="mappedStep > 0" class="step-done">1</view>
<view v-else class="step-number">1</view>
</view>
<text class="step-text" :class="{ active: currentStep === 0 }">入驻协议</text>
<text class="step-text" :class="{ active: mappedStep === 0 }">入驻协议</text>
</view>
<!-- 第一个连接线 -->
<view class="line-segment line-1">
<view class="line-progress" :class="{ active: line1Active, deactivating: line1Deactivating }"></view>
</view>
<!-- 步骤2: 根据身份显示不同信息 -->
<view class="step-item" :class="getStepClass(1)">
<view class="step-icon">
<image
v-if="currentStep === 1"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/55a51333-318f-4ae4-8402-aca265bd499d"
v-if="mappedStep === 1"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/d62a1d01-52cb-418e-ba5b-afef513061b9.png"
class="step-img active-circle"
mode="aspectFit"
/>
<view v-else-if="currentStep > 1" class="step-done">2</view>
<view v-else-if="mappedStep > 1" class="step-done">2</view>
<view v-else class="step-number">2</view>
</view>
<text class="step-text" :class="{ active: currentStep === 1 }">
{{ identity === 1 ? '个人信息' : '资质信息' }}
</text>
<text class="step-text" :class="{ active: mappedStep === 1 }">基本信息</text>
</view>
<!-- 第二个连接线 -->
<view class="line-segment line-2">
<view class="line-progress" :class="{ active: line2Active, deactivating: line2Deactivating }"></view>
</view>
<!-- 步骤3: 根据身份显示不同信息 -->
<view class="step-item" :class="getStepClass(2)">
<view class="step-icon">
<image
v-if="currentStep === 2"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/55a51333-318f-4ae4-8402-aca265bd499d"
v-if="mappedStep === 2"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/d62a1d01-52cb-418e-ba5b-afef513061b9.png"
class="step-img active-circle"
mode="aspectFit"
/>
<view v-else-if="currentStep > 2" class="step-done">3</view>
<view v-else-if="mappedStep > 2" class="step-done">3</view>
<view v-else class="step-number">3</view>
</view>
<text class="step-text" :class="{ active: currentStep === 2 }">
{{ identity === 1 ? '资质信息' : '门店信息' }}
</text>
<text class="step-text" :class="{ active: mappedStep === 2 }">资质认证</text>
</view>
</view>
</view>
@ -67,32 +58,23 @@
export default {
name: "StepTab",
props: {
// (0, 1, 2)
currentStep: {
mappedStep: {
type: Number,
default: 0
},
// 1-2-
identity: {
type: Number,
default: 2
}
},
data() {
return {
prevStep: 0, //
line1Active: false, // 线
line2Active: false, // 线
line1Deactivating: false, // 线
line2Deactivating: false // 线
prevStep: 0,
line1Active: false,
line2Active: false,
line1Deactivating: false,
line2Deactivating: false
};
},
watch: {
// currentStep线
currentStep(newVal, oldVal) {
mappedStep(newVal, oldVal) {
this.prevStep = oldVal;
//
if (newVal > oldVal) {
if (newVal >= 1) {
this.line1Active = true;
@ -102,38 +84,34 @@ export default {
this.line2Active = true;
this.line2Deactivating = false;
}
}
//
else if (newVal < oldVal) {
} else if (newVal < oldVal) {
if (newVal < 2) {
this.line2Deactivating = true;
setTimeout(() => {
this.line2Active = false;
this.line2Deactivating = false;
}, 300); //
}, 300);
}
if (newVal < 1) {
this.line1Deactivating = true;
setTimeout(() => {
this.line1Active = false;
this.line1Deactivating = false;
}, 300); //
}, 300);
}
}
}
},
mounted() {
// 线
this.line1Active = this.currentStep >= 1;
this.line2Active = this.currentStep >= 2;
this.line1Active = this.mappedStep >= 1;
this.line2Active = this.mappedStep >= 2;
},
methods: {
// CSS
getStepClass(stepIndex) {
if (stepIndex === this.currentStep) {
if (stepIndex === this.mappedStep) {
return 'active';
}
else if (stepIndex < this.currentStep) {
else if (stepIndex < this.mappedStep) {
return 'completed';
}
return '';
@ -206,7 +184,7 @@ export default {
justify-content: center;
align-items: center;
font-size: 28rpx;
color: #E8101E;
color: #FF4767;
font-weight: 500;
transition: all 0.3s ease;
box-shadow: inset 0 0 4rpx rgba(232, 16, 30, 0.2);
@ -259,35 +237,16 @@ export default {
transform-origin: left center;
}
/* 激活状态的线条 - 前进动画 */
.line-progress.active {
width: 100%;
background-color: #E8101E;
background-color: #FF4767;
transition: width 0.3s ease;
}
/* 取消激活状态的线条 - 返回动画 */
.line-progress.deactivating {
width: 0%;
background-color: #E8101E;
background-color: #FF4767;
transition: width 0.3s ease;
transform-origin: right center;
}
/* 响应式调整 */
@media (max-width: 750rpx) {
.step-container {
/* padding: 5rpx 40rpx; */
}
.line-segment {
/* width: 100rpx; */
}
/* .line-1,
.line-2 {
margin-left: 15rpx;
margin-right: 15rpx;
} */
}
</style>

View File

@ -1,8 +1,24 @@
<template>
<uv-tabbar fixed :value="selected" @change="change1" activeColor="#ee0a24" :placeholder="false"
:safeAreaInsetBottom="false" :customStyle="{height:`110rpx`}">
<uv-tabbar-item v-for="(item,index) in $store.state.tabbarList" :key="index" :badge="getNum(index)"
:customStyle="{height:`110rpx`}">
<uv-tabbar
v-if="tabbarReady"
:key="refreshKey"
fixed
:value="currentTabIndex"
@change="onTabChange"
activeColor="#FF4767"
:placeholder="false"
:safeAreaInsetBottom="false"
:customStyle="{height:'110rpx'}"
>
<uv-tabbar-item
v-for="(item, idx) in tabbarList"
:key="idx"
:name="idx"
:text="item.text"
:badge="getNum(idx)"
:customStyle="{height:'110rpx'}"
@click="onItemClick(idx)"
>
<template v-slot:active-icon>
<image class="icon" :src="item.selectedIconPath"></image>
</template>
@ -14,110 +30,180 @@
</template>
<script>
import request from "@/utils/request";
export default {
import { mapState, mapMutations } from 'vuex';
import request from "@/utils/request";
export default {
name: "tab-bar",
props: {
selected: {
type: [Number, String]
}
},
// props.selected Vuex
data() {
return {
tabbarList: [{
"iconPath": "/static/images/tabbar/new_home_gray.png",
"text": "首页",
"selectedIconPath": "/static/images/tabbar/new_home.png",
"pagePath": "/pages/home/home"
refreshKey: 0, //
tabbarReady: false, //
tabbarList: [ // tab
{
iconPath: "/static/images/tabbar/new_home_gray.png",
text: "首页",
selectedIconPath: "/static/images/tabbar/new_home.png",
pagePath: "/pages/home/home"
},
{
"iconPath": "/static/images/tabbar/new_message_gray.png",
"text": "消息",
"selectedIconPath": "/static/images/tabbar/new_message.png",
"pagePath": "/pages/message/message"
iconPath: "/static/images/tabbar/new_order_gray.png",
text: "订单",
selectedIconPath: "/static/images/tabbar/new_order.png",
pagePath: "/pages/order/userorder-list"
},
{
"iconPath": "/static/images/tabbar/new_my_gray.png",
"text": "我的",
"selectedIconPath": "/static/images/tabbar/new_my.png",
"pagePath": "/pages/my/my"
iconPath: "/static/images/tabbar/new_message_gray.png",
text: "消息",
selectedIconPath: "/static/images/tabbar/new_message.png",
pagePath: "/pages/message/message"
},
{
iconPath: "/static/images/tabbar/new_my_gray.png",
text: "我的",
selectedIconPath: "/static/images/tabbar/new_my.png",
pagePath: "/pages/my/my"
}
]
};
},
computed: {
messageNum() {
return this.$store.state.messageNum || 0
}
},
async created() {
if (!this.$store.state.tabbarList.length) {
let isnNetwork = uni.getStorageSync("isnNetwork");
if (isnNetwork) {
await this.getButtonImg()
} else {
uni.getNetworkType({
success: (res) => {
console.log(res.networkType);
if (res.networkType == "none") {
//
...mapState(['messageNum']),
this.$store.commit("setTabbarList", this.tabbarList)
} else {
//
uni.setStorageSync("isnNetwork", 1);
this.getButtonImg()
// ========= =========
// Vuex
currentTabIndex() {
try {
//
const pages = getCurrentPages();
if (pages && pages.length > 0) {
//
const currentPage = pages[pages.length - 1];
//
const currentPath = '/' + (currentPage.route || currentPage.__route__);
// tabbarList
const index = this.tabbarList.findIndex(item => item.pagePath === currentPath);
if (index !== -1) {
return index;
}
}
// Vuex
return this.$store.state.currentTabIndex || 0;
} catch (e) {
return this.$store.state.currentTabIndex || 0;
}
}
// =================================================
},
watch: {
// tabbarList
tabbarList: {
deep: true,
handler() {
this.rebuildTabbar();
}
}
},
created() {
//
this.tabbarReady = false;
this.$nextTick(() => {
this.tabbarReady = true;
//
// this.getButtonImg();
});
}
}
},
methods: {
...mapMutations(['SET_CURRENT_TAB_INDEX']),
//
setCurrentTabIndex(index) {
this.SET_CURRENT_TAB_INDEX(index);
},
// uv-tabbar
rebuildTabbar() {
this.tabbarReady = false;
this.$nextTick(() => {
this.refreshKey++;
this.tabbarReady = true;
});
},
// tab > 0
getNum(index) {
if (index == 1) {
return this.messageNum
} else {
return 0
if (index === 2 && this.messageNum > 0) {
return this.messageNum;
}
return null;
},
//
//
async getButtonImg() {
try {
let res = await request.post("/sj/poster/getButtonImg", {});
if (res.code == 200) {
// if (res.data?.photo) {
// this.topBgImage = res.data?.photo + '?time=666'
// }
if (res.code === 200) {
this.tabbarList.forEach((item, index) => {
if (res.data[index].iconPath) {
item.iconPath = res.data[index].iconPath
if (res.data[index]?.iconPath) {
item.iconPath = res.data[index].iconPath;
}
if (res.data[index].selectedIconPath) {
item.selectedIconPath = res.data[index].selectedIconPath
if (res.data[index]?.selectedIconPath) {
item.selectedIconPath = res.data[index].selectedIconPath;
}
})
});
this.$store.commit("setTabbarList", this.tabbarList);
//
this.rebuildTabbar();
}
} catch (err) {
console.error('getButtonImg 请求失败', err);
}
this.$store.commit("setTabbarList", this.tabbarList)
},
change1(val) {
// pages tabbar
// 使navigateTo 使
// uv-tabbar change
onTabChange(val) {
const idx = parseInt(val, 10);
this.doJump(idx);
},
// item
onItemClick(idx) {
this.doJump(idx);
},
// +
doJump(index) {
const idx = parseInt(index, 10);
if (isNaN(idx) || !this.tabbarList[idx]) return;
// Vuex
this.setCurrentTabIndex(idx);
//
const targetPage = this.tabbarList[idx].pagePath;
uni.switchTab({
url: this.tabbarList[val].pagePath
})
this.messageUpudateNum()
url: targetPage,
fail: (err) => console.error('switchTab 失败', err)
});
//
if (typeof this.messageUpudateNum === 'function') {
this.messageUpudateNum();
}
}
}
};
</script>
<style lang="scss">
.icon {
height: 98rpx;
width: 98rpx;
}
::v-deep .uv-tabbar__content{
z-index: 999!important;
}
.icon {
height: 54rpx;
width: 54rpx;
}
::v-deep .uv-tabbar__content {
z-index: 999 !important;
}
</style>

View File

@ -383,7 +383,7 @@
justify-content: center;
width: 690rpx;
height: 80rpx;
background: #E8101E;
background: #FF4767;
border-radius: 38rpx;
font-weight: 400;
font-size: 30rpx;

View File

@ -169,7 +169,7 @@ export default {
justify-content: center;
width: 690rpx;
height: 80rpx;
background: #E8101E;
background: #FF4767;
border-radius: 38rpx;
font-weight: 400;
font-size: 30rpx;

View File

@ -307,7 +307,7 @@ export default {
&__text {
font-weight: 400;
font-size: 18rpx;
color: #E8101E;
color: #FF4767;
line-height: 25rpx;
text-align: left;
font-style: normal;
@ -371,7 +371,7 @@ export default {
&__immediately {
width: 236rpx;
height: 78rpx;
background: #E8101E;
background: #FF4767;
border-radius: 39rpx;
display: flex;
align-items: center;
@ -405,7 +405,7 @@ export default {
&__active {
height: 28rpx;
background: linear-gradient(270deg, #fe78b3 0%, #E8101E 100%);
background: linear-gradient(270deg, #fe78b3 0%, #FF4767 100%);
border-radius: 39rpx;
}

View File

@ -2,14 +2,21 @@
"name" : "美融融商家",
"appid" : "__UNI__BBE6285",
"description" : "同城便捷到家美业“技术外卖”服务、同城预约到店服务平台美融融为同城爱美人群提供更优质的更全面的服务。",
"versionName" : "1.0.09",
"versionCode" : 1009,
"versionName" : "1.0.22",
"versionCode" : 1022,
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"screenOrientation" : [
//
"portrait-primary", //
"portrait-secondary", //
"landscape-primary", //
"landscape-secondary" //
],
"compatible" : {
"ignoreVersion" : true //trueHBuilderX1.9.0
},
@ -34,7 +41,8 @@
"Payment" : {},
"VideoPlayer" : {},
"OAuth" : {},
"Camera" : {}
"Camera" : {},
"Barcode" : {}
},
/* */
"distribute" : {
@ -69,6 +77,7 @@
},
/* ios */
"ios" : {
"UIRequiresFullScreen" : false, //
"urlschemewhitelist" : [ "iosmap" ],
"dSYMs" : false,
"plistcmds" : [

View File

@ -31,6 +31,20 @@
"enablePullDownRefresh": true
}
},
{
"path": "pages/home/growth-package",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "pages/home/growth-package-result",
"style": {
"navigationBarTitleText": "支付结果",
"navigationStyle": "custom"
}
},
{
"path": "pages/message/message",
"style": {
@ -89,8 +103,12 @@
"path": "pages/order/userorder-list",
"style": {
"navigationBarTitleText": "订单",
"navigationBarBackgroundColor": "#FFFFFF"
"navigationBarBackgroundColor": "#FFFFFF",
"app-plus": {
"softinputNavBar": "none"
}
}
},
{
"path": "pages/order/userorder-detail",
@ -150,7 +168,13 @@
}
},
{
"path": "pages/add/add",
"path": "pages/notice/notice_list",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/notice/detail",
"style": {
"navigationStyle": "custom"
}
@ -221,6 +245,12 @@
"navigationStyle": "custom"
}
},
{
"path": "pages/search_common/search",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/settings/index",
"style": {
@ -584,6 +614,30 @@
"navigationStyle": "custom"
}
},
{
"path": "pages/shop/verify/verify-order",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/shop/verify/manual-verify",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/shop/verify/verify-fail",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/shop/verify/verify-order-detail",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/shop/photoAlbum/photoManage",
"style": {
@ -596,6 +650,14 @@
"navigationStyle": "custom"
}
},
{
"path": "pages/shop/photoAlbum/videoplayer",
"style": {
"navigationBarTitleText": "全屏视频播放",
"pageOrientation": "auto",
"navigationStyle": "custom"
}
},
{
"path": "pages/shop/photoAlbum/videoList",
"style": {
@ -943,6 +1005,43 @@
"style": {
"navigationBarTitleText": "拍摄技巧"
}
},
{
"path": "pages/my/myFavorite",
"style": {
"navigationBarTitleText": "我的收藏",
"navigationStyle": "custom"
}
},
{
"path": "pages/jingxuan/selected-skills",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/jingxuan/selected-brands",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/jingxuan/brand-detail",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/jingxuan/skill-detail",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/ruzhu/successruzhu",
"style": {
"navigationBarTitleText": "入驻成功"
}
}
@ -955,7 +1054,7 @@
},
"tabBar": {
"color": "#999",
"selectedColor": "#E8101E",
"selectedColor": "#FF4767",
"backgroundColor": "#fff",
"list": [{
"iconPath": "/static/images/tabbar/home_icon_dis.png",
@ -963,7 +1062,14 @@
"selectedIconPath": "/static/images/tabbar/home_icon_active.png",
"pagePath": "pages/home/home"
},
{ "iconPath": "/static/images/tabbar/new_message_gray.png",
{
"iconPath": "/static/images/tabbar/new_order_gray.png",
"text": "订单",
"selectedIconPath": "/static/images/tabbar/new_order.png",
"pagePath": "pages/order/userorder-list"
},
{
"iconPath": "/static/images/tabbar/new_message_gray.png",
"text": "消息",
"selectedIconPath": "/static/images/tabbar/new_message.png",
"pagePath": "pages/message/message"

View File

@ -55,7 +55,7 @@
<!-- 设为默认地址 -->
<view class="form-item switch-item">
<text class="label">设为默认地址</text>
<switch :checked="addressInfo.is_default == 1" @change="onSwitchChange" color="#E8101E" />
<switch :checked="addressInfo.is_default == 1" @change="onSwitchChange" color="#FF4767" />
</view>
<!-- 顶部图标 -->
<view class="biao"></view>
@ -622,7 +622,7 @@
.save-btn {
width: 630rpx;
height: 96rpx;
background-color: #E8101E;
background-color: #FF4767;
border-radius: 44rpx;
display: flex;
justify-content: center;

View File

@ -261,7 +261,7 @@
/* .tag {
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
background-color: rgba(252, 67, 124, 0.08);
padding: 6rpx 16rpx;
border-radius: 6rpx;
@ -370,7 +370,7 @@
width: 690rpx;
height: 78rpx;
border-radius: 39rpx;
border: 1rpx solid #E8101E;
border: 1rpx solid #FF4767;
text-align: center;
line-height: 64rpx;
display: flex;
@ -383,7 +383,7 @@
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 30rpx;
color: #E8101E;
color: #FF4767;
line-height: 42rpx;
text-align: left;
font-style: normal;

View File

@ -7,7 +7,7 @@
<!-- <view class="search-container">
<text class="search-container-text" @click="goSelectCity">{{ city.name }}</text>
<search-box v-model="searchKeyword" placeholder="请输入地址" width="571rpx" height="68rpx"
border="1px solid #E8101E" style="padding: 0;" @confirm="onSearch" :showBack="false"></search-box>
border="1px solid #FF4767" style="padding: 0;" @confirm="onSearch" :showBack="false"></search-box>
</view> -->
<view class="search-container">
<text class="search-container-text" @click="goSelectCity">
@ -858,7 +858,7 @@
flex: 1;
height: 68rpx;
background: #FFFFFF;
border: 2rpx solid #E8101E;
border: 2rpx solid #FF4767;
border-radius: 60rpx;
position: relative;
@ -936,7 +936,7 @@
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 26rpx;
color: #E8101E;
color: #FF4767;
line-height: 34rpx;
text-align: center;
font-style: normal;
@ -1124,7 +1124,7 @@
width: 48rpx;
height: 26rpx;
border-radius: 6rpx;
background: #E8101E;
background: #FF4767;
margin-right: 10rpx;
&__text {

View File

@ -55,7 +55,7 @@
<!-- 设为默认地址 -->
<view class="form-item switch-item">
<text class="label">设为默认地址</text>
<switch :checked="addressInfo.is_default == 1" @change="onSwitchChange" color="#E8101E" />
<switch :checked="addressInfo.is_default == 1" @change="onSwitchChange" color="#FF4767" />
</view>
<!-- 顶部图标 -->
<view class="biao"></view>
@ -614,7 +614,7 @@
.save-btn {
width: 630rpx;
height: 96rpx;
background-color: #E8101E;
background-color: #FF4767;
border-radius: 44rpx;
display: flex;
justify-content: center;

View File

@ -666,7 +666,7 @@
flex: 1;
height: 68rpx;
background: #FFFFFF;
border: 2rpx solid #E8101E;
border: 2rpx solid #FF4767;
border-radius: 60rpx;
position: relative;

View File

@ -6,37 +6,22 @@
<!-- 城市选择-->
<view class="search-container" v-if="isSelectCity">
<text class="search-container-text" @click="goSelectCity">{{ city.name }}</text>
<image src="https://mrrplus.oss-cn-beijing.aliyuncs.com/wxstaic/images/Fill.jpg" mode="aspectFit" class="text-img"></image>
<image src="https://mrrplus.oss-cn-beijing.aliyuncs.com/wxstaic/images/Fill.jpg" mode="aspectFit"
class="text-img"></image>
</view>
<!-- 地图容器 -->
<view class="map-container" :style="{ height: mapHeight + 'rpx' }">
<map
v-if="mapVisible"
:key="mapRenderKey"
id="map"
class="map"
:latitude="latitude"
:longitude="longitude"
scale="19"
@tap="onMapTap"
@regionchange="changeMap"
></map>
<map v-if="mapVisible" :key="mapRenderKey" id="map" class="map" :latitude="latitude" :longitude="longitude"
scale="19" @tap="onMapTap" @regionchange="changeMap"></map>
<image
v-if="showMapSnapshot"
class="map-snapshot"
:src="mapSnapshotUrl"
mode="aspectFill"
@load="onSnapshotLoad"
@error="onSnapshotError"
></image>
<image v-if="showMapSnapshot" class="map-snapshot" :src="mapSnapshotUrl" mode="aspectFill"
@load="onSnapshotLoad" @error="onSnapshotError"></image>
<cover-view v-if="mapVisible" class="center-marker">
<cover-image
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/04007cab-3047-42b0-baab-44df344a92d2"
class="center-marker-img"
></cover-image>
class="center-marker-img"></cover-image>
</cover-view>
</view>
@ -44,13 +29,16 @@
<view class="bottom-edit-section">
<!-- 地址识别输入行 -->
<view class="address-input-row">
<textarea class="raw-address-input" v-model="rawAddress" placeholder="粘贴文本到此处,将自动识别地址信息。" placeholder-style="color: #C6C9CC; font-size: 14px;" :maxlength="200" :adjust-position="true" />
<textarea class="raw-address-input" v-model="rawAddress" placeholder="粘贴文本到此处,将自动识别地址信息。"
placeholder-style="color: #C6C9CC; font-size: 14px;" :maxlength="200" :adjust-position="true" />
<view class="action-buttons">
<view class="action-btn paste-btn" @click="hasRawContent ? clearRawAddress() : onPaste()">
<text class="btn-text" :style="{ color: '#333333' }">{{ hasRawContent ? '一键清空' : '粘贴' }}</text>
</view>
<view class="action-btn smart-btn" :class="{ 'smart-btn-active': hasRawContent }" @click="onSmartRecognize">
<text class="btn-text" :style="{ color: hasRawContent ? '#E8101E' : '#999999' }">{{ smartBtnText }}</text>
<view class="action-btn smart-btn" :class="{ 'smart-btn-active': hasRawContent }"
@click="onSmartRecognize">
<text class="btn-text"
:style="{ color: hasRawContent ? '#FF4767' : '#999999' }">{{ smartBtnText }}</text>
</view>
</view>
</view>
@ -61,13 +49,17 @@
<text class="row-label">所在地区</text>
<view class="row-content">
<text :class="currentRegion ? 'content-text' : 'placeholder'">{{ currentRegion || "请选择"}}</text>
<image src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/71c63001-a104-49ad-a90d-30d2ade73f4d.png" mode="aspectFit" class="arrow-icon"></image>
<image
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/71c63001-a104-49ad-a90d-30d2ade73f4d.png"
mode="aspectFit" class="arrow-icon"></image>
</view>
</view>
<view class="line"></view>
<view class="info-row">
<text class="row-label">详细地址</text>
<input type="text" class="detail-input" v-model="detailAddress" @input="onDetailInput" placeholder="请填写详细地址" placeholder-style="color: #999999; font-size: 14px;" :adjust-position="true" />
<input type="text" class="detail-input" v-model="detailAddress" @input="onDetailInput"
placeholder="请填写详细地址" placeholder-style="color: #999999; font-size: 14px;"
:adjust-position="true" />
</view>
<view class="line"></view>
</view>
@ -89,32 +81,19 @@
<text class="popup-close" @click="closeRegionPicker">×</text>
</view>
<view class="region-body">
<picker-view
@change="onRegionChange"
class="picker-scroll"
:indicator-style="indicatorStyle"
:value="regionPickerValue"
>
<picker-view @change="onRegionChange" class="picker-scroll" :indicator-style="indicatorStyle"
:value="regionPickerValue">
<picker-view-column>
<view
class="picker-item"
v-for="(item, index) in regionProvinceList"
:key="index"
>{{ item.province }}</view>
<view class="picker-item" v-for="(item, index) in regionProvinceList" :key="index">
{{ item.province }}</view>
</picker-view-column>
<picker-view-column>
<view
class="picker-item"
v-for="(item, index) in regionCityList"
:key="index"
>{{ item.city }}</view>
<view class="picker-item" v-for="(item, index) in regionCityList" :key="index">
{{ item.city }}</view>
</picker-view-column>
<picker-view-column>
<view
class="picker-item"
v-for="(item, index) in regionDistrictList"
:key="index"
>{{ item.area }}</view>
<view class="picker-item" v-for="(item, index) in regionDistrictList" :key="index">
{{ item.area }}</view>
</picker-view-column>
</picker-view>
</view>
@ -135,7 +114,9 @@
import request from '@/utils/request'
import ChinaCitys from './data/ChinaCitys.json'
import store from '@/store'
import { setVersion } from '@/utils/version.js'
import {
setVersion
} from '@/utils/version.js'
export default {
data() {
@ -263,15 +244,16 @@
}
this.startDependencyInitLock()
if (hasLocation) {
this.applyMapLocation(options.longitude, options.latitude, true)
this.applyMapLocation(options.longitude, options.latitude, false)
return
}
const dependencyCode = options.dependency_code
;(async () => {
;
(async () => {
let center = null
if (dependencyCode && regions[2]) {
@ -294,7 +276,7 @@
this.updateMapCenter(
parseFloat(locationRes[0]),
parseFloat(locationRes[1]),
true
false
)
}
} else if (!skipAutoLocate) {
@ -326,6 +308,7 @@
this.suppressInitDetailFill = true
}
this.reverseGeocode(parseFloat(options.longitude), parseFloat(options.latitude))
return
}
@ -374,7 +357,7 @@
this.blankPageInitLock = false
this.getCurrentLocation(silentLocate, false)
}
},
},
onShow() {
if (!this.isSelectCity) return
@ -504,6 +487,7 @@
this.mapReverseTimer = null
}
if (!noReverse) {
this.mapReverseTimer = setTimeout(() => {
this.reverseGeocode(lng, lat)
@ -556,7 +540,11 @@
},
findRegionIndexByCode(baseData, provinceCode, cityCode, areaCode) {
const result = { provinceIndex: -1, cityIndex: -1, areaIndex: -1 }
const result = {
provinceIndex: -1,
cityIndex: -1,
areaIndex: -1
}
const targetProvinceCode = (provinceCode || '').slice(0, 6)
const targetCityCode = (cityCode || '').slice(0, 6)
const targetAreaCode = (areaCode || '').slice(0, 6)
@ -613,7 +601,10 @@
return new Promise((resolve, reject) => {
const handleFail = (err) => {
if (!silent) {
uni.showToast({ title: '获取位置失败', icon: 'none' })
uni.showToast({
title: '获取位置失败',
icon: 'none'
})
}
reject(err)
}
@ -966,7 +957,10 @@
clearRawAddress() {
this.rawAddress = ''
uni.showToast({ title: '已清空', icon: 'none' })
uni.showToast({
title: '已清空',
icon: 'none'
})
},
onDetailInput(e) {
@ -982,7 +976,10 @@
success: (res) => {
if (res.data) {
this.rawAddress = res.data
uni.showToast({ title: '已粘贴', icon: 'none' })
uni.showToast({
title: '已粘贴',
icon: 'none'
})
}
}
})
@ -1125,15 +1122,27 @@
validateRecognizedDetail(detailText, geo, hit = {}) {
const cleaned = String(detailText || '').trim()
if (!cleaned) {
return { valid: false, cleaned: '', reason: 'empty' }
return {
valid: false,
cleaned: '',
reason: 'empty'
}
}
if (this.isLikelyInvalidRecognizedDetail(cleaned)) {
return { valid: false, cleaned: '', reason: 'invalid_detail' }
return {
valid: false,
cleaned: '',
reason: 'invalid_detail'
}
}
if (!geo) {
return { valid: false, cleaned: '', reason: 'geocode_failed' }
return {
valid: false,
cleaned: '',
reason: 'geocode_failed'
}
}
const geoLevel = String(geo?.level || '').trim()
@ -1156,14 +1165,26 @@
!/[A-Za-z]{6,}/.test(cleaned)
if (hasAddressFeature || preciseLevel) {
return { valid: true, cleaned, reason: '' }
return {
valid: true,
cleaned,
reason: ''
}
}
if (sameDistrict && purePoiLike) {
return { valid: true, cleaned, reason: 'district_geo_verified' }
return {
valid: true,
cleaned,
reason: 'district_geo_verified'
}
}
return { valid: false, cleaned: '', reason: 'detail_not_precise' }
return {
valid: false,
cleaned: '',
reason: 'detail_not_precise'
}
},
cleanRecognizedDetail(detail, province, city, district) {
@ -1425,10 +1446,14 @@
const lonlatParsed = this.parseLonlatInfo(res?.data?.lonlat || {})
return {
province_name: (street.province_name || street.province || stdParsed.province_name || divisionParsed.province_name || '').trim(),
city_name: (street.city_name || street.city || stdParsed.city_name || divisionParsed.city_name || '').trim(),
county_name: (street.county_name || street.district || street.county || stdParsed.county_name || divisionParsed.county_name || '').trim(),
town_name: (street.town_name || street.town || street.street_name || stdParsed.town_name || divisionParsed.town_name || '').trim(),
province_name: (street.province_name || street.province || stdParsed.province_name ||
divisionParsed.province_name || '').trim(),
city_name: (street.city_name || street.city || stdParsed.city_name || divisionParsed.city_name ||
'').trim(),
county_name: (street.county_name || street.district || street.county || stdParsed.county_name ||
divisionParsed.county_name || '').trim(),
town_name: (street.town_name || street.town || street.street_name || stdParsed.town_name ||
divisionParsed.town_name || '').trim(),
road_name: (street.road_name || street.road || stdParsed.road_name || '').trim(),
road_no: (street.road_no || street.roadno || stdParsed.road_no || '').trim(),
poi_name: (street.poi_name || street.poi || stdParsed.poi_name || '').trim(),
@ -1440,9 +1465,11 @@
block_no: (street.block_no || street.block || stdParsed.block_no || '').trim(),
detail: (street.detail || street.address || stdParsed.detail || stdParsed.address || '').trim(),
address: (street.address || street.detail || stdParsed.address || stdParsed.detail || '').trim(),
province_code: (street.province_code || stdParsed.province_code || divisionParsed.province_code || '').trim(),
province_code: (street.province_code || stdParsed.province_code || divisionParsed.province_code ||
'').trim(),
city_code: (street.city_code || stdParsed.city_code || divisionParsed.city_code || '').trim(),
county_code: (street.county_code || street.district_code || stdParsed.county_code || divisionParsed.county_code || '').trim(),
county_code: (street.county_code || street.district_code || stdParsed.county_code || divisionParsed
.county_code || '').trim(),
backend_gcj02: lonlatParsed.gcj02,
backend_wgs84: lonlatParsed.wgs84,
backend_level: lonlatParsed.level,
@ -1680,13 +1707,19 @@
this.suppressInitDetailFill = false
if (!this.hasRawContent) {
uni.showToast({ title: '请先粘贴或输入地址', icon: 'none' })
uni.showToast({
title: '请先粘贴或输入地址',
icon: 'none'
})
return
}
const originalRawText = (this.rawAddress || '').trim()
if (!originalRawText) {
uni.showToast({ title: '请先粘贴或输入地址', icon: 'none' })
uni.showToast({
title: '请先粘贴或输入地址',
icon: 'none'
})
return
}
@ -1697,11 +1730,16 @@
const removedNoise = sanitizedText !== originalRawText
if (!sanitizedText) {
uni.showToast({ title: '未提取到有效地址', icon: 'none' })
uni.showToast({
title: '未提取到有效地址',
icon: 'none'
})
return
}
uni.showLoading({ title: '识别中...' })
uni.showLoading({
title: '识别中...'
})
try {
const res = await this.requestRecognizeAddress(sanitizedText)
@ -1851,7 +1889,8 @@
showCancel: false
})
} else {
const tip = this.getRecognizeToast(level, removedNoise, geocodeOk, hit, detailText, detailRejected)
const tip = this.getRecognizeToast(level, removedNoise, geocodeOk, hit, detailText,
detailRejected)
if (tip.type === 'modal') {
uni.showModal({
title: tip.title,
@ -1868,7 +1907,8 @@
return
}
this.commitRecognizedResult(province, city, district, regionText, detailText, geo, level, removedNoise, geocodeOk, hit, detailRejected)
this.commitRecognizedResult(province, city, district, regionText, detailText, geo, level,
removedNoise, geocodeOk, hit, detailRejected)
} catch (error) {
console.error('智能识别失败:', error)
uni.hideLoading()
@ -2015,7 +2055,8 @@
}
},
commitRecognizedResult(province, city, district, regionText, detailText, geo, level, removedNoise, geocodeOk, hit, detailRejected) {
commitRecognizedResult(province, city, district, regionText, detailText, geo, level, removedNoise, geocodeOk,
hit, detailRejected) {
this.currentProvince = province
this.currentCity = city
this.currentDistrict = district
@ -2165,7 +2206,10 @@
const districtItem = this.regionDistrictList[districtIdx]
if (!provinceItem || !cityItem || !districtItem) {
uni.showToast({ title: '选择无效', icon: 'none' })
uni.showToast({
title: '选择无效',
icon: 'none'
})
return
}
@ -2195,7 +2239,10 @@
const [lng, lat] = center.split(',')
this.updateMapCenter(parseFloat(lng), parseFloat(lat), true)
} else {
uni.showToast({ title: '无法定位该地区,请手动拖动地图', icon: 'none' })
uni.showToast({
title: '无法定位该地区,请手动拖动地图',
icon: 'none'
})
}
this.closeRegionPicker()
@ -2221,7 +2268,8 @@
keywords: adcode.slice(0, 6)
},
success: (res) => {
if (res.data.status === '1' && res.data.districts && res.data.districts.length > 0) {
if (res.data.status === '1' && res.data.districts &&
res.data.districts.length > 0) {
const district = res.data.districts[0]
if (district.center) {
resolveInner(district.center)
@ -2245,7 +2293,8 @@
keywords: keyword
},
success: (res) => {
if (res.data.status === '1' && res.data.districts && res.data.districts.length > 0) {
if (res.data.status === '1' && res.data.districts &&
res.data.districts.length > 0) {
const district = res.data.districts[0]
if (district.center) {
resolveInner(district.center)
@ -2294,13 +2343,14 @@
const source = this.source || currentPage.options.source
//
if(addressItem.cityCode == 500100){
if (addressItem.cityCode == 500100) {
addressItem.cityname = "重庆城区"
}
if(addressItem.cityCode == 500200){
if (addressItem.cityCode == 500200) {
addressItem.cityname = "重庆郊县"
}
if (source === 'syr_info') {
uni.$emit('address-selected-for-syr', addressItem)
uni.setStorageSync('syr_selected_address', addressItem)
@ -2417,7 +2467,8 @@
left: 0;
right: 0;
background-color: #FFFFFF;
padding: 20rpx 20rpx env(safe-area-inset-bottom) 20rpx; /* 适配刘海屏 */
padding: 20rpx 20rpx env(safe-area-inset-bottom) 20rpx;
/* 适配刘海屏 */
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
z-index: 10;
}
@ -2483,8 +2534,8 @@
}
.smart-btn.smart-btn-active {
border-color: #E8101E;
color: #E8101E;
border-color: #FF4767;
color: #FF4767;
}
.address-info-panel {
@ -2555,7 +2606,7 @@
}
.confirm-btn {
background-color: #E8101E;
background-color: #FF4767;
border-radius: 49rpx;
height: 98rpx;
display: flex;
@ -2582,10 +2633,12 @@
opacity: 0;
transition: all 0.3s ease;
}
.popup.show {
visibility: visible !important;
opacity: 1 !important;
}
.popup-mask {
position: fixed;
top: 0;
@ -2594,6 +2647,7 @@
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
}
.popup-content {
position: relative;
background-color: #ffffff;
@ -2603,9 +2657,11 @@
transition: transform 0.3s ease;
max-height: 70vh;
}
.popup.show .popup-content {
transform: translateY(0);
}
.popup-header {
display: flex;
align-items: center;
@ -2614,23 +2670,28 @@
padding: 0 0 20rpx 0;
border-bottom: 1rpx solid #F0F0F0;
}
.popup-title {
font-size: 32rpx;
font-weight: 500;
color: #333;
}
.popup-close {
font-size: 40rpx;
color: #999;
padding: 10rpx;
}
.region-body {
height: 400rpx;
overflow: hidden;
}
.picker-scroll {
height: 400rpx;
}
.picker-item {
height: 68rpx;
line-height: 68rpx;
@ -2638,12 +2699,14 @@
font-size: 28rpx;
color: #333;
}
.popup-footer {
display: flex;
margin-top: 30rpx;
padding-top: 20rpx;
border-top: 1rpx solid #F0F0F0;
}
.popup-btn {
flex: 1;
height: 88rpx;
@ -2653,22 +2716,28 @@
justify-content: center;
margin: 0 16rpx;
}
.popup-btn.cancel {
background-color: #f5f5f5;
}
.popup-btn.confirm {
background: #e8101e;
background: #FF4767;
}
.popup-btn .btn-text {
font-size: 32rpx;
font-weight: 500;
}
.popup-btn.cancel .btn-text {
color: #666666;
}
.popup-btn.confirm .btn-text {
color: #ffffff;
}
/* 指示器样式(沿用原有) */
.indicatorStyle {
height: 68rpx;

View File

@ -274,7 +274,7 @@
flex: 1;
height: 68rpx;
background: #FFFFFF;
border: 2rpx solid #E8101E;
border: 2rpx solid #FF4767;
background-color: #fafafa;
display: flex;
align-items: center;

View File

@ -8,7 +8,7 @@
></custom-navbar>
<!-- <view class="search-container">
<search-box v-model="searchKeyword" placeholder="请输入你的服务地址" width="690rpx" height="68rpx"
border="1px solid #E8101E" style="padding: 0;" @confirm="onSearch" :showBack="false"></search-box>
border="1px solid #FF4767" style="padding: 0;" @confirm="onSearch" :showBack="false"></search-box>
</view>
<scroll-view scroll-y class="hao-scroll">
<view class="hot-citys">

View File

@ -1,822 +1,88 @@
<template>
<view style="z-index: 100;">
<!-- 视频滑动主容器 -->
<view class="view-box" :style="viewTopLength">
<!-- 复用的5个视频容器 - 核心复用节点,不可删减 -->
<view class="video-box" :style="itemViewTopLength0">
<video :src="videoInfo_0.src" :style="fillScreen" :autoplay="videoInfo_0.autoplay"
:loop="videoInfo_0.loop" :controls="videoInfo_0.controls"
:enable-play-gesture="videoInfo_0.enablePlayGesture" :objectFit="videoInfo_0.objectFit || objectFit"
:show-fullscreen-btn="videoInfo_0.showFullscreenBtn"
:enable-progress-gesture="videoInfo_0.enableProgressGesture" @timeupdate="timeupdate(0,$event)"
@ended='ended' :show-play-btn="videoInfo_0.showPlayBtn" :show-progress="videoInfo_0.showProgress"
initial-time="0" id='video_id_0' @play="playing(0)" :muted="videoInfo_0.isMuted"
webkit-playsinline="true" playsinline="true">
</video>
<cover-view v-if="!videoInfo_0.flag" :style="stopIcon" class="cover-view-stop" />
<cover-view v-if="cover && videoInfo_0.coverStatus" :style="fillScreen2" class="video-image">
<image :mode="videoInfo_0.mode || mode" :src="videoInfo_0.coverUrl" :style="fillScreen"></image>
</cover-view>
<view class="video-action" @touchstart.stop='videotouchstart' @touchmove.stop="videotochmove"
@touchend.stop='videotouchend' @click.stop="videoClick"></view>
<!-- 视频文本区域 - 每个视频都有 -->
<view class="video-texts">
<view class="flex-row-center" style="flex: 1;">
<text class="video-title">{{ videoInfo_0.src }}</text>
<text class="video-score">评分 4.8</text>
</view>
<view class="flex-row-center-between" style="flex: 1;">
<text class="video-text">{{videoInfo_0.name}}</text>
<!-- 声音图标 - 绑定0号视频的状态点击切换静音 -->
<image class="video-sound"
:src="videoInfo_0.isMuted ? '/static/images/icons/sound_no.png' : '/static/images/icons/sound_yes.png'"
@click.stop="toggleMute(0)"></image>
</view>
</view>
</view>
<view class="video-box" :style="itemViewTopLength1">
<video :src="videoInfo_1.src" :style="fillScreen" :autoplay="videoInfo_1.autoplay"
:loop="videoInfo_1.loop" :controls="videoInfo_1.controls"
:enable-play-gesture="videoInfo_1.enablePlayGesture" :objectFit="videoInfo_1.objectFit || objectFit"
:show-fullscreen-btn="videoInfo_1.showFullscreenBtn"
:enable-progress-gesture="videoInfo_1.enableProgressGesture" @timeupdate="timeupdate(1,$event)"
@ended='ended' :show-play-btn="videoInfo_1.showPlayBtn" :show-progress="videoInfo_1.showProgress"
initial-time="0" id='video_id_1' @play="playing(1)" :muted="videoInfo_1.isMuted"
webkit-playsinline="true" playsinline="true">
</video>
<cover-view v-if="!videoInfo_1.flag" :style="stopIcon" class="cover-view-stop" />
<cover-view v-if="cover && videoInfo_1.coverStatus" :style="fillScreen2" class="video-image">
<image :mode="videoInfo_1.mode || mode" :src="videoInfo_1.coverUrl" :style="fillScreen"></image>
</cover-view>
<view class="video-action" @touchstart='videotouchstart' @touchmove="videotochmove"
@touchend='videotouchend' @click="videoClick"></view>
<!-- 视频文本区域 - 1号视频 -->
<view class="video-texts">
<view class="flex-row-center" style="flex: 1;">
<text class="video-title">珍草品汇理疗馆珍馆服务流程11订单</text>
<text class="video-score">评分 4.8</text>
</view>
<view class="flex-row-center-between" style="flex: 1;">
<text class="video-text">{{videoInfo_1.name}}</text>
<!-- 声音图标 - 绑定1号视频的状态 -->
<image class="video-sound"
:src="videoInfo_1.isMuted ? '/static/images/icons/sound_no.png' : '/static/images/icons/sound_yes.png'"
@click.stop="toggleMute(1)"></image>
</view>
</view>
</view>
<view class="video-box" :style="itemViewTopLength2">
<video :src="videoInfo_2.src" :style="fillScreen" :autoplay="videoInfo_2.autoplay"
:loop="videoInfo_2.loop" :controls="videoInfo_2.controls"
:enable-play-gesture="videoInfo_2.enablePlayGesture" :objectFit="videoInfo_2.objectFit || objectFit"
:show-fullscreen-btn="videoInfo_2.showFullscreenBtn"
:enable-progress-gesture="videoInfo_2.enableProgressGesture" @timeupdate="timeupdate(2,$event)"
@ended='ended' :show-play-btn="videoInfo_2.showPlayBtn" :show-progress="videoInfo_2.showProgress"
initial-time="0" id='video_id_2' @play="playing(2)" :muted="videoInfo_2.isMuted"
webkit-playsinline="true" playsinline="true">
</video>
<cover-view v-if="!videoInfo_2.flag" :style="stopIcon" class="cover-view-stop" />
<cover-view v-if="cover && videoInfo_2.coverStatus" :style="fillScreen2" class="video-image">
<image :mode="videoInfo_2.mode || mode" :src="videoInfo_2.coverUrl" :style="fillScreen"></image>
</cover-view>
<view class="video-action" @touchstart='videotouchstart' @touchmove="videotochmove"
@touchend='videotouchend' @click="videoClick"></view>
<!-- 视频文本区域 - 2号视频 -->
<view class="video-texts">
<view class="flex-row-center" style="flex: 1;">
<text class="video-title">珍草品汇理疗馆珍馆服务流程11订单</text>
<text class="video-score">评分 4.8</text>
</view>
<view class="flex-row-center-between" style="flex: 1;">
<text class="video-text">{{videoInfo_2.name}}</text>
<!-- 声音图标 - 绑定2号视频的状态 -->
<image class="video-sound"
:src="videoInfo_2.isMuted ? '/static/images/icons/sound_no.png' : '/static/images/icons/sound_yes.png'"
@click.stop="toggleMute(2)"></image>
</view>
</view>
</view>
<view class="video-box" :style="itemViewTopLength3">
<video :src="videoInfo_3.src" :style="fillScreen" :autoplay="videoInfo_3.autoplay"
:loop="videoInfo_3.loop" :controls="videoInfo_3.controls"
:enable-play-gesture="videoInfo_3.enablePlayGesture" :objectFit="videoInfo_3.objectFit || objectFit"
:show-fullscreen-btn="videoInfo_3.showFullscreenBtn"
:enable-progress-gesture="videoInfo_3.enableProgressGesture" @timeupdate="timeupdate(3,$event)"
@ended='ended' :show-play-btn="videoInfo_3.showPlayBtn" :show-progress="videoInfo_3.showProgress"
initial-time="0" id='video_id_3' @play="playing(3)" :muted="videoInfo_3.isMuted"
webkit-playsinline="true" playsinline="true">
</video>
<cover-view v-if="!videoInfo_3.flag" :style="stopIcon" class="cover-view-stop" />
<cover-view v-if="cover && videoInfo_3.coverStatus" :style="fillScreen2" class="video-image">
<image :mode="videoInfo_3.mode || mode" :src="videoInfo_3.coverUrl" :style="fillScreen"></image>
</cover-view>
<view class="video-action" @touchstart='videotouchstart' @touchmove="videotochmove"
@touchend='videotouchend' @click="videoClick"></view>
<!-- 视频文本区域 - 3号视频 -->
<view class="video-texts">
<view class="flex-row-center" style="flex: 1;">
<text class="video-title">珍草品汇理疗馆珍馆服务流程11订单</text>
<text class="video-score">评分 4.8</text>
</view>
<view class="flex-row-center-between" style="flex: 1;">
<text class="video-text">{{videoInfo_3.name}}</text>
<!-- 声音图标 - 绑定3号视频的状态 -->
<image class="video-sound"
:src="videoInfo_3.isMuted ? '/static/images/icons/sound_no.png' : '/static/images/icons/sound_yes.png'"
@click.stop="toggleMute(3)"></image>
</view>
</view>
</view>
<view class="video-box" :style="itemViewTopLength4">
<video :src="videoInfo_4.src" :style="fillScreen" :autoplay="videoInfo_4.autoplay"
:loop="videoInfo_4.loop" :controls="videoInfo_4.controls"
:enable-play-gesture="videoInfo_4.enablePlayGesture" :objectFit="videoInfo_4.objectFit || objectFit"
:show-fullscreen-btn="videoInfo_4.showFullscreenBtn"
:enable-progress-gesture="videoInfo_4.enableProgressGesture" @timeupdate="timeupdate(4,$event)"
@ended='ended' :show-play-btn="videoInfo_4.showPlayBtn" :show-progress="videoInfo_4.showProgress"
initial-time="0" id='video_id_4' @play="playing(4)" :muted="videoInfo_4.isMuted"
webkit-playsinline="true" playsinline="true">
</video>
<cover-view v-if="!videoInfo_4.flag" :style="stopIcon" class="cover-view-stop" />
<cover-view v-if="cover && videoInfo_4.coverStatus" :style="fillScreen2" class="video-image">
<image :mode="videoInfo_4.mode || mode" :src="videoInfo_4.coverUrl" :style="fillScreen"></image>
</cover-view>
<view class="video-action" @touchstart='videotouchstart' @touchmove="videotochmove"
@touchend='videotouchend' @click="videoClick"></view>
<!-- 视频文本区域 - 4号视频 -->
<view class="video-texts">
<view class="flex-row-center" style="flex: 1;">
<text class="video-title">珍草品汇理疗馆珍馆服务流程11订单</text>
<text class="video-score">评分 4.8</text>
</view>
<view class="flex-row-center-between" style="flex: 1;">
<text class="video-text">{{videoInfo_4.name}}</text>
<!-- 声音图标 - 绑定4号视频的状态 -->
<image class="video-sound"
:src="videoInfo_4.isMuted ? '/static/images/icons/sound_no.png' : '/static/images/icons/sound_yes.png'"
@click.stop="toggleMute(4)"></image>
</view>
</view>
</view>
<view @touchend='progressTouchend' class="progress-view">
<uv-slider v-if="showSlider" v-model="progressValue" activeColor="#D8D8D8" block-size="6"
backgroundColor="#494949;" class="progress-slider"></uv-slider>
</view>
<!-- 修改Dot指示器 - 最多显示5个 -->
<view class="dots" :style="{top:`${0.5*winHeight}px`}" v-if="videoListLength > 1">
<view class="dots-container">
<view class="dot-item" v-for="(dotIndex, idx) in visibleDots" :key="idx"
:class="{ active: dotIndex === videoIndex }" @click="jumpToVideo(dotIndex)"></view>
</view>
<text class="pageNumber">
{{ videoIndex+1 }}/{{ videoListLength }}
</text>
</view>
<view class="video-page">
<!-- 原生视频组件:全端兼容 -->
<video id="fullVideo" class="video-box" :src="videoUrl" controls show-fullscreen-btn enable-progress-gesture
:autoplay="false" :loop="false" :muted="false" @play="onPlay" @pause="onPause"
@fullscreenchange="handleFullScreenChange" @ended="onVideoEnd"></video>
<!-- 自定义操作按钮 -->
<view class="btn-wrap">
<button type="primary" size="default" @click="openFullScreen">
手动横屏全屏播放
</button>
<button type="warn" size="default" @click="closeFullScreen">
退出全屏
</button>
</view>
</view>
</template>
<script>
let VIDEO_LIST = []
let initVideoInfo = {
src: "",
coverStatus: true,
coverUrl: "",
flag: false,
objectFit: "",
mode: "",
isMuted: false, // 默认不静音
autoplay: false,
loop: true,
controls: false,
enablePlayGesture: true,
showFullscreenBtn: false,
enableProgressGesture: false,
showPlayBtn: false,
showProgress: false
}
export default {
props: {
popVideoList: {
type: Array,
default: [] // 默认值设为空数组JSON避免parse报错
},
marginTop: {
type: String,
default: "88" // 默认值设为空数组JSON避免parse报错
}
},
data() {
return {
playSetTime:null,
showSlider: true,
progressTime: 0,
progressValue: 0,
boxHeight: 0,
objectFit: "fill",
mode: "scaleToFill",
winWidth: 1,
winHeight: 1,
videoIndex: 0,
viewTop: 0,
viewTopPostion: 0,
videoList: [],
videoListLength: 0,
itemViewTop0: 0,
itemViewTop1: 0,
itemViewTop2: 0,
itemViewTop3: 0,
itemViewTop4: 0,
videoInfo_0: initVideoInfo,
videoInfo_1: initVideoInfo,
videoInfo_2: initVideoInfo,
videoInfo_3: initVideoInfo,
videoInfo_4: initVideoInfo,
stopIcon: '',
videotouchstartTimestamp: 0,
videotouchendTimestamp: 0,
moveView: true,
cover: true,
slidingStatus: false,
videoPlayingStatus: false,
preloadedIndex: [],
startPageY: 0,
endPageY: 0,
// 新增控制可视dot的数组
visibleDots: [],
// 测试视频地址,替换自己的
videoUrl: "https://www.w3school.com.cn/i/movie.mp4",
videoContext: null
};
},
watch: {
// videoList: {
// handler(newVal, oldVal) {
// },
// immediate: true, // 可选:组件初始化时立即执行一次(即使值未变化)
// deep: false // 普通类型无需深度监听默认false
// }
},
created(options) {
//#ifdef APP-PLUS
plus.screen.lockOrientation("portrait-primary")
//#endif
if (this.popVideoList.length) {
try {
VIDEO_LIST = []
this.popVideoList.forEach((item, index) => {
let obj = {
"src": "",
"coverStatus": true,
"coverUrl": "",
"flag": false,
"objectFit": "contain",
"mode": "aspectFit",
"isMuted": false, // 默认不静音
"autoplay": false,
"loop": true,
"controls": false,
"enablePlayGesture": true,
"showFullscreenBtn": false,
"enableProgressGesture": false,
"showPlayBtn": false,
"showProgress": false,
"name": ""
}
obj.name = item.name
obj.src = item.url
obj.coverUrl = item.url +
`?x-oss-process=video/snapshot,t_1000,m_fast,f_jpg,w_600,ar_auto`
VIDEO_LIST.push(obj)
if (index == 0) {
initVideoInfo = obj
}
})
} catch (e) {}
this.getSystemInfo();
this.videoList = VIDEO_LIST;
this.videoListLength = this.videoList.length;
this.boxHeight = this.videoListLength * this.winHeight;
for (let i = 0; i < 5; i++) {
if (this.videoList[i]) {
this['videoInfo_' + i] = this.videoList[i]
}
}
// 初始化可视dot范围
this.updateVisibleDots();
// setTimeout(() => {
// this.playVideo(0)
// }, 500)
}
},
onUnload() {
for (let i = 0; i < 5; i++) {
this.unloadVideo(i)
}
},
computed: {
getVideoTop() {
return uni.upx2px(this.marginTop)
},
viewTopLength() {
return `height:${this.boxHeight}px;top:${this.viewTop}px;`
},
fillScreen() {
let style = `height:${this.winHeight}px;width:${this.winWidth}px;`
return style
},
fillScreen2() {
let style = `height:auto;width:${this.winWidth}px;`
return style
},
itemViewTopLength0() {
return `height:${this.winHeight}px;top:${this.itemViewTop0}px;`
},
itemViewTopLength1() {
return `height:${this.winHeight}px;top:${this.itemViewTop1}px;`
},
itemViewTopLength2() {
return `height:${this.winHeight}px;top:${this.itemViewTop2}px;`
},
itemViewTopLength3() {
return `height:${this.winHeight}px;top:${this.itemViewTop3}px;`
},
itemViewTopLength4() {
return `height:${this.winHeight}px;top:${this.itemViewTop4}px;`
}
onReady() {
// 初始化视频实例
this.videoContext = uni.createVideoContext("fullVideo", this);
},
methods: {
// 新增:切换视频静音状态
toggleMute(videoNum) {
// 1. 切换数据层状态
this['videoInfo_' + videoNum].isMuted = !this['videoInfo_' + videoNum].isMuted;
// 2. 同步视频实例静音状态(恢复并完善原有注释代码,加容错)
const videoCtx = uni.createVideoContext("video_id_" + videoNum, this);
if (videoCtx) {
videoCtx.mute(this['videoInfo_' + videoNum].isMuted);
}
// 进入横屏全屏
openFullScreen() {
// direction 90横屏-90反向横屏0自适应
this.videoContext.requestFullScreen({
direction: 90
});
},
// 修改点击dot跳转 - 传真实的视频索引
jumpToVideo(targetIndex) {
if (targetIndex === this.videoIndex || this.slidingStatus) return;
this.stopVideo(this.videoIndex % 5, true);
const diff = targetIndex - this.videoIndex;
this.videoIndex = targetIndex;
this.viewTopPostion = -this.videoIndex * this.winHeight;
this.viewTop = this.viewTopPostion;
if (this.videoListLength > 5) {
this.sortItemView(diff > 0 ? -1 : 1);
}
// 更新可视dot范围
this.updateVisibleDots();
setTimeout(() => {
this.playVideo(this.videoIndex % 5);
}, 400);
// 退出全屏
closeFullScreen() {
this.videoContext.exitFullScreen();
},
// 核心更新可视dot范围 - 最多显示5个
updateVisibleDots() {
const maxShow = 5;
const total = this.videoListLength;
// 视频总数 ≤ 5显示全部
if (total <= maxShow) {
this.visibleDots = Array.from({
length: total
}, (_, i) => i);
return;
}
// 视频总数 > 5显示包含当前索引的5个dot
// 计算起始索引,避免越界
let start = Math.max(0, Math.min(this.videoIndex - 2, total - maxShow));
// 生成5个连续的索引
this.visibleDots = Array.from({
length: maxShow
}, (_, i) => start + i);
// 播放监听
onPlay() {
console.log("视频开始播放");
},
progressTouchend() {
let s = Math.round(this.progressValue / 100 * this.time)
let i = this.videoIndex % 5
uni.createVideoContext("video_id_" + i, this).seek(s)
uni.createVideoContext("video_id_" + i, this).play()
// 暂停监听
onPause() {
console.log("视频暂停");
},
sliderchanging(e) {
console.log(3333, e)
},
getSystemInfo() {
var _this = this;
uni.getSystemInfo({
success: function(res) {
_this.winWidth = res.windowWidth;
_this.winHeight = res.windowHeight;
let p = uni.upx2px(150)
_this.stopIcon = `left:${(res.windowWidth - p)/2}px;top:${(res.windowHeight - p)/2}px;`
_this.itemViewTop1 = res.windowHeight
_this.itemViewTop2 = res.windowHeight * 2
_this.itemViewTop3 = res.windowHeight * 3
_this.itemViewTop4 = res.windowHeight * 4
},
})
},
unloadVideo(index) {
if (index < 0 || index >= this.videoListLength) return;
const nodeNum = index % 5;
const videoCtx = uni.createVideoContext("video_id_" + nodeNum, this);
videoCtx.stop();
this.preloadedIndex = this.preloadedIndex.filter(item => item !== index);
},
playVideo(num) {
clearTimeout(this.playSetTime)
uni.$uv.debounce(() => {
this.videoPlayingStatus = true
this["videoInfo_" + num].flag = true
let VideoContext = uni.createVideoContext("video_id_" + num, this)
this.playSetTime = setTimeout(() => {
VideoContext.play()
}, 400)
}, 30)
},
stopVideo(num, control = false) {
const videoCtx = uni.createVideoContext("video_id_" + num, this);
if (!control) {
this['videoInfo_' + num].coverStatus = true;
videoCtx.stop()
// 全屏状态切换监听
handleFullScreenChange(e) {
if (e.detail.fullScreen) {
uni.setNavigationBarHidden(true); // 全屏隐藏顶部导航
} else {
videoCtx.pause()
uni.setNavigationBarHidden(false); // 退出恢复导航
}
this["videoInfo_" + num].flag = false
console.log("当前全屏状态:", e.detail.fullScreen);
},
nowPlayVideo() {
clearTimeout(this.playSetTime)
uni.$uv.debounce(() => {
this.videoPlayingStatus = true
this["videoInfo_" + this.videoIndex].flag = true
let VideoContext = uni.createVideoContext("video_id_" + this.videoIndex, this)
this.playSetTime = setTimeout(() => {
VideoContext.play()
}, 400)
}, 30)
},
nowStopVideo() {
const videoCtx = uni.createVideoContext("video_id_" + this.videoIndex, this);
videoCtx.pause()
this["videoInfo_" + this.videoIndex].flag = false
},
videoClick() {
let num = this.videoIndex % 5
if (this["videoInfo_" + num].flag) {
this.stopVideo(num, true)
} else {
this.playVideo(num, true)
}
},
videotouchstart(e) {
this.videotouchendTimestamp = this.videotouchstartTimestamp
this.videotouchstartTimestamp = e.timestamp
if ((this.videotouchstartTimestamp - this.videotouchendTimestamp) < 500) return
if (this.slidingStatus) return
this.startPageY = e.changedTouches[0].screenY;
},
videotochmove(e) {
if ((this.videotouchstartTimestamp - this.videotouchendTimestamp) < 500) return
if (this.slidingStatus) return
this.endPageY = e.changedTouches[0].screenY;
let sliding = this.endPageY - this.startPageY
if (sliding > 20 || sliding < -20) {
this.moveView = false
} else {
this.moveView = true
}
if (this.videoIndex == 0 && sliding > 50) {
this.viewTop = 50
} else if (this.videoIndex == this.videoListLength - 1 && sliding < 0) {
this.viewTop = this.viewTopPostion
} else {
this.viewTop = this.viewTopPostion + sliding
}
},
videotouchend(e) {
if ((this.videotouchstartTimestamp - this.videotouchendTimestamp) < 500) return
if (this.slidingStatus) return
this.moveView = true
this.endPageY = e.changedTouches[0].screenY;
let sliding = this.endPageY - this.startPageY
if ((sliding >= -40 && sliding < 0) || (sliding <= 40 && sliding > 0)) {
this.slidingStatus = true
this.videoSlidingReset(sliding)
return
}
if (sliding < -30) {
this.slidingStatus = true
if (this.videoIndex == this.videoListLength - 1) {
this.videoSlidingReset(sliding)
uni.showToast({
title: "已经到底了",
icon: "none"
})
return
}
this.moveView = false
this.videoSlidingToTop(sliding)
return
}
if (sliding > 30) {
this.slidingStatus = true
if (this.videoIndex == 0) {
this.videoSlidingReset(sliding)
return
}
this.moveView = false
this.videoSlidingToBottom(sliding)
}
},
videoSlidingReset(sliding) {
this.viewTop = this.viewTopPostion
this.slidingStatus = false
},
videoSlidingToTop(sliding) {
this.stopVideo(this.videoIndex % 5, true)
this.videoIndex++
if (this.videoListLength > 5) {
this.sortItemView(sliding)
}
this.viewTopPostion = this.viewTopPostion - this.winHeight
setTimeout(() => {
this.playVideo(this.videoIndex % 5)
}, 5)
let timer = setInterval(() => {
this.viewTop = this.viewTop - 35
if (this.viewTop < (this.viewTopPostion + 10)) {
this.viewTop = this.viewTopPostion
clearInterval(timer)
this.slidingStatus = false
this.slidingCallback()
}
}, 3)
},
videoSlidingToBottom(sliding) {
this.stopVideo(this.videoIndex % 5, true)
this.videoIndex--
if (this.videoListLength > 5) {
this.sortItemView(sliding)
}
this.viewTopPostion = this.viewTopPostion + this.winHeight
setTimeout(() => {
this.playVideo(this.videoIndex % 5)
}, 5)
let timer = setInterval(() => {
this.viewTop = this.viewTop + 35
if (this.viewTop > (this.viewTopPostion - 10)) {
this.viewTop = this.viewTopPostion
clearInterval(timer)
this.slidingStatus = false
this.slidingCallback()
}
}, 3)
},
// 修改滑动结束后更新可视dot
slidingCallback() {
this.progressValue = 0
this.updateVisibleDots();
},
//滑动组件排序
sortItemView(silding) {
// 向上滑动
if (silding < 0) {
if (this.videoIndex > 2 && this.videoIndex < this.videoListLength - 2) {
let i = (this.videoIndex - 3) % 5
this["itemViewTop" + i] = this["itemViewTop" + i] + this.winHeight * 5
this['videoInfo_' + i] = this.videoList[this.videoIndex + 2]
}
return
}
// 向下滑动
if (silding > 0) {
if (this.videoIndex > 1 && this.videoIndex < this.videoListLength - 3) {
let i = (this.videoIndex + 3) % 5
this["itemViewTop" + i] = this["itemViewTop" + i] - this.winHeight * 5
this['videoInfo_' + i] = this.videoList[this.videoIndex - 2]
}
}
},
playing(i) {
setTimeout(() => {
this['videoInfo_' + i].coverStatus = false
this.moveView = true
this.slidingStatus = false
}, 600)
},
timeupdate(i, e) {
if (this.videoPlayingStatus) {
this.slidingStatus = false
this.videoPlayingStatus = false
setTimeout(() => {
this['videoInfo_' + i].coverStatus = false
this.moveView = true
}, 200)
}
this["videoInfo_" + i].flag = true
if (e.detail.duration >= this.progressTime) {
if (this.videoIndex%5 == i) {
this.time = e.detail.duration
this.progress = e.detail.currentTime
this.progressValue = (e.detail.currentTime / e.detail.duration) * 100
}
this.showProgress = true
} else {
this.showProgress = false
}
},
ended() {
let num = this.videoIndex % 5
this["videoInfo_" + num].flag = false
this.progressValue = 0
}
// 视频播放结束
onVideoEnd() {
// 播放完毕自动退出全屏
this.videoContext.exitFullScreen();
}
}
};
</script>
<style scoped lang="less">
.view-box {
// position: fixed;
// left: 0;
// right: 0;
background-color: #000;
// z-index: 900;
<style scoped>
.video-page {
padding: 15rpx;
}
.video-box {
position: absolute;
left: 0;
right: 0;
z-index: 2000;
}
.video-image {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 2001;
}
.video-action {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 2008;
}
.video-texts {
position: absolute;
left: 30rpx;
right: 30rpx;
bottom: 120rpx;
z-index: 2009; // 确保在视频上方显示
.video-title {
font-weight: 500;
font-size: 24rpx;
color: #FFFFFF;
text-align: left;
font-style: normal;
lines: 1;
max-width: 240rpx;
}
.video-score {
font-weight: 500;
font-size: 24rpx;
color: #FFFFFF;
text-align: left;
font-style: normal;
margin-left: 20rpx;
}
.video-text {
font-weight: 400;
font-size: 28rpx;
color: #FFFFFF;
text-align: left;
font-style: normal;
}
.video-sound {
width: 44rpx;
height: 42rpx;
cursor: pointer; // 点击指针,提示可点击
}
}
.cover-view-stop {
position: absolute;
z-index: 9999;
width: 150rpx;
height: 200rpx;
background-color: rgba(0, 0, 0, 0.3);
border-radius: 50%;
}
.progress-view {
z-index: 9999;
position: fixed;
left: 30rpx;
right: 30rpx;
bottom: 80rpx;
.progress-slider {}
}
// 修改Dot指示器样式 - 最多显示5个 + 过渡动画
.dots {
position: fixed;
left: 20rpx;
top: 0;
transform: translateY(-50%);
z-index: 9999;
flex-direction: row;
.dots-container {
// 限制最多显示5个超出隐藏
width: fit-content;
width: 100%;
height: 420rpx;
border-radius: 12rpx;
overflow: hidden;
margin: auto 0;
.dot-item {
width: 10rpx;
height: 10rpx;
background: #FFFFFF;
border-radius: 50%;
// 核心:过渡动画,切换更丝滑
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
margin-bottom: 10rpx;
&.active {
width: 10rpx;
height: 30rpx;
background: #FFFFFF;
border-radius: 5rpx;
}
}
}
.pageNumber {
font-weight: bold;
font-size: 28rpx;
color: #FFFFFF;
line-height: 36rpx;
text-align: left;
font-style: normal;
margin-left: 6rpx;
}
}
.flex-row-center-between {
.btn-wrap {
margin-top: 30rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.flex-row-start-between {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
}
.flex-row-center-center {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.flex-row-center {
display: flex;
flex-direction: row;
align-items: center;
}
.flex-row-start {
display: flex;
flex-direction: row;
align-items: flex-start;
}
.flex-row-end-between {
display: flex;
flex-direction: row;
align-items: flex-end;
justify-content: space-between;
gap: 20rpx;
}
</style>

View File

@ -0,0 +1,106 @@
<template>
<view class="video-page">
<!-- 原生视频组件全端兼容 -->
<video id="fullVideo" class="video-box" :src="videoUrl" controls show-fullscreen-btn enable-progress-gesture
:autoplay="true" :loop="false" :muted="false" @play="onPlay" @pause="onPause" object-fit="cover" :direction="90"
@fullscreenchange="handleFullScreenChange" @ended="onVideoEnd" @fullscreenclick="tapback"></video>
</view>
</template>
<script>
export default {
data() {
return {
videoUrl: "",
videoContext: null
};
},
onLoad(options) {
console.log(options, '页面参数');
//
if (options.url) {
this.videoUrl = options.url;
}
},
onReady() {
// #ifdef APP-PLUS
//plus.screen.unlockOrientation(); //
// #endif
//
this.videoContext = uni.createVideoContext("fullVideo", this);
//
//this.autoFullScreen();
},
methods: {
tapback(e) {
console.log('点击全屏按钮');
// if (e.target.fullScreen) {
// plus.screen.lockOrientation('landscape'); //
// }else{
// plus.screen.lockOrientation('portrait'); //
// }
},
// iOS
autoFullScreen() {
if (!this.videoContext) return;
// iOS090
const direction = uni.getSystemInfoSync().platform === 'ios' ? 0 : 90;
//
setTimeout(() => {
this.videoContext.requestFullScreen({
direction: direction
});
}, 300)
},
//
openFullScreen() {
if (!this.videoContext) return;
const direction = uni.getSystemInfoSync().platform === 'ios' ? 0 : 90;
this.videoContext.requestFullScreen({
direction
});
},
// 退
closeFullScreen() {
if (!this.videoContext) return;
this.videoContext.exitFullScreen();
},
//
onPlay() {
console.log("视频开始播放");
},
//
onPause() {
console.log("视频暂停");
},
//
handleFullScreenChange(e) {
// 退
// uni.setNavigationBarHidden(!!e.detail.fullScreen);
console.log("全屏状态:", e.detail.fullScreen);
},
//
onVideoEnd() {
this.closeFullScreen();
}
}
};
</script>
<style scoped>
.video-page {
width: 100%;
height: 100vh;
box-sizing: border-box;
}
.video-box {
width: 100%;
/* 自适应高度,避免拉伸 */
height: 100%;
object-fit: cover;
}
</style>

View File

@ -1,52 +1,59 @@
<template>
<view class="container">
<view class="positonFixed">
<view class="header-top flex-row-center" :class="{videoTop:current==0}"
:style="{ height: `${88+statusBarHeight}rpx`,paddingTop: `${statusBarHeight}rpx`}">
<view class="header-top flex-row-center" :style="{ height: `${100 + statusBarHeight}rpx`,paddingTop:`${statusBarHeight}rpx`}">
<view class="left-area" @click="goBack">
<image src="/static/images/whiteBack.png" mode="aspectFit" class="back-icon"></image>
<image src="/static/images/back.png" mode="aspectFit" class="back-icon"></image>
</view>
<view class="flex-row-center-center" style="flex: 1;margin-right: 18rpx;height:66rpx;">
<uv-tabs :list="list" @click="tabsClick" itemStyle="height:66rpx;" :activeStyle="activeStyle"
:inactiveStyle="inactiveStyle" :current="current" :lineColor="`rgba(232, 16, 30, 1)`"></uv-tabs>
</view>
</view>
<scroll-view scroll-x style="height: 100rpx;" v-if="current==1">
<scroll-view scroll-x style="height: 100rpx;background-color: #fff;" v-if="current==1">
<view class="container-tabs flex-row-center">
<view class="container-tab flex-row-center-center" @click="clickTab(item)"
:class="{active:cardListIndex==item.id}" v-for="(item,index) in cardList[1]" :key="index">
:class="{active:cardListIndex==item.id}" v-for="(item,index) in cardList[user_type]"
:key="index">
<text class="container-tab-text">{{ item.title }}</text>
</view>
</view>
</scroll-view>
</view>
<view :style="swiperStyle3"></view>
<view class="container-content" :style="swiperStyle2">
<swiper class="swiper" circular :style="swiperStyle" :current="current" @change="swiperChange" :disable-touch="current==0">
<swiper class="swiper" circular :style="swiperStyle" :current="current" @change="swiperChange"
:disable-touch="current==0">
<swiper-item>
<videoSlide ref="ideoSlideRrf" :popVideoList="videoList" :marginTop="`${88 + this.statusBarHeight}`" v-show="current==0" v-if="videoList.length"></videoSlide>
<scroll-view scroll-y :style="{height:`${viewportHeight - 100 - statusBarHeight}rpx`}">
<view class="swiper-items3">
<view class="swiper-item3" :class="{item2Left:index%2 == 0}"
v-for="(item,index) in videoList" :key="index" @click="tovideo(item)">
<image class="swiper-item3-video" mode="aspectFill" :src="item.img"></image>
<image
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/c6c039fc-99fd-492e-ae9a-ed43260c94ef.png"
mode="widthFix"
style="width: 80rpx;position: absolute;top: 50%;left: 50%;transform: translate3d(-50%, -50%, 0);">
</image>
</view>
</view>
</scroll-view>
</swiper-item>
<swiper-item>
<scroll-view scroll-y :style="{height:`${viewportHeight-88-this.statusBarHeight-100}rpx`}">
<scroll-view scroll-y :style="{height:`${viewportHeight - 100 - statusBarHeight - 100}rpx`}">
<view class="swiper-items2">
<view class="swiper-item2" :class="{item2Left:index%2 == 0}"
v-for="(item,index) in imagObj[cardListIndex]" :key="index">
v-for="(item,index) in imagObj[cardListIndex]" :key="index" @click="toimg(item)">
<image class="swiper-item2-img" mode="aspectFill" :src="item.url"></image>
<view class="swiper-item2-title">
<text class="swiper-item2-title-text">{{ item.name }}</text>
</view>
</view>
</view>
</scroll-view>
</swiper-item>
</swiper>
</view>
</view>
</template>
@ -68,6 +75,23 @@
title: "主图",
id: 1
},
{
title: "作品",
id: 3
},
{
title: "其他",
id: 4
}
],
2: [{
title: "全部",
id: 0
},
{
title: "主图",
id: 1
},
{
title: "环境",
id: 2
@ -94,7 +118,7 @@
inactiveStyle: {
fontWeight: 500,
fontSize: "32rpx",
color: "#666666",
color: "#333333",
},
current: 1,
statusBarHeight: 0,
@ -108,6 +132,7 @@
};
},
async onLoad(options) {
// 获取状态栏高度
const systemInfo = await uni.getSystemInfoSync()
this.systemInfo = systemInfo
@ -120,6 +145,12 @@
this.getVideoList()
}
},
onShow() {
// // #ifdef APP-PLUS
// plus.screen.unlockOrientation(); //解除屏幕方向的锁定,但是不一定是竖屏;
// plus.screen.lockOrientation('portrait'); //锁死屏幕方向为竖屏
// // #endif
},
computed: {
// 计算swiper的样式
swiperStyle() {
@ -128,68 +159,82 @@
// 注意100vh 是视口高度,需要先转数值计算,再拼接单位
let viewportHeight = this.pxToRpx(this.systemInfo.windowHeight || window?.innerHeight || document
?.documentElement?.clientHeight|| 0, screenWidth ) ;
let height = viewportHeight - 88 - this.statusBarHeight;
?.documentElement?.clientHeight || 0, screenWidth);
let height = viewportHeight - 100 - this.statusBarHeight;
return {
height: `${height}rpx`
};
}
return {
height: `${this.viewportHeight}rpx`
height: `${this.viewportHeight - 100 - this.statusBarHeight}rpx`
};
},
swiperStyle2() {
if (this.current == 1) {
return {
marginTop: `${88 + this.statusBarHeight + 100}rpx`,
height: `${this.viewportHeight-88-this.statusBarHeight-100}rpx`
height: `${this.viewportHeight-100-this.statusBarHeight-100}rpx`
};
}
return {
height: `${this.viewportHeight}rpx`
height: `${this.viewportHeight - 100 - this.statusBarHeight}rpx`
};
},
swiperStyle3() {
if (this.current == 1) {
return {
width: `750rpx`,
height: `${200 + this.statusBarHeight}rpx`,
};
}
return {
width: `750rpx`,
height: `${100 + this.statusBarHeight}rpx`,
};
},
viewportHeight() {
let screenWidth = this.systemInfo.screenWidth;
// 注意100vh 是视口高度,需要先转数值计算,再拼接单位
let viewportHeight = this.pxToRpx(this.systemInfo.windowHeight || window?.innerHeight || document
?.documentElement?.clientHeight|| 0, screenWidth) ;
?.documentElement?.clientHeight || 0, screenWidth);
return viewportHeight
},
},
methods: {
goBack() {
uni.navigateBack(-1)
},
toimg(item) {
console.log(item, 'item');
uni.previewImage({
urls: [item.url], // 外面加 [] 变成数组
current: 0 // 可选,默认第一张
})
},
tovideo(item) {
uni.navigateTo({
url: '/pages/album/components/videoplayer?url=' + item.url
})
},
clickTab(item) {
this.cardListIndex = item.id
},
tabsClick(e) {
this.$nextTick(() => {
console.log('12312312312');
this.current = e.index
if(this.current==1){
this.activeStyle.color = "#333333"
this.inactiveStyle.color = "#666666"
this.$refs.ideoSlideRrf.nowStopVideo()
}
if(this.current==0){
this.activeStyle.color = "#fff"
this.inactiveStyle.color = "#ccc"
this.$refs.ideoSlideRrf.nowPlayVideo()
}
})
},
swiperChange(e) {
this.$nextTick(() => {
this.current = e.detail.current
if(this.current==1){
if (this.current == 1) {
this.activeStyle.color = "#333333"
this.inactiveStyle.color = "#666666"
this.$refs.ideoSlideRrf.nowStopVideo()
}
if(this.current==0){
this.activeStyle.color = "#fff"
this.inactiveStyle.color = "#ccc"
this.$refs.ideoSlideRrf.nowPlayVideo()
if (this.current == 0) {
this.activeStyle.color = "#333333"
this.inactiveStyle.color = "#666666"
}
})
@ -208,12 +253,17 @@
type: 1
}).then(res => {
if (res.code == 200) {
if (res.data.list.length != 0) {
this.imagObj = res.data.list[1]
let all = []
for (let key in res.data.list[1]) {
all = [...all, ...res.data.list[1][key]]
}
this.imagObj[0] = all
} else {
this.imagObj[0] = []
}
}
})
},
@ -224,8 +274,17 @@
type: 2
}).then(res => {
if (res.code == 200) {
if (res.data.list.length != 0) {
console.log(res.data, 'res.data');
for (let key in res.data.list[2]) {
this.videoList = [...this.videoList, ...res.data.list[2][key]]
this.videoList.forEach((item) => {
item.img = item.url +
'?x-oss-process=video/snapshot,t_1000,m_fast,f_jpg,w_600,ar_auto'
})
}
} else {
this.videoList = []
}
}
})
@ -266,10 +325,12 @@
}
}
}
.videoTop{
.videoTop {
background: transparent;
::v-deep .uv-tabs__wrapper__nav__line{
background: #fff!important;
::v-deep .uv-tabs__wrapper__nav__line {
background: #fff !important;
}
}
@ -296,7 +357,7 @@
.container-tab-text {
font-weight: 400;
font-size: 28rpx;
color: #E8101E;
color: #FF4767;
line-height: 40rpx;
text-align: left;
font-style: normal;
@ -306,6 +367,7 @@
.container-content {
z-index: 1000;
.swiper {
background: #fff;
@ -349,6 +411,47 @@
}
}
.swiper-items3 {
flex-wrap: wrap;
flex-direction: row;
display: flex;
padding-bottom: 20rpx;
.swiper-item3 {
width: 370rpx;
height: 370rpx;
margin-top: 10rpx;
overflow: hidden;
position: relative;
box-sizing: border-box;
border-radius: 20rpx;
.swiper-item3-video {
width: 370rpx;
height: 370rpx;
}
.swiper-item3-title {
position: absolute;
left: 0;
top: 0;
height: 33rpx;
padding: 0 8rpx;
background: rgba(0, 0, 0, 0.5);
border-radius: 0rpx 0rpx 10rpx 0rpx;
width: fit-content;
.swiper-item3-title-text {
font-weight: 400;
font-size: 18rpx;
color: #FFFFFF;
line-height: 33rpx;
}
}
}
}
.item2Left {
margin-right: 9rpx;
}
@ -398,6 +501,4 @@
align-items: flex-end;
justify-content: space-between;
}
</style>

View File

@ -86,7 +86,7 @@
? "门店下单购买后,您需到上门至门店进行服务,门店端展示价格为"
: ""
}}
<text style="color: #e8101e" v-if="showShopPrice">{{
<text style="color: #FF4767" v-if="showShopPrice">{{
shopPrice
}}</text>
{{ showShopPrice ? "元。" : "" }}
@ -364,19 +364,6 @@
>
</view>
<!-- 新增上门提醒弹窗-->
<transition name="fade">
<view class="popup-mask2" v-if="showReminderPopup" @click="closeReminderPopup(false)">
<view class="popup-content2" @click.stop>
<view class="popup-title">提示</view>
<view class="popup-message">此服务是需要上门服务的哦</view>
<view class="popup-buttons">
<view class="popup-btn popup-btn-notshow" @click="closeReminderPopup(true)">不再提示</view>
<view class="popup-btn popup-btn-know" @click="closeReminderPopup(false)">我已知晓</view>
</view>
</view>
</view>
</transition>
</view>
</template>
@ -402,7 +389,6 @@ export default {
},
data() {
return {
showReminderPopup: false,
artisanType: getApp().globalData.artisanType,
nowQer: "xc",
progress: 0,
@ -474,11 +460,6 @@ export default {
this.serviceTypes = this.serviceTypes.concat(res.data);
});
}
//
const notShow = uni.getStorageSync('addServiceReminderNotShow');
if (!notShow) {
this.showReminderPopup = true;
}
this.getUserInfo();
},
computed: {
@ -575,14 +556,6 @@ export default {
return true;
},
closeReminderPopup(notShowAgain) {
console.log('关闭弹窗,不再提示:', notShowAgain);
if (notShowAgain) {
uni.setStorageSync('addServiceReminderNotShow', true);
}
this.showReminderPopup = false;
},
goImgAndText() {
uni.navigateTo({
url: `/pages/shop/add-img-text?list=${this.formData.graphic_details}`,
@ -1234,13 +1207,13 @@ export default {
.btn-draft {
background-color: #ffffff;
color: #e8101e;
border: 2rpx solid #e8101e;
color: #FF4767;
border: 2rpx solid #FF4767;
box-sizing: border-box;
}
.btn-submit {
background-color: #e8101e;
background-color: #FF4767;
color: #ffffff;
}
@ -1512,13 +1485,13 @@ export default {
}
.popup-btn-know {
background-color: #e8101e;
background-color: #FF4767;
color: #ffffff;
}
.popup-btn-notshow {
background-color: #ffffff;
color: #e8101e;
border: 1rpx #e8101e solid;
color: #FF4767;
border: 1rpx #FF4767 solid;
}
</style>

View File

@ -1204,7 +1204,7 @@ export default {
}
.change-info-btn, .view-change-btn, .resubmit-btn {
background: linear-gradient(180deg, #f52540 0%, #e8101e 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
}
/* 双按钮行样式 */
@ -1225,7 +1225,7 @@ export default {
}
.submit-btn {
background: linear-gradient(180deg, #F52540 0%, #E8101E 100%, #E8101E 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
border-radius: 43rpx;
padding: 0 15rpx;
}
@ -1257,7 +1257,7 @@ export default {
.title-bar {
width: 6rpx;
height: 30rpx;
background-color: #E8101E;
background-color: #FF4767;
margin-right: 7rpx;
border-radius: 5rpx;
}
@ -1405,7 +1405,7 @@ export default {
width: 60rpx;
height: 60rpx;
border: 4rpx solid #fff;
border-top: 4rpx solid #E8101E;
border-top: 4rpx solid #FF4767;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 16rpx;
@ -1470,8 +1470,8 @@ export default {
}
.radio.checked {
background-color: #E8101E;
border-color: #E8101E;
background-color: #FF4767;
border-color: #FF4767;
}
.radio-icon-image {
@ -1640,7 +1640,7 @@ export default {
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(180deg, #f52540 0%, #e8101e 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
}
.popup-btn .btn-text {
@ -1659,7 +1659,7 @@ export default {
}
.confirm-text {
color: #E8101E;
color: #FF4767;
font-weight: 500;
}
@ -1771,13 +1771,13 @@ export default {
}
.tip-btn.cancel {
border: #E8101E 2rpx solid;
border: #FF4767 2rpx solid;
background-color: transparent;
color: #e8101e;
color: #FF4767;
}
.tip-btn.confirm {
background: linear-gradient(180deg, #f52540 0%, #e8101e 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
color: #FFFFFF;
}

View File

@ -165,7 +165,7 @@ export default {
timeRange: '7days',
count: 0,
chartOpts: {
color: ["#E8101E"],
color: ["#FF4767"],
padding: [15, 15, 0, 15],
enableScroll: false,
legend: false,
@ -657,7 +657,7 @@ page {
}
.time-filter .active {
color: #E8101E;
color: #FF4767;
}
.chart-container {
@ -676,7 +676,7 @@ page {
width: 214rpx;
height: 56rpx;
background-color: #FFFFFF;
border: 1rpx solid #E8101E;
border: 1rpx solid #FF4767;
border-radius: 8rpx;
display: flex;
justify-content: center;
@ -687,7 +687,7 @@ page {
.type-name {
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
}
.type-count {
@ -708,7 +708,7 @@ page {
background-color: #F53F3F;
}
.order-type-item.active {
background-color: #E8101E;
background-color: #FF4767;
}
.order-type-item.active .type-name {
color: #FFFFFF;
@ -800,7 +800,7 @@ page {
}
.action-btn.verify .action-text {
font-size: 28rpx;
color: #E8101E;
color: #FF4767;
}
.nothings-box {

View File

@ -249,7 +249,7 @@ page {
flex-flow: row nowrap;
justify-content: center;
align-items: center;
border: 1rpx solid #E8101E;
border: 1rpx solid #FF4767;
background-color: #FFF1F1;
border-radius: 22rpx;
box-sizing: border-box;
@ -267,7 +267,7 @@ page {
}
.verified-text {
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
}
.verified-text.no {
color: #666666;

View File

@ -4,7 +4,9 @@
<view class="custom-navbar">
<!-- 左侧返回按钮和标题 -->
<view class="navbar-left">
<image class="back-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/456f4e6c-e86a-42c9-a272-484db9af6c7c" mode="aspectFit" @click="handleBack"></image>
<image class="back-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/456f4e6c-e86a-42c9-a272-484db9af6c7c"
mode="aspectFit" @click="handleBack"></image>
<text class="navbar-title">我的资料</text>
</view>
@ -36,24 +38,55 @@
<!-- 营业时间商家 / 服务时间手艺人 -->
<view class="info-item" @click="navigateToTimeSetting">
<view class="info-left">
<image class="item-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/21f1efaf-0c17-49b0-a2b2-e0fd573b92b3" mode="aspectFit"></image>
<image class="item-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/21f1efaf-0c17-49b0-a2b2-e0fd573b92b3"
mode="aspectFit"></image>
<text class="item-label">{{ identity == '2' ? '营业时间' : '服务时间' }}</text>
</view>
<image class="arrow-right" src="/static/images/arrow_right.png" mode="aspectFit"></image>
<view>
<image
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/338d957b-cbb6-409b-9e7a-ef5faa0f9b70.png"
mode="widthFix"
style="width:26rpx;margin-top: 26rpx;position: relative;top: 4rpx;left: -10rpx;"
v-if="!userInfo.business_time">
</image>
<text style="margin-right: 16rpx;"
:style="{ color: userInfo.business_time ? '#c9c9c9' : '#ec5d57' }">
{{ userInfo.business_time ? '已设置' : '待完善' }}
</text>
<image class="arrow-right" src="/static/images/arrow_right.png" mode="aspectFit">
</image>
</view>
</view>
<!-- 服务技能 -->
<view class="info-item" @click="navigateToServiceSkill">
<view class="info-left">
<image class="item-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/b90e2322-a261-4c17-a581-85010c3bc112" mode="aspectFit"></image>
<image class="item-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/b90e2322-a261-4c17-a581-85010c3bc112"
mode="aspectFit"></image>
<text class="item-label">服务技能</text>
</view>
<image class="arrow-right" src="/static/images/arrow_right.png" mode="aspectFit"></image>
<view>
<image
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/338d957b-cbb6-409b-9e7a-ef5faa0f9b70.png"
mode="widthFix"
style="width:26rpx;margin-top: 26rpx;position: relative;top: 4rpx;left: -10rpx;"
v-if="shouldShowServiceSkillWarningIcon()">
</image>
<text style="margin-right: 16rpx;"
:style="{ color: serviceSkillStatusColor() }">
{{ serviceSkillStatusText() }}
</text>
<image class="arrow-right" src="/static/images/arrow_right.png" mode="aspectFit">
</image>
</view>
</view>
<!-- 门店信息 -->
<view class="info-item" v-if="identity == '2'" @click="navigateToShopInfo">
<view class="info-left">
<image class="item-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e9618006-db69-446e-92e1-ba9dd81e79f9" mode="aspectFit"></image>
<image class="item-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e9618006-db69-446e-92e1-ba9dd81e79f9"
mode="aspectFit"></image>
<text class="item-label">门店信息</text>
</view>
<image class="arrow-right" src="/static/images/arrow_right.png" mode="aspectFit"></image>
@ -62,7 +95,9 @@
<!-- 服务区域手艺人显示 -->
<view class="info-item" v-if="identity == '1'" @click="navigateToServiceArea">
<view class="info-left">
<image class="item-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/a35603b1-50fb-4248-9f2e-2dd4d5e249c0" mode="aspectFit"></image>
<image class="item-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/a35603b1-50fb-4248-9f2e-2dd4d5e249c0"
mode="aspectFit"></image>
<text class="item-label">服务区域</text>
</view>
<image class="arrow-right" src="/static/images/arrow_right.png" mode="aspectFit"></image>
@ -71,7 +106,9 @@
<!-- 个人信息手艺人显示 -->
<view class="info-item" v-if="identity == '1'" @click="navigateToPersonalInfo">
<view class="info-left">
<image class="item-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/77dc7a16-3b86-44e5-8acc-5f3b85ffc137" mode="aspectFit"></image>
<image class="item-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/77dc7a16-3b86-44e5-8acc-5f3b85ffc137"
mode="aspectFit"></image>
<text class="item-label">个人信息</text>
</view>
<image class="arrow-right" src="/static/images/arrow_right.png" mode="aspectFit"></image>
@ -80,7 +117,9 @@
<!-- 个人信息手艺人显示 -->
<view class="info-item" @click="navigateToimg">
<view class="info-left">
<image class="item-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/53a9d1e1-e2d2-4001-b599-82e0c0747f1a.png" mode="aspectFit"></image>
<image class="item-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/53a9d1e1-e2d2-4001-b599-82e0c0747f1a.png"
mode="aspectFit"></image>
<text class="item-label">相册管理</text>
</view>
<image class="arrow-right" src="/static/images/arrow_right.png" mode="aspectFit"></image>
@ -89,7 +128,9 @@
<!-- 个人信息手艺人显示 -->
<view class="info-item" @click="navigateTovideo">
<view class="info-left">
<image class="item-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/6fc821cd-8dd1-41f1-8854-889fb5f76b78.png" mode="aspectFit"></image>
<image class="item-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/6fc821cd-8dd1-41f1-8854-889fb5f76b78.png"
mode="aspectFit"></image>
<text class="item-label">视频管理</text>
</view>
<image class="arrow-right" src="/static/images/arrow_right.png" mode="aspectFit"></image>
@ -110,16 +151,33 @@
<!-- 资质信息 -->
<view class="info-item" @click="navigateToQualification">
<view class="info-left">
<image class="item-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/2677692c-5931-4ac7-846e-b6d321184789" mode="aspectFit"></image>
<image class="item-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/2677692c-5931-4ac7-846e-b6d321184789"
mode="aspectFit"></image>
<text class="item-label">资质信息</text>
</view>
<image class="arrow-right" src="/static/images/arrow_right.png" mode="aspectFit"></image>
<view>
<image
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/338d957b-cbb6-409b-9e7a-ef5faa0f9b70.png"
mode="widthFix"
style="width:26rpx;margin-top: 26rpx;position: relative;top: 4rpx;left: -10rpx;"
v-if="shouldShowQualificationWarningIcon()">
</image>
<text style="margin-right: 16rpx;"
:style="{ color: qualificationStatusColor() }">
{{ qualificationStatusText() }}
</text>
<image class="arrow-right" src="/static/images/arrow_right.png" mode="aspectFit">
</image>
</view>
</view>
<!-- 所属门店手艺人显示 -->
<view class="info-item" v-if="identity == '1'" @click="navigateToBelongShop">
<view class="info-left">
<image class="item-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/b5541cb5-58c1-4e72-b88f-5cf6450338a8" mode="aspectFit"></image>
<image class="item-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/b5541cb5-58c1-4e72-b88f-5cf6450338a8"
mode="aspectFit"></image>
<text class="item-label">所属门店</text>
</view>
<image class="arrow-right" src="/static/images/arrow_right.png" mode="aspectFit"></image>
@ -131,7 +189,9 @@
</template>
<script>
import { handleImgSizeUni } from '@/utils/service.js'
import {
handleImgSizeUni
} from '@/utils/service.js'
import request from "../../utils/request";
export default {
@ -140,6 +200,11 @@ export default {
identity: '',
currentUserId: '', // ID
userInfo: {}, //
authDetails: null,
serviceSkillAuditStatus: null,
isServiceSkillAuditLoaded: false,
qualificationAuditStatus: null,
isQualificationAuditLoaded: false,
sharing: false //
}
},
@ -252,9 +317,9 @@ export default {
//
handleMpAutoShare() {
//
const url = this.identity == '2'
? `/pages/user/store-detail?id=${this.currentUserId}`
: `/pages/user/syr-detail?id=${this.currentUserId}`;
const url = this.identity == '2' ?
`/pages/user/store-detail?id=${this.currentUserId}` :
`/pages/user/syr-detail?id=${this.currentUserId}`;
uni.navigateTo({
url: url,
@ -286,9 +351,9 @@ export default {
});
//
const url = this.identity == '2'
? `/pages/user/store-detail?id=${this.currentUserId}&autoShare=1`
: `/pages/user/syr-detail?id=${this.currentUserId}&autoShare=1`;
const url = this.identity == '2' ?
`/pages/user/store-detail?id=${this.currentUserId}&autoShare=1` :
`/pages/user/syr-detail?id=${this.currentUserId}&autoShare=1`;
uni.navigateTo({
url: url,
@ -312,12 +377,13 @@ export default {
// APP
executeAppShare(imageUrl) {
//
const backupImageUrl = "https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/yh/1756713709528-717.png?x-oss-process=image/resize,p_40"
const backupImageUrl =
"https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/yh/1756713709528-717.png?x-oss-process=image/resize,p_40"
//
const shareTitle = this.identity == '2'
? "好友刚推了个超赞商家点开认识TA ~"
: "好友刚推了个超赞手艺人点开认识TA ~"
const shareTitle = this.identity == '2' ?
"好友刚推了个超赞商家点开认识TA ~" :
"好友刚推了个超赞手艺人点开认识TA ~"
//
const shareConfig = {
@ -425,11 +491,20 @@ export default {
console.log('调用接口获取用户信息type:', type)
//
const result = await request.post('/user/getuser', { type })
const result = await request.post('/sj/user/getUser', {
type
})
if (result.data && result.data.id) {
this.userInfo = result.data
this.currentUserId = result.data.id
this.fetchServiceSkillApplyDetails()
this.fetchQualificationApplyDetails()
if (this.isSettled()) {
this.fetchAuthDetails()
} else {
this.authDetails = null
}
//
uni.setStorageSync('userInfo', result.data)
@ -442,6 +517,7 @@ export default {
// ID
this.tryAlternativeUserInfo()
uni.hideLoading();
}
},
@ -481,6 +557,98 @@ export default {
}
},
fetchAuthDetails() {
return request.post("/sj/userSjAuth/details", {
type: 1,
id_type: '1'
}).then(res => {
if (res.code == 200 && res.data) {
this.authDetails = res.data;
}
return res;
});
},
fetchServiceSkillApplyDetails() {
this.isServiceSkillAuditLoaded = false;
return request.post('/sj/userSjAuth/details', {
apply_type: 2
}).then(res => {
if ((res.code == 200 || res.state == 1) && res.data) {
const status = Number(res.data.apply_state);
this.serviceSkillAuditStatus = Number.isNaN(status) ? null : status;
} else {
this.serviceSkillAuditStatus = null;
}
this.isServiceSkillAuditLoaded = true;
return res;
}).catch(error => {
console.log('获取服务技能申请详情失败:', error);
this.serviceSkillAuditStatus = null;
this.isServiceSkillAuditLoaded = true;
});
},
fetchQualificationApplyDetails() {
this.isQualificationAuditLoaded = false;
return request.post('/sj/userSjAuth/details', {
apply_type: 4
}).then(res => {
if ((res.code == 200 || res.state == 1) && res.data) {
const statusCheck = this.getQualificationApplyStatus(res.data);
this.qualificationAuditStatus = statusCheck.isValid ? statusCheck.status : null;
} else {
this.qualificationAuditStatus = null;
}
this.isQualificationAuditLoaded = true;
return res;
}).catch(error => {
console.log('获取资质信息申请详情失败:', error);
this.qualificationAuditStatus = null;
this.isQualificationAuditLoaded = true;
});
},
isSettled() {
return typeof this.userInfo.id_type !== 'undefined' && this.userInfo.id_type !== 0;
},
isCertified() {
return typeof this.userInfo.id_type !== 'undefined' && this.userInfo.id_type > 0;
},
isUnderReview() {
return this.authDetails && Object.keys(this.authDetails).length > 0;
},
async ensureAuthDetails() {
if (this.identity != '2' || !this.isSettled() || this.isUnderReview()) {
return;
}
await this.fetchAuthDetails();
},
getQualificationApplyStatus(detail) {
const status = Number(detail.apply_state);
if (Number.isNaN(status)) {
return {
isValid: false,
status: null
};
}
if (status === 2) {
return {
isValid: true,
status
};
}
return {
isValid: true,
status
};
},
loadIdentity() {
//
const app = getApp()
@ -564,24 +732,77 @@ export default {
})
},
navigateToQualification() { //
if (this.identity == '2') {
//
async navigateToQualification() {
if (this.identity != '2' || this.isCertified()) {
uni.navigateTo({
url: '/pages/shop/qualification_sj_change'
})
} else {
//
});
return;
}
await this.ensureAuthDetails();
if (!this.isSettled()) {
uni.navigateTo({
url: '/pages/artisan/qualification_syr_change'
})
url: '/pages/ruzhu/ruzhu'
});
} else if (this.isUnderReview()) {
uni.navigateTo({
url: '/pages/ruzhu/ruzhu?type=1'
});
} else {
uni.navigateTo({
url: '/pages/ruzhu/ruzhu?step=3'
});
}
},
navigateToBelongShop() { //
uni.navigateTo({
url: '/pages/artisan/belong_shop'
})
},
hasServiceSkills() {
return this.userInfo.servers_kill_arr && this.userInfo.servers_kill_arr.length > 0;
},
serviceSkillStatusText() {
if (this.serviceSkillAuditStatus === 1) return '审核中';
if (this.serviceSkillAuditStatus === 3) return '审核未通过';
return this.hasServiceSkills() ? '已设置' : '待完善';
},
serviceSkillStatusColor() {
const statusText = this.serviceSkillStatusText();
if (statusText === '审核未通过' || statusText === '待完善') return '#ec5d57';
if (statusText === '审核中') return '#e59e45';
return '#c9c9c9';
},
shouldShowServiceSkillWarningIcon() {
const statusText = this.serviceSkillStatusText();
return statusText === '审核未通过' || statusText === '待完善';
},
getCredentialsState() {
const state = Number(this.userInfo.credentials_state);
return Number.isNaN(state) ? null : state;
},
qualificationStatusText() {
if (!this.isQualificationAuditLoaded) return '';
if (this.qualificationAuditStatus === 1) return '审核中';
if (this.qualificationAuditStatus === 2) return '已认证';
if (this.qualificationAuditStatus === 3) return '审核未通过';
const credentialsState = this.getCredentialsState();
if (credentialsState === 0) return '待完善';
if (credentialsState === 1) return '审核中';
if (this.userInfo.id_type < 0) return '审核未通过';
return '已认证';
},
qualificationStatusColor() {
const statusText = this.qualificationStatusText();
if (statusText === '审核未通过' || statusText === '待完善') return '#ec5d57';
if (statusText === '审核中') return '#e59e45';
return '#389930';
},
shouldShowQualificationWarningIcon() {
const statusText = this.qualificationStatusText();
return statusText === '审核未通过' || statusText === '待完善';
}
}
}
@ -599,7 +820,8 @@ export default {
top: 0;
left: 0;
right: 0;
height: 108rpx; /* 使用标准导航栏高度 */
height: 108rpx;
/* 使用标准导航栏高度 */
background: white;
display: flex;
align-items: center;
@ -697,7 +919,7 @@ export default {
.title-bar {
width: 6rpx;
height: 30rpx;
background-color: #E8101E;
background-color: #FF4767;
margin-right: 10rpx;
border-radius: 5rpx;
}
@ -731,7 +953,7 @@ export default {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx 0 ;
padding: 32rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}

View File

@ -1544,7 +1544,7 @@ export default {
.tag {
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
margin-left: 14rpx;
}
.textarea {
@ -1655,7 +1655,7 @@ export default {
.save-btn {
width: 702rpx;
height: 64rpx;
background: #E8101E;
background: #FF4767;
border-radius: 32rpx;
display: flex;
align-items: center;
@ -1762,8 +1762,8 @@ export default {
}
.checkbox.checked {
background-color: #E8101E;
border-color: #E8101E;
background-color: #FF4767;
border-color: #FF4767;
}
.check-icon {
@ -1796,7 +1796,7 @@ export default {
}
.popup-btn.confirm {
background: linear-gradient(90deg, #E8101E 0%, #FF618F 100%);
background: linear-gradient(90deg, #FF4767 0%, #FF618F 100%);
}
.popup-btn .btn-text {
@ -1834,7 +1834,7 @@ export default {
}
.region-tab.active {
color: #E8101E;
color: #FF4767;
font-weight: 500;
}
@ -1846,7 +1846,7 @@ export default {
transform: translateX(-50%);
width: 40rpx;
height: 4rpx;
background-color: #E8101E;
background-color: #FF4767;
border-radius: 2rpx;
}
@ -1907,7 +1907,7 @@ export default {
.confirm-text {
font-size: 32rpx;
color: #E8101E;
color: #FF4767;
font-weight: 500;
}
@ -1949,7 +1949,7 @@ export default {
}
.time-block.active .time-value {
color: #E8101E;
color: #FF4767;
}
.time-separator {

View File

@ -43,7 +43,7 @@
<view class="radio-item" @click="editable && setHealthCardValidType('permanent')">
<view class="radio" :class="{ checked: displayData.health_card_expiry_type === 1 }">
<image v-if="displayData.health_card_expiry_type === 1"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/cb185ec8-9d30-4e53-b5dc-14b546013a60"
src="/static/images/agree_y.png"
class="radio-icon-image"
mode="aspectFit"></image>
</view>
@ -52,7 +52,7 @@
<view class="radio-item" @click="editable && setHealthCardValidType('date')">
<view class="radio" :class="{ checked: displayData.health_card_expiry_type === 2 }">
<image v-if="displayData.health_card_expiry_type === 2"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/cb185ec8-9d30-4e53-b5dc-14b546013a60"
src="/static/images/agree_y.png"
class="radio-icon-image"
mode="aspectFit"></image>
</view>
@ -1292,7 +1292,7 @@ export default {
}
.change-info-btn, .view-change-btn, .resubmit-btn {
background: linear-gradient(180deg, #f52540 0%, #e8101e 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
}
/* 双按钮行样式 */
@ -1313,7 +1313,7 @@ export default {
}
.submit-btn {
background: linear-gradient(180deg, #F52540 0%, #E8101E 100%, #E8101E 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
border-radius: 43rpx;
padding: 0 15rpx;
}
@ -1345,7 +1345,7 @@ export default {
.title-bar {
width: 6rpx;
height: 30rpx;
background-color: #E8101E;
background-color: #FF4767;
margin-right: 7rpx;
border-radius: 5rpx;
}
@ -1472,8 +1472,8 @@ export default {
}
.radio.checked {
background-color: #E8101E;
border-color: #E8101E;
background-color: #FF4767;
border-color: #FF4767;
}
.radio-icon-image {

View File

@ -104,7 +104,7 @@
@click="toggleDistrictSelect(index)"
>
<view class="checkbox" :class="{ checked: district.checked }">
<image v-if="district.checked" class="check-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/66325904-4603-48fd-b03b-90c684af96e9" mode="aspectFit"></image>
<image v-if="district.checked" class="check-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/6948baf3-aad9-4d87-9483-9db0e462a3f7.png" mode="aspectFit"></image>
</view>
<text class="checkbox-label">{{district.area}}</text>
</view>
@ -752,7 +752,7 @@ export default {
.title-bar {
width: 6rpx;
height: 30rpx;
background-color: #E8101E;
background-color: #FF4767;
margin-right: 10rpx;
border-radius: 5rpx;
}
@ -854,7 +854,7 @@ export default {
.limit-text {
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
}
/* 区域选择器样式 */
@ -942,7 +942,7 @@ export default {
}
.region-tab.active {
color: #E8101E;
color: #FF4767;
font-weight: 500;
}
@ -954,7 +954,7 @@ export default {
transform: translateX(-50%);
width: 40rpx;
height: 4rpx;
background-color: #E8101E;
background-color: #FF4767;
border-radius: 2rpx;
}
@ -994,8 +994,8 @@ export default {
}
.checkbox.checked {
background-color: #E8101E;
border-color: #E8101E;
background-color: #FF4767;
border-color: #FF4767;
}
.check-icon {
@ -1035,7 +1035,7 @@ export default {
}
.popup-btn.confirm {
background: linear-gradient(180deg, #f52540 0%, #e8101e 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
}
.popup-btn .btn-text {
@ -1075,11 +1075,11 @@ export default {
}
.change-info-btn {
background: linear-gradient(180deg, #f52540 0%, #e8101e 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
}
.view-change-btn {
background: linear-gradient(180deg, #f52540 0%, #e8101e 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
}
/* 双按钮行样式 */
@ -1100,7 +1100,7 @@ export default {
}
.submit-btn {
background: linear-gradient(180deg, #F52540 0%, #E8101E 100%, #E8101E 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
border-radius: 43rpx;
padding: 0 15rpx;
}

View File

@ -25,7 +25,7 @@
<view class="checkbox" :class="{ checked: isCurrentSkillSelected(skill.id) }">
<image v-if="isCurrentSkillSelected(skill.id)"
class="check-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/66325904-4603-48fd-b03b-90c684af96e9"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/6948baf3-aad9-4d87-9483-9db0e462a3f7.png"
mode="aspectFit">
</image>
</view>
@ -45,7 +45,7 @@
<view class="checkbox" :class="{ checked: skill.checked }">
<image v-if="skill.checked"
class="check-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/66325904-4603-48fd-b03b-90c684af96e9"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/6948baf3-aad9-4d87-9483-9db0e462a3f7.png"
mode="aspectFit">
</image>
</view>
@ -721,7 +721,7 @@ export default {
.title-bar {
width: 6rpx;
height: 30rpx;
background-color: #E8101E;
background-color: #FF4767;
margin-right: 10rpx;
border-radius: 5rpx;
}
@ -790,8 +790,8 @@ export default {
}
.checkbox.checked {
background-color: #E8101E;
border-color: #E8101E;
background-color: #FF4767;
border-color: #FF4767;
}
.check-icon {
@ -827,12 +827,12 @@ export default {
.change-info-btn {
margin-top: 20rpx;
background: linear-gradient(180deg, #f52540 0%, #e8101e 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
}
.view-change-btn {
margin-top: 20rpx;
background: linear-gradient(180deg, #f52540 0%, #e8101e 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
}
/* 双按钮行样式 */
@ -853,7 +853,7 @@ export default {
}
.submit-btn {
background: linear-gradient(180deg, #F52540 0%, #E8101E 100%, #E8101E 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
border-radius: 43rpx;
padding: 0 15rpx;
}

View File

@ -371,7 +371,7 @@ export default {
.title-bar {
width: 6rpx;
height: 30rpx;
background-color: #E8101E;
background-color: #FF4767;
margin-right: 10rpx;
border-radius: 5rpx;
}
@ -410,7 +410,7 @@ export default {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 28rpx;
color: #E8101E;
color: #FF4767;
line-height: 40rpx;
text-align: left;
font-style: normal;
@ -476,7 +476,7 @@ export default {
.confirm-text {
font-size: 32rpx;
color: #E8101E;
color: #FF4767;
font-weight: 500;
}
@ -526,7 +526,7 @@ export default {
}
.time-block.active .time-value-picker {
color: #E8101E;
color: #FF4767;
}
.time-separator {
@ -572,7 +572,7 @@ export default {
border: none;
width: 290rpx;
height: 86rpx;
background: linear-gradient( 180deg, #F52540 0%, #E8101E 100%, #E8101E 100%);
background: linear-gradient( 180deg, #F52540 0%, #FF4767 100%, #FF4767 100%);
border-radius: 43rpx;
}
@ -592,7 +592,7 @@ export default {
}
.btn-confirm-large {
background: #E8101E;
background: #FF4767;
color: white;
}
</style>

View File

@ -100,13 +100,13 @@
<view class="radio-group">
<view class="radio-item" @click="editable && setIdCardValidType(1)">
<view class="radio" :class="{ checked: displayData.idcard_expiry_type === 1 }">
<image v-if="displayData.idcard_expiry_type === 1" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/cb185ec8-9d30-4e53-b5dc-14b546013a60" class="radio-icon-image" mode="aspectFit"></image>
<image v-if="displayData.idcard_expiry_type === 1" src="/static/images/agree_y.png" class="radio-icon-image" mode="aspectFit"></image>
</view>
<text class="radio-label">长久有效</text>
</view>
<view class="radio-item" @click="editable && setIdCardValidType(2)">
<view class="radio" :class="{ checked: displayData.idcard_expiry_type === 2 }">
<image v-if="displayData.idcard_expiry_type === 2" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/cb185ec8-9d30-4e53-b5dc-14b546013a60" class="radio-icon-image" mode="aspectFit"></image>
<image v-if="displayData.idcard_expiry_type === 2" src="/static/images/agree_y.png" class="radio-icon-image" mode="aspectFit"></image>
</view>
<text class="radio-label">指定日期有效</text>
</view>
@ -1813,7 +1813,7 @@ export default {
}
.change-info-btn, .view-change-btn, .resubmit-btn {
background: linear-gradient(180deg, #f52540 0%, #e8101e 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
}
/* 双按钮行样式 */
@ -1834,7 +1834,7 @@ export default {
}
.submit-btn {
background: linear-gradient(180deg, #F52540 0%, #E8101E 100%, #E8101E 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
border-radius: 43rpx;
padding: 0 15rpx;
}
@ -1866,7 +1866,7 @@ export default {
.title-bar {
width: 6rpx;
height: 30rpx;
background-color: #E8101E;
background-color: #FF4767;
margin-right: 7rpx;
border-radius: 5rpx;
}
@ -2029,7 +2029,7 @@ export default {
width: 60rpx;
height: 60rpx;
border: 4rpx solid #fff;
border-top: 4rpx solid #E8101E;
border-top: 4rpx solid #FF4767;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 16rpx;
@ -2115,8 +2115,8 @@ export default {
}
.radio.checked {
background-color: #E8101E;
border-color: #E8101E;
background-color: #FF4767;
border-color: #FF4767;
}
.radio-icon-image {
@ -2291,7 +2291,7 @@ export default {
.confirm-text {
font-size: 32rpx;
color: #E8101E;
color: #FF4767;
font-weight: 500;
}
@ -2321,7 +2321,7 @@ export default {
}
.popup-btn.confirm {
background: linear-gradient(180deg, #f52540 0%, #e8101e 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
}
.popup-btn .btn-text {
@ -2431,13 +2431,13 @@ export default {
}
.tip-btn.cancel {
border: #E8101E 2rpx solid;
border: #FF4767 2rpx solid;
background-color: transparent;
color: #e8101e;
color: #FF4767;
}
.tip-btn.confirm {
background: linear-gradient(180deg, #f52540 0%, #e8101e 100%);
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
color: #FFFFFF;
}

View File

@ -326,7 +326,7 @@ export default {
.add-icon {
font-size: 32rpx;
color: #E8101E;
color: #FF4767;
margin-right: 8rpx;
}
@ -367,7 +367,7 @@ export default {
}
.confirm-btn {
color: #E8101E;
color: #FF4767;
}
.title {

View File

@ -934,7 +934,7 @@
.get-code-btn {
font-weight: 400;
font-size: 30rpx;
color: #E8101E;
color: #FF4767;
line-height: 42rpx;
text-align: center;
font-style: normal;
@ -959,7 +959,7 @@
.input-error {
display: block;
font-size: 24rpx;
color: #e8101e;
color: #FF4767;
margin-left: 30rpx;
margin-top: -20rpx;
margin-bottom: 20rpx;

View File

@ -132,7 +132,7 @@ import request from '../../utils/request';
.state {
height: 60rpx;
line-height: 60rpx;
background-color: #E8101E;
background-color: #FF4767;
padding: 0 24rpx;
}
.state-text {

View File

@ -132,7 +132,7 @@ export default {
color: #91908F;
}
.btn-box {
background: linear-gradient( 180deg, #F52540 0%, #E8101E 100%);
background: linear-gradient( 180deg, #F52540 0%, #FF4767 100%);
height: 88rpx;
border-radius: 43rpx;
font-weight: 500;

View File

@ -226,7 +226,7 @@
.score {
font-weight: 400;
font-size: 26rpx;
color: #E8101E;
color: #FF4767;
line-height: 37rpx;
}

View File

@ -251,7 +251,7 @@
.score {
font-weight: 400;
font-size: 26rpx;
color: #E8101E;
color: #FF4767;
line-height: 37rpx;
text-align: left;
font-style: normal;

View File

@ -279,7 +279,7 @@
.score {
font-weight: 400;
font-size: 26rpx;
color: #E8101E;
color: #FF4767;
line-height: 37rpx;
text-align: left;
font-style: normal;

View File

@ -177,7 +177,7 @@
}
.active {
color: #E8101E;
color: #FF4767;
}
}

View File

@ -0,0 +1,239 @@
<template>
<view class="brand-card-comp" @click="onClick">
<image class="brand-bg" :src="item.bgSrc" mode="aspectFill"></image>
<image class="mask-bg"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/9bca353c-fa74-407e-a69e-6f5e9aa43955.png"
mode="widthFix"></image>
<view class="card-content">
<view class="avatar-wrapper" style="border-color: #F7E5D7;">
<image class="avatar-img" :src="item.avatar || 'https://dummyimage.com/100x100/ccc/fff.png&text=logo'">
</image>
</view>
<text class="brand-name">{{ item.name }}</text>
<view class="tags-container">
<view class="tag-item" v-for="(tag, idx) in displayTags" :key="idx" :style="getTagStyle(tag)">
<!-- <image class="tag-icon" v-if="tag.icon" :src="tag.icon"></image> -->
<text class="tag-text">{{ tag.text }} </text>
<text class="tag-text1"></text>
</view>
</view>
</view>
</view>
</template>
<script>
//
const TAG_LIBRARY = {
franchise: { //
text: '品牌加盟',
color: '#8D4C1B ',
borderColor: '#D6CAC0 ',
icon: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/60816ae2-92ee-4473-9eca-562e1200dbca.png'
},
recruit: { //
text: '学员招募',
color: '#E5505F',
borderColor: '#EFCFD2 ',
icon: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/fd0699b9-9f3c-4b1e-8693-d0c9ec7eabc3.png'
},
more: { //
text: '更多详情',
color: '#333333',
borderColor: '#ECECEC',
icon: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/cec453ce-f3fc-4881-8f2c-9ec5c61e759c.png'
}
}
export default {
name: 'brand-card',
props: {
//
item: {
type: Object,
default: () => ({})
},
// key ['franchise', 'recruit']
tagKeys: {
type: Array,
default: null
}
},
computed: {
// 使 tagKeys item.tags
displayTags() {
if (this.tagKeys && this.tagKeys.length) {
return this.tagKeys.map(key => TAG_LIBRARY[key]).filter(v => v)
}
// item.tags 使
if (this.item.tags && Array.isArray(this.item.tags)) {
return this.item.tags
}
return []
},
tagsLength() {
return this.displayTags.length
}
},
methods: {
//
onClick() {
// 1. emit便
this.$emit('click', this.item);
// 2.
if (!uni.getStorageSync("accessToken")) {
uni.navigateTo({
url: '/pages/blogPopup/blogPopup'
});
return;
}
// 3.
if (this.item && this.item.id) {
uni.navigateTo({
url: `/pages/jingxuan/brand-detail?id=${this.item.id}`
});
}
},
getTagStyle(tag) {
const len = this.tagsLength
let sizeStyle = {}
if (len === 1) {
sizeStyle = {
width: '288rpx',
height: '42rpx',
padding: '0 12rpx',
boxSizing: 'border-box'
}
} else if (len === 2) {
sizeStyle = {
width: '142rpx',
height: '42rpx',
padding: '0 12rpx',
boxSizing: 'border-box'
}
}
return {
...sizeStyle,
color: tag.color,
borderColor: tag.borderColor
}
}
}
}
</script>
<style lang="scss">
.brand-card-comp {
width: 328rpx;
height: 339rpx;
position: relative;
border-radius: 21rpx;
overflow: hidden;
background-color: #fff;
}
.brand-bg {
width: 100%;
height: 184rpx;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
.mask-bg {
width: 100%;
height: 251rpx;
position: absolute;
bottom: 0;
left: 0;
z-index: 2;
display: block;
}
.card-content {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
z-index: 3;
display: flex;
flex-direction: column;
align-items: center;
padding-bottom: 24rpx;
}
.avatar-wrapper {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
border: 2rpx solid;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
margin-top: -38rpx;
margin-bottom: 12rpx;
}
.avatar-img {
width: 100%;
height: 100%;
}
.brand-name {
font-size: 28rpx;
font-weight: 500;
color: #333333;
line-height: 34rpx;
margin-bottom: 12rpx;
}
.tags-container {
display: flex;
justify-content: center;
align-items: center;
gap: 20rpx;
flex-wrap: wrap;
}
.tag-item {
display: flex;
align-items: center;
justify-content: center;
border-radius: 21rpx;
border: 1rpx solid;
background-color: transparent;
padding: 7rpx 12rpx;
}
.tag-icon {
width: 26rpx;
height: 24rpx;
margin-right: 6rpx;
flex-shrink: 0;
}
.tag-text {
font-size: 24rpx;
line-height: 28rpx;
white-space: nowrap;
margin-top: 4rpx;
padding-right: 2rpx;
}
.tag-text1 {
font-size: 40rpx;
line-height: 28rpx;
white-space: nowrap;
margin-top: 4rpx;
padding-right: 2rpx;
margin-bottom: 6rpx;
font-weight: 200;
}
</style>

View File

@ -156,6 +156,7 @@
weChatRefund: "/sj/workSeatOrder/weChatRefund",
aliRefund: "/sj/workSeatOrder/aliRefund",
startOrder: "/sj/workSeatOrder/start",
sureOrder:"/sj/workSeatOrder/sure",
endOrder: "/sj/workSeatOrder/end",
ordernum: "/sj/workSeatOrder/orderNum",
list: "/sj/workSeatOrder/list",
@ -209,7 +210,7 @@
secondTabs() {
if (this.activeFirstId == 1 || this.activeFirstId == 2) {
return [{
title: `服务(${this.numInfo.noservernum})`,
title: `开始(${this.numInfo.noservernum})`,
id: 3
},
{
@ -405,6 +406,7 @@
return;
}
this.clickType = true;
console.log(111,this.activeFirstId,this.urls)
request
.post(this.urls[this.activeFirstId].sureOrder, {
id: order.id,
@ -414,7 +416,7 @@
.then((res) => {
if (res.state != 1 || res.code != 200) {
uni.showToast({
title: res.msg,
title: "开始服务",
icon: "none",
});
} else {
@ -459,7 +461,7 @@
console.log(res);
if (res.state == 1 || res.code == 200) {
uni.showToast({
title: res.msg,
title: "开始服务",
icon: "none",
});
this.searchList();
@ -513,27 +515,27 @@
if (this.idType == 1) {
if (this.activeFirstId == 1) {
uni.navigateTo({
url: `/pages/shop/userorder-detail?order_id=${order.id}&order_kind=${order.order_kind}`,
url: `/pages/shop/userorder-detail?order_id=${order.id}&order_kind=${order.order_kind}&haveButton=${this.haveButton}`,
});
}
if (this.activeFirstId == 2) {
uni.navigateTo({
url: `/pages/shop/userorder-detail?order_id=${order.id}&serviceType=2`,
url: `/pages/shop/userorder-detail?order_id=${order.id}&serviceType=2&haveButton=${this.haveButton}`,
});
}
if (this.activeFirstId == 3) {
uni.navigateTo({
url: `/pages/shop/SellerDetail?order_id=${order.id}`,
url: `/pages/shop/SellerDetail?order_id=${order.id}&haveButton=${this.haveButton}`,
});
}
if (this.activeFirstId == 4) {
uni.navigateTo({
url: `/pages/shop/userorder-detail?order_id=${order.id}&order_kind=${order.order_kind}`,
url: `/pages/shop/userorder-detail?order_id=${order.id}&order_kind=${order.order_kind}&haveButton=${this.haveButton}`,
});
}
if (this.activeFirstId == 5) {
uni.navigateTo({
url: `/pages/shop/userorder-detail?order_id=${order.id}&order_kind=${order.order_kind}&serviceType=2`,
url: `/pages/shop/userorder-detail?order_id=${order.id}&order_kind=${order.order_kind}&serviceType=2&haveButton=${this.haveButton}`,
});
}
}
@ -576,7 +578,7 @@
//
&.active {
color: #e8101e; //
color: #FF4767; //
font-weight: 600; //
//
@ -590,8 +592,8 @@
bottom: -6rpx; //
height: 6rpx; //
background: linear-gradient(306deg,
#e8101e 0%,
#e8101e 100%); //
#FF4767 0%,
#FF4767 100%); //
border-radius: 3rpx; //
}
}
@ -626,7 +628,7 @@
//
&.active {
color: #e8101e; //
color: #FF4767; //
background: rgba(252, 67, 124, 0.1); //
font-weight: 600; //
}

View File

@ -0,0 +1,101 @@
<template>
<view class="notice-item" @tap="handleClick">
<view class="item-main">
<text class="item-title line-2">{{ item.title }}</text>
<view class="item-time">
<text class="time-text">发布时间{{ formattedTime }}</text>
</view>
</view>
</view>
</template>
<script>
export default {
name: "NoticeCard",
props: {
//
item: {
type: Object,
default: () => ({})
}
},
computed: {
//
formattedTime() {
const timeStr = this.item.create_time;
if (!timeStr) return '--:--:--';
let formatted = timeStr;
if (timeStr.includes('.') && !timeStr.includes('-')) {
formatted = timeStr.replace(/\./g, '-');
if (formatted.match(/\d{4}-\d{2}-\d{2}\d{2}:\d{2}:\d{2}/)) {
formatted = formatted.replace(/(\d{4}-\d{2}-\d{2})(\d{2}:\d{2}:\d{2})/, '$1 $2');
}
}
if (formatted.includes('-') && formatted.includes(':')) {
return formatted;
}
return timeStr;
}
},
methods: {
handleClick() {
// click
this.$emit('click', this.item);
}
}
};
</script>
<style lang="scss" scoped>
.notice-item {
display: flex;
align-items: center;
justify-content: space-between;
background: #ffffff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
transition: all 0.2s ease;
.item-main {
flex: 1;
min-width: 0; //
.item-title {
font-size: 30rpx;
font-weight: 500;
color: #101010;
line-height: 42rpx;
margin-bottom: 16rpx;
}
.item-time {
display: flex;
align-items: center;
.time-icon {
width: 24rpx;
height: 24rpx;
margin-right: 8rpx;
opacity: 0.6;
}
.time-text {
font-size: 22rpx;
color: #999;
line-height: 30rpx;
}
}
}
}
//
.line-2 {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
word-break: break-all;
}
</style>

View File

@ -171,6 +171,7 @@
},
emitStart() {
this.$emit("start");
},
emitDone() {
this.$emit("done");
@ -181,8 +182,8 @@
<style scoped lang="less">
.order-card {
padding: 20rpx 30rpx 20rpx 30rpx;
margin: 0rpx 30rpx 20rpx 30rpx;
padding: 24rpx 20rpx 24rpx 20rpx;
margin: 0;
background-color: #fff;
border-radius: 12px;
/* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */
@ -205,7 +206,7 @@
.stateText {
font-weight: 400;
font-size: 26rpx;
color: #E8101E;
color: #FF4767;
text-align: left;
font-style: normal;
position: absolute;
@ -255,7 +256,7 @@
.service-price {
font-weight: 500;
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
line-height: 34rpx;
text-align: left;
font-style: normal;
@ -292,7 +293,7 @@
letter-spacing: 2px;
text-align: left;
font-style: normal;
background: linear-gradient(166deg, #FC5E72 0%, #E8101E 100%);
background: linear-gradient(166deg, #FC5E72 0%, #FF4767 100%);
border-radius: 6rpx;
padding: 4rpx 4rpx 4rpx 8rpx;
margin-right: 13rpx;
@ -388,8 +389,8 @@
/* 接单按钮 */
.accept-btn {
border: 2rpx solid #E8101E;
color: #E8101E;
border: 2rpx solid #FF4767;
color: #FF4767;
}
.order-address {

View File

@ -129,7 +129,7 @@
.tab-text {
font-weight: 500;
color: #E8101E;
color: #FF4767;
&::after{
content: '';
//
@ -139,7 +139,7 @@
transform: translateX(-50%);
bottom: -6rpx; //
height: 6rpx; //
background: linear-gradient( 306deg, #E8101E 0%, #F25988 100%); //
background: linear-gradient( 306deg, #FF4767 0%, #F25988 100%); //
border-radius: 3rpx; //
}
}

View File

@ -0,0 +1,196 @@
<template>
<view class="skill-item" @click="handleClick">
<view class="cover-box">
<image class="cover" :src="item.coverSrc" mode="aspectFill"></image>
<image v-if="item.video" class="play-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/5c1ddfef-a200-424a-a9dc-03d9395568f6.png"></image>
</view>
<view class="skill-info">
<view class="skill-title line-2">{{ item.title }}</view>
<view class="skill-type" :class="typeClass">
<image class="type-icon" :src="item.typeIcon || defaultTypeIcon"></image>
<text class="type-text">{{ item.type || '平台课程' }}</text>
</view>
<view class="skill-stats">
<image class="view-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/2409b64a-56bb-4d9e-a4cc-5b3444fcf097.png">
</image>
{{ formatViews(item.views) }}观看 · {{ item.time }}
</view>
</view>
</view>
</template>
<script>
export default {
name: 'SkillItem',
props: {
item: {
type: Object,
required: true,
default: () => ({
id: null,
title: '',
coverSrc: '',
type: '平台课程',
views: 0,
time: '',
typeIcon: ''
})
}
},
data() {
return {
defaultTypeIcon: 'https://dummyimage.com/30x30/ccc/fff.png&text=I' //
}
},
computed: {
typeClass() {
const typeMap = {
'平台课程': 'platform-course',
'脸缘品牌': 'brand-course',
'品牌课程': 'brand-course'
}
return typeMap[this.item.type] || 'platform-course'
}
},
methods: {
formatViews(views) {
if (!views) return '0'
if (views >= 10000) {
return (views / 10000).toFixed(1) + 'w'
}
return views.toString()
},
handleClick() {
// 1. emit便
this.$emit('click', this.item);
// 2.
if (!uni.getStorageSync("accessToken")) {
uni.navigateTo({
url: '/pages/blogPopup/blogPopup'
});
return;
}
// 3.
if (this.item && this.item.id) {
uni.navigateTo({
url: `/pages/jingxuan/skill-detail?id=${this.item.id}`
});
}
}
}
}
</script>
<style lang="scss" scoped>
.skill-item {
display: flex;
margin-bottom: 30rpx;
&:last-child {
margin-bottom: 0;
}
.cover-box {
position: relative;
width: 224rpx;
height: 126rpx;
border-radius: 16rpx;
overflow: hidden;
margin-right: 24rpx;
flex-shrink: 0;
.cover {
width: 100%;
height: 100%;
}
.play-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 39rpx;
height: 39rpx;
}
}
.skill-info {
flex: 1;
display: flex;
min-width: 0;
flex-direction: column;
justify-content: space-between;
.skill-title {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
text-overflow: ellipsis;
overflow: hidden;
font-size: 28rpx;
color: #333;
font-weight: 500;
line-height: 40rpx;
}
.skill-type {
display: flex;
align-items: center;
font-size: 22rpx;
margin-top: 10rpx;
.type-icon {
border: 1rpx solid #D6CAC0;
width: 26rpx;
height: 26rpx;
margin-right: 8rpx;
border-radius: 50%;
}
.type-text {
border-radius: 7rpx;
font-weight: 400;
font-size: 24rpx;
line-height: 33rpx;
padding: 2rpx 10rpx;
}
&.platform-course .type-text {
background: #f9d3d760;
color: #E55463;
}
&.brand-course .type-text {
background: #f9d3d760;
color: #E55463;
}
}
.skill-stats {
font-size: 22rpx;
color: #999;
margin-top: 10rpx;
display: flex;
align-items: center;
.view-icon {
width: 26rpx;
height: 22rpx;
margin-right: 8rpx;
margin-top: -2rpx;
}
}
}
}
.line-2 {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
</style>

View File

@ -45,7 +45,7 @@
</template>
<script>
export default {
export default {
name: "OrderCard",
props: {
dataItem: {
@ -61,57 +61,18 @@
},
computed: {
photo() {
return this.dataItem.photo? JSON.parse(this.dataItem.photo):"";
return this.dataItem.photo ? JSON.parse(this.dataItem.photo) : "";
},
useTime() {
const start = this.dataItem.reserve_start_time;
const endTime = this.dataItem.reserve_end_time;
function formatTimeRange(startTimeStr, endTimeStr) {
if (!startTimeStr || !endTimeStr) return "未设置";
// Date
const startTime = new Date(startTimeStr.replace(/-/g, '/'));
const endTime = new Date(endTimeStr.replace(/-/g, '/'));
if (isNaN(startTime.getTime()) || isNaN(endTime.getTime())) {
console.error("无效的时间格式");
return "时间格式错误";
}
// YYYY
const formatYear = (date) => {
return date.getFullYear() + '年';
};
// MMDD
const formatMonthDay = (date) => {
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${month}${day}`;
};
// HH:MM
const formatTimePart = (date) => {
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
return `${hours}:${minutes}`;
};
//
const isSameDay = startTime.getFullYear() === endTime.getFullYear() &&
startTime.getMonth() === endTime.getMonth() &&
startTime.getDate() === endTime.getDate();
if (isSameDay) {
// 20250529 12:30-13:30
return `${formatYear(startTime)}${formatMonthDay(startTime)} ${formatTimePart(startTime)}-${formatTimePart(endTime)}`;
} else {
// 20250529 12:30-20250530 13:30
return `${formatYear(startTime)}${formatMonthDay(startTime)} ${formatTimePart(startTime)}-${formatYear(endTime)}${formatMonthDay(endTime)} ${formatTimePart(endTime)}`;
}
}
return formatTimeRange(start, endTime);
const end = this.dataItem.reserve_end_time;
if (!start || !end) return "未设置";
const startDate = new Date(start.replace(/-/g, '/'));
const endDate = new Date(end.replace(/-/g, '/'));
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) return "时间格式错误";
const pad = (n) => String(n).padStart(2, "0");
const fmt = (d) => `${pad(d.getMonth() + 1)}.${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
return `${fmt(startDate)}-${fmt(endDate)}`;
},
stateText() {
const textList = [
@ -152,20 +113,20 @@
this.$emit("goRefund", this.dataItem);
}
},
};
};
</script>
<style lang="less" scoped>
.order-card {
.order-card {
position: relative; //
padding: 24rpx 20rpx 0rpx;
margin: 0rpx 30rpx 20rpx 30rpx;
background-color: #fff;
border-radius: 12px;
/* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */
}
}
.orderNumBox {
.orderNumBox {
display: flex;
align-items: center;
justify-content: space-between;
@ -174,7 +135,7 @@
.stateText {
font-weight: 400;
font-size: 26rpx;
color: #E8101E;
color: #FF4767;
text-align: left;
font-style: normal;
position: absolute;
@ -200,35 +161,35 @@
background-image: url("/static/images/icons/tabBj2.png");
color: #999999 !important;
}
}
}
/* 订单编号 */
.order-id {
/* 订单编号 */
.order-id {
width: 530rpx;
font-size: 28rpx;
color: #333333;
}
}
/* 服务信息容器 */
.service-container {
/* 服务信息容器 */
.service-container {
display: flex;
align-items: center;
padding-bottom: 24rpx;
border-bottom: 1px solid #f6f5f5;
width: 100%;
}
}
/* 服务图片 */
.service-img {
/* 服务图片 */
.service-img {
width: 172rpx;
height: 168rpx;
border-radius: 24rpx;
margin-right: 30rpx;
flex-shrink: 0;
}
}
/* 服务信息 */
.service-info {
/* 服务信息 */
.service-info {
display: flex;
flex-direction: column;
justify-content: space-between;
@ -262,33 +223,33 @@
font-family: DINPro, DINPro;
font-weight: 500;
font-size: 36rpx;
color: #E8101E;
color: #FF4767;
line-height: 34rpx;
text-align: left;
font-style: normal;
}
}
}
.service_subTitle {
.service_subTitle {
font-size: 26rpx;
line-height: 26rpx;
color: #666666;
margin-top: 18rpx;
}
}
.service-address {
.service-address {
font-weight: 400;
font-size: 26rpx;
color: #999999;
line-height: 33rpx;
text-align: left;
font-style: normal;
}
}
/* 时间信息 */
.time-info {
/* 时间信息 */
.time-info {
font-weight: 400;
font-size: 26rpx;
color: #333333;
@ -297,18 +258,18 @@
font-style: normal;
padding-bottom: 20rpx;
margin-top: 28rpx;
}
}
/* 按钮组 */
.button-group {
/* 按钮组 */
.button-group {
display: flex;
justify-content: flex-end;
gap: 30rpx;
padding-bottom: 30rpx;
}
}
/* 通用按钮样式 */
.btn {
/* 通用按钮样式 */
.btn {
white-space: nowrap;
width: 100rpx;
height: 32rpx;
@ -327,17 +288,17 @@
line-height: 64rpx;
// padding: 0 28rpx;
}
}
/* 取消按钮 */
.cancel-btn {
/* 取消按钮 */
.cancel-btn {
border: 2rpx solid #979797;
color: #333333;
}
}
/* 接单按钮 */
.accept-btn {
border: 2rpx solid #E8101E;
color: #E8101E;
}
/* 接单按钮 */
.accept-btn {
border: 2rpx solid #FF4767;
color: #FF4767;
}
</style>

View File

@ -0,0 +1,167 @@
<template>
<view class="growth-package-result-page">
<custom-navbar
title="支付结果"
:showBack="true"
backgroundColor="#F7F7F7"
borderBottom="none"
></custom-navbar>
<view class="result-main">
<view class="result-icon" :class="isFail ? 'is-fail' : 'is-success'">
<block v-if="isFail">
<view class="fail-line"></view>
<view class="fail-dot"></view>
</block>
<view v-else class="success-check"></view>
</view>
<view class="result-title">{{ resultInfo.title }}</view>
<view class="result-desc">{{ resultInfo.desc }}</view>
<view class="primary-btn" @tap="handlePrimaryTap">{{ isFail ? "重新支付" : "返回首页" }}</view>
</view>
</view>
</template>
<script>
import CustomNavbar from "@/components/custom-navbar/custom-navbar.vue";
export default {
components: {
CustomNavbar,
},
data() {
return {
status: "success",
};
},
computed: {
isFail() {
return this.status === "fail";
},
resultInfo() {
if (this.isFail) {
return {
title: "报名失败",
desc: "最后一步出了点问题 请重新支付",
};
}
return {
title: "报名成功",
desc: "您已完成支付 感谢你对美融融的支持",
};
},
},
onLoad(options) {
this.status = options.status === "fail" ? "fail" : "success";
},
methods: {
handlePrimaryTap() {
if (this.isFail) {
uni.redirectTo({
url: "/pages/home/growth-package",
});
return;
}
uni.switchTab({
url: "/pages/home/home",
});
},
},
};
</script>
<style lang="scss" scoped>
page {
background: #ffffff;
}
.growth-package-result-page {
min-height: 100vh;
background: #ffffff;
padding-bottom: env(safe-area-inset-bottom);
}
.result-main {
display: flex;
flex-direction: column;
align-items: center;
padding: 246rpx 40rpx 0;
}
.result-icon {
width: 104rpx;
height: 104rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.result-icon.is-success {
background: #12c76b;
}
.result-icon.is-fail {
background: #ff4652;
}
.success-check {
width: 44rpx;
height: 24rpx;
border-left: 8rpx solid #ffffff;
border-bottom: 8rpx solid #ffffff;
transform: rotate(-45deg);
margin-top: -8rpx;
}
.fail-line {
width: 8rpx;
height: 46rpx;
border-radius: 8rpx;
background: #ffffff;
}
.fail-dot {
width: 8rpx;
height: 8rpx;
margin-left: -8rpx;
margin-top: 58rpx;
border-radius: 50%;
background: #ffffff;
}
.result-title {
margin-top: 34rpx;
font-weight: 600;
font-size: 40rpx;
color: #333333;
line-height: 56rpx;
text-align: center;
}
.result-desc {
margin-top: 8rpx;
font-weight: 400;
font-size: 26rpx;
color: #b9b9b9;
line-height: 38rpx;
text-align: center;
}
.primary-btn {
width: 350rpx;
height: 80rpx;
margin-top: 144rpx;
border: 1rpx solid #ff4b67;
border-radius: 40rpx;
box-sizing: border-box;
background: #ffffff;
display: flex;
align-items: center;
justify-content: center;
font-weight: 400;
font-size: 30rpx;
color: #ff4767;
line-height: 42rpx;
}
</style>

View File

@ -0,0 +1,348 @@
<template>
<view class="growth-package-page">
<image class="content-image" src="@/static/images/home/growth-package/intro.png" mode="widthFix"></image>
<view class="deal-carousel">
<swiper class="deal-swiper" autoplay circular :interval="3000" previous-margin="40rpx"
next-margin="40rpx" :current="dealCurrentIndex" @change="handleDealChange">
<swiper-item v-for="(item, index) in dealSlides" :key="item">
<view class="deal-slide" :class="getDealSlideClass(index)">
<image class="deal-slide-image" :src="item" mode="aspectFit"></image>
</view>
</swiper-item>
</swiper>
<view class="deal-indicator">
<view class="indicator-track" v-for="(item, index) in dealSlides" :key="item"
:class="{ active: index === dealCurrentIndex }"></view>
</view>
</view>
<image class="content-image" src="@/static/images/home/growth-package/plan.png" mode="widthFix"></image>
<view class="fixed-action" @tap="handlePayTap">
<image class="action-image" :src="actionImage" mode="widthFix"></image>
</view>
</view>
</template>
<script>
import request from "@/utils/request";
export default {
name: "GrowthPackage",
data() {
return {
dealCurrentIndex: 0,
isPaying: false,
isJoined: false,
dealSlides: [
"/static/images/home/growth-package/deal-1.png",
"/static/images/home/growth-package/deal-2.png",
"/static/images/home/growth-package/deal-3.png",
],
};
},
computed: {
actionImage() {
return this.isJoined
? "/static/images/home/growth-package/action-success.png"
: "/static/images/home/growth-package/action.png";
},
},
onShow() {
this.getGrowthPackageStatus();
},
methods: {
async getGrowthPackageStatus() {
if (!uni.getStorageSync("accessToken")) {
this.isJoined = false;
return;
}
if (this.isPaying) return;
uni.showLoading({
title: "查询报名状态...",
mask: true,
});
try {
const res = await request.post(
"/sj/growthPackage/status",
{},
{
hideLoading: true,
}
);
this.isJoined = !!(res && res.code === 200 && res.data && Number(res.data.is_joined) === 1);
} catch (error) {
console.log("growthPackageStatus error", error);
} finally {
uni.hideLoading();
}
},
handleDealChange(event) {
this.dealCurrentIndex = event.detail.current;
},
getDealSlideClass(index) {
const length = this.dealSlides.length;
const prevIndex = (this.dealCurrentIndex + length - 1) % length;
const nextIndex = (this.dealCurrentIndex + 1) % length;
if (index === this.dealCurrentIndex) {
return "is-active";
}
if (index === prevIndex) {
return "is-prev";
}
if (index === nextIndex) {
return "is-next";
}
return "";
},
handlePayTap() {
if (this.isPaying) return;
if (this.isJoined) return;
if (!uni.getStorageSync("accessToken")) {
uni.navigateTo({
url: "/pages/blogPopup/blogPopup",
});
return;
}
uni.showActionSheet({
itemList: ["微信支付", "支付宝支付"],
success: (res) => {
const payKind = res.tapIndex === 0 ? 2 : 3;
this.createGrowthPackagePay(payKind);
},
});
},
async createGrowthPackagePay(payKind) {
this.isPaying = true;
uni.showLoading({
title: "支付中...",
mask: true,
});
try {
const payRes = await request.post(
"/sj/growthPackage/pay",
{
pay_kind: payKind,
},
{
hideLoading: true,
}
);
if (!payRes || payRes.code !== 200) {
uni.hideLoading();
this.isPaying = false;
if (payRes && payRes.data && (payRes.data.id || payRes.data.number)) {
this.goToPayResult("fail", payRes.data);
return;
}
uni.showToast({
title: (payRes && payRes.msg) || "支付请求失败",
icon: "none",
});
return;
}
const orderInfo = payRes.data || {};
if (Number(orderInfo.is_joined) === 1) {
uni.hideLoading();
this.isPaying = false;
this.isJoined = true;
this.goToPayResult("success", orderInfo.order || {});
return;
}
const paymentParams = orderInfo.pay_params || orderInfo.orderInfo;
if (!paymentParams) {
uni.hideLoading();
this.isPaying = false;
if (orderInfo.id || orderInfo.number) {
this.goToPayResult("fail", orderInfo);
return;
}
uni.showToast({
title: "支付参数缺失",
icon: "none",
});
return;
}
uni.hideLoading();
this.requestGrowthPackagePayment(payKind, paymentParams, orderInfo);
} catch (error) {
uni.hideLoading();
uni.showToast({
title: "支付请求失败",
icon: "none",
});
this.isPaying = false;
console.log("growthPackagePay error", error);
}
},
requestGrowthPackagePayment(payKind, paymentParams, orderInfo) {
const provider = payKind === 2 ? "wxpay" : "alipay";
let payOrderInfo = paymentParams;
if (payKind === 2 && paymentParams && typeof paymentParams === "object") {
payOrderInfo = {
...paymentParams,
};
const systemInfo = uni.getSystemInfoSync();
if (systemInfo.platform === "ios" && payOrderInfo.appid) {
payOrderInfo.appId = payOrderInfo.appid;
delete payOrderInfo.appid;
}
}
uni.requestPayment({
provider,
orderInfo: payOrderInfo,
success: () => {
this.queryGrowthPackagePay(orderInfo);
},
fail: (err) => {
this.isPaying = false;
console.log("growthPackage requestPayment fail", err);
this.goToPayResult("fail", orderInfo);
},
});
},
async queryGrowthPackagePay(orderInfo) {
try {
const queryRes = await request.post(
"/sj/growthPackage/queryPay",
{
id: orderInfo.id,
number: orderInfo.number,
},
{
hideLoading: true,
}
);
const order = queryRes && queryRes.data && queryRes.data.order;
if (queryRes && queryRes.code === 200 && order && Number(order.state) === 2) {
this.isJoined = true;
this.goToPayResult("success", order);
return;
}
this.goToPayResult("fail", orderInfo);
} catch (error) {
console.log("growthPackageQueryPay error", error);
this.goToPayResult("fail", orderInfo);
} finally {
this.isPaying = false;
}
},
goToPayResult(status, orderInfo = {}) {
const params = [`status=${status}`];
if (orderInfo.number) {
params.push(`number=${encodeURIComponent(orderInfo.number)}`);
}
if (orderInfo.pay_money || orderInfo.order_money) {
params.push(`money=${encodeURIComponent(orderInfo.pay_money || orderInfo.order_money)}`);
}
uni.redirectTo({
url: `/pages/home/growth-package-result?${params.join("&")}`,
});
},
},
}
</script>
<style lang="scss">
page {
background: #150903;
}
.growth-package-page {
min-height: 100vh;
padding-bottom: 259rpx;
background: #150903;
.content-image {
display: block;
width: 750rpx;
}
.deal-carousel {
position: relative;
width: 750rpx;
height: 1342rpx;
background: #000000;
overflow: hidden;
}
.deal-swiper {
width: 750rpx;
height: 1280rpx;
background: #000000;
}
.deal-slide {
position: relative;
width: 670rpx;
height: 1280rpx;
background: #000000;
overflow: hidden;
}
.deal-slide-image {
display: block;
width: 670rpx;
height: 1280rpx;
}
.deal-slide.is-prev,
.deal-slide.is-next {
height: 1169rpx;
margin-top: 56rpx;
}
.deal-slide.is-prev .deal-slide-image,
.deal-slide.is-next .deal-slide-image {
position: absolute;
top: -56rpx;
}
.deal-slide.is-prev .deal-slide-image {
right: 10rpx;
border-top-right-radius: 20rpx;
border-bottom-right-radius: 20rpx;
}
.deal-slide.is-next .deal-slide-image {
left: 10rpx;
border-top-left-radius: 20rpx;
border-bottom-left-radius: 20rpx;
}
.deal-indicator {
display: flex;
align-items: center;
justify-content: center;
height: 62rpx;
column-gap: 8rpx;
background: #000000;
}
.indicator-track {
width: 146rpx;
height: 8rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.42);
}
.indicator-track.active {
background: #ffffff;
}
.fixed-action {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 20;
width: 750rpx;
height: 259rpx;
background: #150903;
}
.action-image {
display: block;
width: 750rpx;
}
}
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,544 @@
<template>
<view>
<custom-navbar title="精选品牌"
:leftImg="'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/60ae3383-6ff6-4f42-818b-d9b09a9bd0e0.png'"
:showUser="true"
backgroundColor="url('https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/ce2e62e1-c26a-45c8-a49d-7ed00b2ce02c.png') center 20%/cover no-repeat"
titleColor="#333" borderBottom="none" :show-headle="true"
headleSrc="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/51ebca5d-95c5-4fb8-8aa3-560a79100d25.png"
@onHeadleClick="goToSearch" class="nav-img-size">
</custom-navbar>
<serviecFirstTab :tabs="tabs" :activeId="activeId" activeColor="#FF4767" @tab-click="tabClick" bgColor="#fff">
</serviecFirstTab>
<CommonList ref="brandListRef" :apiUrl="'/sj/brand/list'" :initialQuery="{}"
:listScrollHeight="`calc(100vh - ${statusBarHeight * 2 + 250}rpx)`" @data-loaded="handleDataLoaded">
<template #headerScroll>
<view class="icon-tabs">
<view v-for="item in iconTabs" :key="item.id" class="icon-tab"
:class="{ active: currentSecondClass == item.id }" @click="selectSecondClass(item.id)">
<image :src="item.photo" class="icon-img" mode="aspectFill"></image>
<text class="icon-text">{{ item.title }}</text>
</view>
</view>
<view class="sort-bar">
<view class="sort-item" @tap="toggleSort('time')">
<text class="sort-text" :class="{ active: orderType === 1 || orderType === 2 }">发布时间</text>
<view class="arrows">
<image class="arrow-icon" :src="orderType === 2 ? iconPinkUp : iconGreyUp"></image>
<image class="arrow-icon" :src="orderType === 1 ? iconPinkDown : iconGreyDown"></image>
</view>
</view>
<view class="sort-item" @tap="toggleSort('view')">
<text class="sort-text" :class="{ active: orderType === 3 || orderType === 4 }">浏览量</text>
<view class="arrows">
<image class="arrow-icon" :src="orderType === 4 ? iconPinkUp : iconGreyUp"></image>
<image class="arrow-icon" :src="orderType === 3 ? iconPinkDown : iconGreyDown"></image>
</view>
</view>
</view>
</template>
<template #listData="{ list }">
<view class="zhanwei">
<view class="brand-grid">
<brand-card class="brand-card-wrap" v-for="(brand, idx) in enrichedBrandList(list)" :key="brand.id || idx"
:item="brand" :tagKeys="brand._tagKeys" @click="handleBrandClick(brand)" />
</view>
</view>
</template>
<template #empty>
<noData></noData>
</template>
</CommonList>
</view>
</template>
<script>
import request from "../../utils/request";
import serviecFirstTab from "@/components/common/service-first-tab.vue";
import CommonList from "@/components/common/CommonList.vue";
import brandCard from "@/pages/home/components/brand-card.vue";
import noData from "@/components/noData/noData.vue";
export default {
components: {
serviecFirstTab,
CommonList,
brandCard,
noData
},
data() {
return {
statusBarHeight: 0,
navHeight: 0,
// /ID
activeId: "",
currentSecondClass: "",
tabs: [],
iconTabs: [],
selectedSecondIds: [], // ID
//
orderType: 0,
//
iconGreyUp: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e00ab2ac-bccf-42d8-820c-65727327e279.png',
iconGreyDown: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/dfffb601-6877-4608-a3eb-88695b05c6c2.png',
iconPinkUp: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/5bc84dff-e120-4d4b-a33e-25d68e065654.png',
iconPinkDown: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/ff2c2c9d-694a-4adf-b737-66b07f4a63ae.png',
};
},
onLoad(options) {
const systemInfo = uni.getSystemInfoSync();
this.statusBarHeight = systemInfo.statusBarHeight;
this.navHeight = this.statusBarHeight + 44;
if (options.id) {
this.activeId = options.id;
}
this.getFirstClass();
//
this.loadFilterCategories();
},
methods: {
//
goToSearch() {
uni.navigateTo({
url: '/pages/search_common/search?type=brand' //
});
},
// state === 1
async loadFilterCategories() {
try {
const syrId = uni.getStorageSync('syrId') || 1;
const res = await request.post('/sj/firstclass', { id: syrId })
if ((res.code === 200 || res.state === 1) && res.data) {
const firstClasses = res.data;
//
const promises = firstClasses.map(async (first) => {
try {
const subRes = await request.post('/sj/secondclass', { id: first.id })
return {
id: first.id,
title: first.title,
children: ((subRes.code === 200 || subRes.state === 1) && subRes.data) ? subRes.data : []
}
} catch (e) {
return { id: first.id, title: first.title, children: [] }
}
});
const filterData = await Promise.all(promises);
//
this.brandFilterData = filterData.filter(group => group.children.length > 0);
} else {
this.brandFilterData = []
}
} catch (error) {
console.error('获取分类字典失败', error)
}
},
// ============ ============
toggleSort(type) {
if (type === 'time') {
if (this.orderType === 1) {
this.orderType = 2;
} else if (this.orderType === 2) {
this.orderType = 0;
} else {
this.orderType = 1;
}
} else if (type === 'view') {
if (this.orderType === 3) {
this.orderType = 4;
} else if (this.orderType === 4) {
this.orderType = 0;
} else {
this.orderType = 3;
}
}
this.refreshBrandList();
},
// ============ ============
getFirstClass() {
request.post("/sj/firstclass").then((res) => {
this.tabs = res.data || [];
if (this.tabs.length && !this.activeId) {
this.activeId = this.tabs[0].id;
}
this.getSecondClassList();
this.refreshBrandList();
});
},
getSecondClassList() {
if (!this.activeId) return;
request.post('/sj/secondclass', { id: this.activeId }).then((res) => {
this.iconTabs = res.data || [];
});
},
selectSecondClass(id) {
if (this.currentSecondClass === id) {
this.currentSecondClass = "";
} else {
this.currentSecondClass = id;
}
//
this.selectedSecondIds = [];
this.refreshBrandList();
},
tabClick(id) {
if (this.activeId === id) return;
this.activeId = id;
this.currentSecondClass = "";
this.selectedSecondIds = [];
this.getSecondClassList();
this.refreshBrandList();
},
// ============ ============
refreshBrandList() {
//
const targetSecondClass = this.selectedSecondIds.length > 0
? this.selectedSecondIds.join(',')
: this.currentSecondClass;
const params = {
first_class: this.activeId || undefined,
second_class: targetSecondClass || undefined,
// orderType 0 undefined使
order_type: this.orderType === 0 ? undefined : this.orderType
};
Object.keys(params).forEach(key => {
if (params[key] === undefined) delete params[key];
});
if (this.$refs.brandListRef) {
// CommonList queryData
['first_class', 'second_class', 'order_type'].forEach(key => {
if (!Object.prototype.hasOwnProperty.call(params, key) && this.$refs.brandListRef.queryData) {
this.$delete(this.$refs.brandListRef.queryData, key);
}
});
this.$refs.brandListRef.manualRefresh(params);
}
},
enrichedBrandList(list) {
if (!list || !list.length) return [];
return list.map(brand => {
const tagKeys = [];
if (brand.franchise_state === 1) tagKeys.push('franchise');
if (brand.student_state === 1) tagKeys.push('recruit');
if (tagKeys.length === 0) tagKeys.push('more');
return {
...brand,
name: brand.name,
bgSrc: brand.cover_img || 'https://dummyimage.com/300x200/ccc/fff.png',
avatar: brand.logo_img || 'https://dummyimage.com/100x100/ccc/fff.png',
_tagKeys: tagKeys
};
});
},
handleBrandClick(brand) {
uni.navigateTo({
url: `/pages/brand/detail?id=${brand.id}`
});
},
handleDataLoaded(data) {
console.log("品牌列表加载完成", data);
}
}
};
</script>
<style lang="less" scoped>
//
::v-deep .icon-headle {
width: 32rpx !important;
height: 32rpx !important;
}
// Tab
.service-first-tab {
border-bottom: none;
padding-bottom: 27rpx;
position: relative;
z-index: 101;
}
::v-deep .service-first-tab {
box-shadow: none !important;
}
/* ================= 抽屉下拉样式 ================= */
.filter-panel {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
pointer-events: none;
overflow: hidden;
&.show {
pointer-events: auto;
.filter-mask {
opacity: 1;
}
.filter-container {
transform: translateY(0);
}
}
.filter-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
.filter-container {
position: absolute;
top: 0;
left: 0;
right: 0;
background: #fff;
border-radius: 0 0 30rpx 30rpx;
padding: 140rpx 30rpx 40rpx;
display: flex;
flex-direction: column;
box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.05);
transform: translateY(-100%);
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}
}
.filter-scroll {
max-height: 50vh;
}
.filter-group {
margin-bottom: 30rpx;
.group-title {
font-weight: 500;
font-size: 28rpx;
color: #333333;
line-height: 40rpx;
margin-bottom: 20rpx;
}
.pill-list {
display: flex;
flex-wrap: wrap;
gap: 20rpx 10rpx;
/* 控制行间距和列间距 */
}
}
/* 通用胶囊按钮 (一行四个) */
.pill-item {
width: calc(25% - 8rpx);
/* 精确计算一行四个 */
height: 58rpx;
background: #F5F5F5;
font-weight: 400;
font-size: 24rpx;
/* 字号略小以防溢出 */
color: #333333;
line-height: 37rpx;
/* flex 居中文字 */
display: flex;
align-items: center;
justify-content: center;
border-radius: 12rpx;
transition: all 0.3s;
box-sizing: border-box;
white-space: nowrap;
&.active {
background: #FFF0F2;
color: #FF4767;
border: 1rpx solid #FFB0BD;
}
}
.filter-footer {
display: flex;
justify-content: space-between;
margin-top: 40rpx;
.reset-btn,
.confirm-btn {
width: 47%;
text-align: center;
padding: 22rpx 0;
border-radius: 40rpx;
font-size: 28rpx;
}
.reset-btn {
background: #F5F5F5;
color: #666;
}
.confirm-btn {
background: #FF4767;
color: #fff;
}
}
/* ================= 其他页面样式 ================= */
//
.icon-tabs {
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
align-items: center;
column-gap: 36rpx;
row-gap: 30rpx;
background: transparent;
padding: 0 0 0 30rpx;
margin-bottom: 30rpx;
}
.icon-tab {
display: flex;
flex-direction: column;
align-items: center;
}
.icon-img {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
border: 2rpx solid transparent;
}
.icon-text {
margin-top: 10rpx;
font-weight: 400;
font-size: 24rpx;
color: #333333;
line-height: 33rpx;
text-align: left;
}
.icon-tab.active .icon-text {
color: #FF4767;
line-height: 33rpx;
}
.icon-tab.active .icon-img {
border: 2rpx solid #FF4767;
}
//
.sort-bar {
display: flex;
align-items: center;
padding: 0 30rpx;
margin-bottom: 24rpx;
gap: 100rpx;
.sort-item {
display: flex;
align-items: center;
.sort-text {
font-weight: 400;
font-size: 26rpx;
color: #666666;
line-height: 37rpx;
margin-right: 6rpx;
&.active {
color: #FF4767;
font-weight: 500;
}
}
.arrows {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.arrow-icon {
width: 13rpx;
height: 8rpx;
margin: 2rpx 0;
}
}
}
}
//
.zhanwei {
background-color: #f5f5f5;
padding: 20rpx 0 0 0;
border-radius: 20rpx 20rpx 0rpx 0rpx;
}
.brand-grid {
background-color: #ffffff;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 20rpx 24rpx;
row-gap: 24rpx;
border-radius: 20rpx 20rpx 0rpx 0rpx;
.brand-card-wrap {
width: 48.5%;
}
}
.common-list {
::v-deep .list-container {
padding: 0;
}
::v-deep .list-scroll {
margin-top: 0rpx;
}
}
.service-list {
background-color: #f5f5f5;
border-radius: 20rpx 20rpx 0 0;
}
.nav-img-size::v-deep .icon-headle {
width: 36rpx !important;
height: 36rpx !important;
}
</style>

View File

@ -0,0 +1,579 @@
<template>
<view class="skill-market-page">
<custom-navbar title="技能集市" :showBack="true" backgroundColor="#FFFFFF" :show-headle="true" borderBottom="none"
headleSrc="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/51ebca5d-95c5-4fb8-8aa3-560a79100d25.png"
@onHeadleClick="goToSearch" class="nav-img-size">
</custom-navbar>
<view class="sticky-header-container" :style="{ top: navHeight + 'px' }">
<view class="official-author-block" v-if="authorInfo.show">
<view class="author-left">
<image class="author-avatar" :src="authorInfo.avatar" mode="aspectFill"></image>
<view class="author-info">
<text class="author-name">{{ authorInfo.name }}</text>
<text class="author-desc">发布了{{ authorInfo.videoCount }}条技能</text>
</view>
</view>
<view class="view-more-btn" v-if="!authorInfo.isOfficial" @click="goToBrandDetail">
<text>查看更多</text>
<view class="arrow-right"></view>
</view>
</view>
<view class="sort-bar-wrap">
<view class="sort-bar">
<view class="left-sorts">
<view class="sort-item" @tap="toggleSort('time')">
<text class="sort-text" :class="{ active: orderType === 1 || orderType === 2 }">发布时间</text>
<view class="arrows">
<image class="arrow-icon" :src="orderType === 2 ? iconPinkUp : iconGreyUp"></image>
<image class="arrow-icon" :src="orderType === 1 ? iconPinkDown : iconGreyDown"></image>
</view>
</view>
<view class="sort-item" @tap="toggleSort('view')">
<text class="sort-text" :class="{ active: orderType === 3 || orderType === 4 }">浏览量</text>
<view class="arrows">
<image class="arrow-icon" :src="orderType === 4 ? iconPinkUp : iconGreyUp"></image>
<image class="arrow-icon" :src="orderType === 3 ? iconPinkDown : iconGreyDown"></image>
</view>
</view>
</view>
<view class="right-filter" @tap="toggleFilterPanel" v-if="!authorInfo.show">
<text class="filter-text">筛选</text>
<image class="filter-icon" :src="filterIcon" :class="{ 'rotate': filterVisible }"></image>
</view>
</view>
<view class="filter-panel" :class="{ 'show': filterVisible }" catchtouchmove="true">
<view class="filter-mask" @tap="closeFilterPanel"></view>
<view class="filter-container">
<text class="group-title">课程类型</text>
<view class="course-filter">
<view class="course-type-item" :class="{ active: linkType === '' }" @tap="selectAndApply('')">全部</view>
<view class="course-type-item" :class="{ active: linkType === 1 }" @tap="selectAndApply(1)">平台课程</view>
<view class="course-type-item" :class="{ active: linkType === 2 }" @tap="selectAndApply(2)">品牌课程</view>
</view>
</view>
</view>
</view>
</view>
<view class="list-container">
<view v-if="skillList.length > 0">
<skill-card v-for="(item, index) in skillList" :key="item.id || index" :item="item"
@click="goToDetail(item)"></skill-card>
</view>
<view v-else>
<noData></noData>
</view>
</view>
<view style="height: 60rpx;"></view>
</view>
</template>
<script>
import request from "@/utils/request"
import SkillCard from "@/pages/home/components/skill-card.vue"
import noData from "@/components/noData/noData.vue";
const USE_REAL_API = true
export default {
components: { SkillCard, noData },
data() {
return {
navHeight: 0,
orderType: 0,
linkType: '',
// //
authorInfo: {
show: false, //
isOfficial: false, //
brandId: null, // ID
name: '', // /
avatar: '', //
videoCount: 0 //
},
filterVisible: false,
skillList: [],
page: 1,
limit: 10,
isFinished: false,
iconGreyUp: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e00ab2ac-bccf-42d8-820c-65727327e279.png',
iconGreyDown: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/dfffb601-6877-4608-a3eb-88695b05c6c2.png',
iconPinkUp: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/5bc84dff-e120-4d4b-a33e-25d68e065654.png',
iconPinkDown: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/ff2c2c9d-694a-4adf-b737-66b07f4a63ae.png',
mockDatabase: []
}
},
computed: {
filterIcon() {
return this.filterVisible
? 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e00ab2ac-bccf-42d8-820c-65727327e279.png'
: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/bfe7a104-bef8-4432-9487-42f05e3e9035.png'
}
},
//
onLoad(options) {
if (options && options.showAuthor === '1') {
this.authorInfo.show = true;
this.authorInfo.isOfficial = options.isOfficial === '1';
this.authorInfo.brandId = options.linkId || null;
// decodeURIComponent
this.authorInfo.name = options.authorName ? decodeURIComponent(options.authorName) : '未知作者';
this.authorInfo.avatar = options.authorAvatar ? decodeURIComponent(options.authorAvatar) : 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/bbe832bf-aae6-4579-b3cc-246d252488c3';
this.authorInfo.videoCount = options.videoCount || 0;
//
this.linkType = this.authorInfo.isOfficial ? 1 : 2;
} else if (options && options.linkType !== undefined) {
this.linkType = Number(options.linkType);
}
const sysInfo = uni.getSystemInfoSync()
const sbh = sysInfo.statusBarHeight || 0
this.navHeight = sbh
this.initMockData()
this.fetchData(true)
},
onReachBottom() {
if (!this.isFinished) {
this.fetchData(false)
}
},
methods: {
goToSearch() {
uni.navigateTo({
url: '/pages/search_common/search?type=skill'
});
},
//
goToBrandDetail() {
if (this.authorInfo.brandId) {
uni.navigateTo({
url: `/pages/jingxuan/brand-detail?id=${this.authorInfo.brandId}`
});
} else {
uni.showToast({ title: '缺少品牌信息', icon: 'none' });
}
},
toggleSort(type) {
if (type === 'time') {
if (this.orderType === 1) this.orderType = 2
else if (this.orderType === 2) this.orderType = 0
else this.orderType = 1
} else if (type === 'view') {
if (this.orderType === 3) this.orderType = 4
else if (this.orderType === 4) this.orderType = 0
else this.orderType = 3
}
this.fetchData(true)
},
toggleFilterPanel() {
this.filterVisible = !this.filterVisible
},
closeFilterPanel() {
this.filterVisible = false
},
selectAndApply(typeVal) {
this.linkType = typeVal;
//
this.authorInfo.show = false;
this.closeFilterPanel();
this.fetchData(true);
},
async fetchData(isReset = false) {
if (isReset) {
this.page = 1
this.isFinished = false
this.skillList = []
}
if (USE_REAL_API) {
uni.showLoading({ title: '加载中...' })
try {
const params = {
page: this.page,
limit: this.limit
}
if (this.orderType !== 0) params.order_type = this.orderType;
if (this.linkType !== '') params.link_type = this.linkType;
// link_id
if (this.authorInfo.show && !this.authorInfo.isOfficial && this.authorInfo.brandId) {
params.link_id = this.authorInfo.brandId;
}
const res = await request.post('/sj/skill/list', params)
if ((res.code === 200 || res.state === 1) && res.data && res.data.list) {
const list = res.data.list.map(item => this.formatApiData(item))
if (list.length < this.limit) this.isFinished = true
this.skillList = isReset ? list : this.skillList.concat(list)
this.page++
} else {
this.isFinished = true
}
} catch (e) {
uni.showToast({ title: '加载失败', icon: 'none' })
} finally {
uni.hideLoading()
}
} else {
uni.showLoading({ title: '模拟加载中...' })
setTimeout(() => {
let list = [...this.mockDatabase]
if (this.linkType !== '') {
list = list.filter(item => item.link_type === this.linkType)
}
//
if (this.authorInfo.show && !this.authorInfo.isOfficial && this.authorInfo.brandId) {
list = list.filter(item => item.brand_id == this.authorInfo.brandId)
}
if (this.orderType !== 0) {
list.sort((a, b) => {
if (this.orderType === 1) return b.timestamp - a.timestamp
if (this.orderType === 2) return a.timestamp - b.timestamp
if (this.orderType === 3) return b.views - a.views
if (this.orderType === 4) return a.views - b.views
return 0
})
}
const start = (this.page - 1) * this.limit
const end = start + this.limit
const pageData = list.slice(start, end)
if (pageData.length < this.limit) this.isFinished = true
this.skillList = isReset ? pageData : this.skillList.concat(pageData)
this.page++
uni.hideLoading()
}, 500)
}
},
formatApiData(item) {
return {
id: item.id,
title: item.name,
coverSrc: item.cover_img || 'https://dummyimage.com/300x200/ccc/fff.png',
type: item.link_name || (item.link_type === 1 ? '平台课程' : '品牌课程'),
link_type: item.link_type,
views: item.browse_num || 0,
time: item.create_time ? this.formatTimeAgo(item.create_time) : '',
typeIcon: item.link_logo || ''
}
},
formatTimeAgo(timeStr) {
const now = new Date()
const time = new Date(timeStr.replace(/-/g, '/'))
const diff = Math.floor((now - time) / 1000)
if (diff < 3600) return Math.floor(diff / 60) + '分钟前'
if (diff < 86400) return Math.floor(diff / 3600) + '小时前'
if (diff < 604800) return Math.floor(diff / 86400) + '天前'
return timeStr.split(' ')[0]
},
goToDetail(item) {
if (item.id) {
uni.navigateTo({ url: `/pages/skill/detail?id=${item.id}` })
} else {
uni.showToast({ title: '详情页开发中', icon: 'none' })
}
},
initMockData() {
this.mockDatabase = [
{ id: 1, title: '穿戴甲的操作视频扫码验券操作教程', coverSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg', type: '平台课程', link_type: 1, views: 836000, time: '7小时前', timestamp: Date.now() - 7 * 3600000 },
{ id: 2, title: '脸部按摩操作步骤详情', coverSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg', type: '品牌课程', link_type: 2, brand_id: 101, views: 8334, time: '1天前', timestamp: Date.now() - 24 * 3600000 },
{ id: 3, title: '全身SPA的操作视频操作教程', coverSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg', type: '平台课程', link_type: 1, views: 2344, time: '3天前', timestamp: Date.now() - 3 * 24 * 3600000 },
{ id: 4, title: '高级面部护理手法详解最新版', coverSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg', type: '平台课程', link_type: 1, views: 836, time: '4月26日', timestamp: Date.now() - 30 * 24 * 3600000 },
{ id: 5, title: '光疗美甲进阶全方位解析', coverSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg', type: '品牌课程', link_type: 2, brand_id: 101, views: 12000, time: '2小时前', timestamp: Date.now() - 2 * 3600000 }
]
}
}
}
</script>
<style lang="scss" scoped>
.skill-market-page {
min-height: 100vh;
background: #fff;
}
/* 统一吸顶容器 */
.sticky-header-container {
position: sticky;
background: #fff;
z-index: 101;
/* 确保整体吸顶时下方有稍微一点阴影或边框分割更好看,可根据 UI 需求开启 */
// border-bottom: 1rpx solid #F5F5F5;
}
/* 官方/品牌 作者展示块样式 */
.official-author-block {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 30rpx;
background: #fff;
border-bottom: 10rpx solid #F5F5F5;
.author-left {
display: flex;
align-items: center;
.author-avatar {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.author-info {
display: flex;
flex-direction: column;
.author-name {
font-weight: 500;
font-size: 28rpx;
color: #333333;
line-height: 40rpx;
margin-bottom: 4rpx;
}
.author-desc {
font-weight: 400;
font-size: 20rpx;
color: #999999;
line-height: 28rpx;
}
}
}
.view-more-btn {
display: flex;
align-items: center;
font-size: 24rpx;
color: #FF4767;
border: 2rpx solid #FF4767;
padding: 8rpx 20rpx;
border-radius: 45rpx;
.arrow-right {
width: 10rpx;
height: 10rpx;
border-top: 2rpx solid #FF4767;
border-right: 2rpx solid #FF4767;
transform: rotate(45deg);
margin-left: 6rpx;
}
}
}
.sort-bar-wrap {
/* 移除单独的 sticky由外层 container 统一控制 */
background: #fff;
position: relative;
}
.sort-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 30rpx;
.left-sorts {
display: flex;
align-items: center;
gap: 100rpx;
.sort-item {
display: flex;
align-items: center;
.sort-text {
font-size: 28rpx;
color: #333333;
margin-right: 6rpx;
font-weight: 400;
&.active {
color: #FF4767;
font-weight: 500;
}
}
.arrows {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.arrow-icon {
width: 14rpx;
height: 8rpx;
margin: 2rpx 0;
}
}
}
}
.right-filter {
display: flex;
align-items: center;
.filter-text {
font-size: 26rpx;
color: #666666;
margin-right: 8rpx;
}
.filter-icon {
width: 24rpx;
height: 24rpx;
transition: transform 0.3s ease;
&.rotate {
transform: rotate(180deg);
width: 18rpx;
height: 12rpx;
}
}
}
}
.list-container {
padding: 0 24rpx 30rpx;
}
.filter-panel {
position: absolute;
top: 100%;
left: 0;
right: 0;
height: 100vh;
z-index: 100;
pointer-events: none;
overflow: hidden;
&.show {
pointer-events: auto;
.filter-mask {
opacity: 1;
}
.filter-container {
transform: translateY(0);
}
}
.filter-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
.filter-container {
position: absolute;
top: 0;
left: 0;
right: 0;
background: #fff;
border-radius: 0 0 24rpx 24rpx;
padding: 30rpx 30rpx 40rpx;
display: flex;
flex-direction: column;
transform: translateY(-100%);
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}
}
.group-title {
font-weight: 500;
font-size: 28rpx;
color: #333333;
margin-bottom: 24rpx;
display: block;
}
.course-filter {
display: flex;
gap: 20rpx;
.course-type-item {
background: #F5F5F5;
color: #333333;
font-size: 26rpx;
padding: 12rpx 36rpx;
border-radius: 12rpx;
border: 1rpx solid transparent;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
&.active {
background: #FFF0F2;
color: #FF4767;
border-color: #FFB0BD;
}
}
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
padding: 120rpx 0;
.empty-icon {
width: 346rpx;
height: 373rpx;
}
}
::v-deep .icon-headle {
width: 32rpx !important;
height: 32rpx !important;
}
::v-deep .back-icon {
width: 28rpx !important;
height: 36rpx !important;
}
.nav-img-size::v-deep .icon-headle {
width: 36rpx !important;
height: 36rpx !important;
}
</style>

View File

@ -0,0 +1,768 @@
<template>
<view class="skill-detail-page">
<custom-navbar title="技能集市" backgroundColor="#ffffff" titleColor="#333" borderBottom="none">
</custom-navbar>
<!-- <view class="mock-switch" @click="toggleMockData">
<text>{{ useMockData ? '关闭模拟数据' : '开启模拟数据' }}</text>
</view> -->
<view class="video-section" v-if="article.videoSrc">
<view class="video-wrapper">
<video v-if="!tipShow" id="customVideo" class="main-video" :src="article.videoSrc" :poster="article.poster"
:controls="showNativeControls" :show-center-play-btn="false" :muted="isMuted" object-fit="cover"
@timeupdate="onTimeUpdate" @loadedmetadata="onLoadedMetaData" @ended="onEnded" @play="onPlay" @pause="onPause"
@fullscreenchange="onFullScreenChange">
<cover-image v-if="!isPlaying && !showNativeControls" class="center-play-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/bb720bb0-4ada-49f6-9ce5-38abfd613d5d.png"
@click.stop="togglePlay"></cover-image>
<cover-view class="custom-control-bar" v-if="!showNativeControls">
<cover-image class="bar-bg"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/ce7f28f8-470d-4f5b-9c7e-ff2bf8635226.png"></cover-image>
<cover-view class="bar-content">
<cover-image v-if="isPlaying" class="ctrl-icon" :src="iconPause" @click.stop="togglePlay"></cover-image>
<cover-image v-if="!isPlaying" class="ctrl-icon" :src="iconPlay" @click.stop="togglePlay"></cover-image>
<cover-view class="time-text">{{ formatTime(currentTime) }}</cover-view>
<cover-view class="slider-track">
<cover-view class="slider-bg"></cover-view>
<cover-view class="slider-active" :style="activeStyle"></cover-view>
<cover-view class="slider-thumb" v-if="currentTime > 0" :style="thumbStyle"></cover-view>
</cover-view>
<cover-view class="time-text">{{ formatTime(duration) }}</cover-view>
<cover-image v-if="!isMuted" class="ctrl-icon mute-icon" :src="iconSoundOn"
@click.stop="toggleMute"></cover-image>
<cover-image v-if="isMuted" class="ctrl-icon mute-icon" :src="iconMute"
@click.stop="toggleMute"></cover-image>
<cover-image class="ctrl-icon fullscreen-icon" :src="iconFullscreen"
@click.stop="toggleFullScreen"></cover-image>
</cover-view>
</cover-view>
</video>
<image v-else class="main-video fallback-poster" :src="article.poster || article.videoSrc" mode="aspectFill">
</image>
</view>
</view>
<view class="article-header" v-if="article.title">
<view class="title">{{ article.title }}</view>
<view class="meta-row">
<view class="author-info" @click="goToAuthorMarket">
<image class="avatar" :src="article.authorAvatar" mode="aspectFill"></image>
<text class="author-name">{{ article.authorName }}</text>
<text class="date">{{ article.date }}</text>
</view>
<view class="favorite-btn" @click="toggleFavorite">
<image class="star-icon" :src="isFavorite ? filledStarUrl : emptyStarUrl" />
<text :class="{ 'active-fav': isFavorite }">{{ isFavorite ? '已收藏' : '收藏' }}</text>
</view>
</view>
</view>
<view class="article-content-wrapper">
<view class="article-content">
<rich-text :nodes="formattedHtmlContent"></rich-text>
</view>
</view>
<view class="recommend-section" v-if="recommendList.length > 0">
<view class="section-header">
<text class="section-title">相关推荐</text>
<view class="refresh-btn" @click="refreshList">
<image class="refresh-icon" :class="{ 'is-rotating': isRefreshing }"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/f2152145-df2a-49ab-87f5-f66b3207d0b0.png">
</image>
<text>换一批</text>
</view>
</view>
<view class="list-container">
<skill-card v-for="(item, index) in recommendList" :key="index" :item="item"></skill-card>
</view>
</view>
<tipsPopup2 :show="tipShow" @closePopup="closePopup" sureText="确认" @okBtn="okBtn">
{{ tipsText }}
</tipsPopup2>
</view>
</template>
<script>
import request from "@/utils/request";
import SkillCard from "@/pages/home/components/skill-card.vue";
import tipsPopup2 from "@/components/tips-popup/tips-popup2";
export default {
components: {
SkillCard,
tipsPopup2
},
data() {
return {
useMockData: false,
articleId: null,
isFavorite: false,
emptyStarUrl: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/53378402-5456-4a80-b2ec-7c18d43d970e.png',
filledStarUrl: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/10461305-6bf8-4016-ae76-3415e055ec63.png',
isRefreshing: false,
isLogin: false,
tipShow: false,
tipsText: "",
tipType: "",
videoContext: null,
isPlaying: false,
isMuted: false,
showNativeControls: false,
currentTime: 0,
duration: 0,
sliderValue: 0,
iconPlay: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/88ca4fe3-645c-48a9-abda-1f9b45714970.png',
iconPause: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/cf9d6dd6-e9df-4c46-bafa-e1a72f7ddbcd.png',
iconSoundOn: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/9f13a630-7efd-4c61-b6fe-b6b282e28e42.png',
iconMute: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/80f856c1-3d66-4b11-b21a-8eb6a3272a3c.png',
iconFullscreen: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/4286ef33-6849-4851-8866-4230d2523a33.png',
article: {},
recommendList: [],
recommendPage: 1
}
},
computed: {
formattedHtmlContent() {
if (!this.article.htmlContent) return '';
let html = this.article.htmlContent;
html = html.replace(/<img/gi, '<img style="max-width:100%; height:auto; display:block; border-radius:10px; margin: 20rpx 0;"');
html = html.replace(/<p/gi, '<p style="margin-bottom: 24rpx; line-height: 40rpx;"');
return html;
},
activeStyle() {
return `width: ${this.sliderValue}%;`;
},
thumbStyle() {
return `left: ${this.sliderValue}%;`;
}
},
onLoad(options) {
if (options && options.id) {
this.articleId = parseInt(options.id, 10);
} else {
this.articleId = 1;
}
this.getPageData();
},
onShow() {
this.isLogin = uni.getStorageSync("accessToken") ? true : false;
},
methods: {
//
goToAuthorMarket() {
const isOfficial = this.article.linkType === 1 ? '1' : '0';
const linkId = this.article.linkId || '';
const name = encodeURIComponent(this.article.authorName || '');
const avatar = encodeURIComponent(this.article.authorAvatar || '');
const count = this.article.authorVideoCount || 0;
uni.navigateTo({
url: `/pages/jingxuan/selected-skills?showAuthor=1&isOfficial=${isOfficial}&linkId=${linkId}&authorName=${name}&authorAvatar=${avatar}&videoCount=${count}`
});
},
getVideoContext() {
if (!this.videoContext) {
this.videoContext = uni.createVideoContext('customVideo', this);
}
return this.videoContext;
},
closePopup() {
this.tipShow = false;
},
okBtn() {
if (this.tipType == "login") {
uni.navigateTo({
url: "/pages/blogPopup/blogPopup",
});
}
this.tipShow = false;
},
formatTime(sec) {
if (!sec) return '00:00';
sec = Math.round(sec);
let m = Math.floor(sec / 60);
let s = sec % 60;
return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
},
onLoadedMetaData(e) {
this.duration = e.detail.duration;
},
onTimeUpdate(e) {
if (e.detail.duration && this.duration === 0) {
this.duration = e.detail.duration;
}
this.currentTime = e.detail.currentTime;
if (this.duration > 0) {
this.sliderValue = Number(((this.currentTime / this.duration) * 100).toFixed(2));
}
},
onEnded() {
this.isPlaying = false;
this.currentTime = 0;
this.sliderValue = 0;
if (this.showNativeControls) {
const ctx = this.getVideoContext();
if (ctx) ctx.exitFullScreen();
}
},
onPlay() { this.isPlaying = true; },
onPause() { this.isPlaying = false; },
onFullScreenChange(e) {
const isFull = e.detail.fullScreen;
if (!isFull) {
this.showNativeControls = false;
} else {
this.showNativeControls = true;
}
},
togglePlay() {
const ctx = this.getVideoContext();
if (!ctx) return;
if (this.isPlaying) {
ctx.pause();
} else {
ctx.play();
}
},
toggleMute() {
this.isMuted = !this.isMuted;
},
toggleFullScreen() {
const ctx = this.getVideoContext();
if (!ctx) return;
this.showNativeControls = true;
this.$nextTick(() => {
ctx.requestFullScreen({ direction: 90 });
});
},
toggleMockData() {
this.useMockData = !this.useMockData;
uni.showToast({ title: this.useMockData ? 'Mock已开启' : 'Mock已关闭', icon: 'none' });
this.getPageData();
},
getPageData() {
if (this.useMockData) {
this.injectMockData();
return;
}
uni.showLoading({ title: '加载中...' });
request.post("/sj/skill/details", {
id: this.articleId
}).then((res) => {
if (res.code == 200 && res.data) {
const data = res.data;
this.article = {
title: data.name || '暂无标题',
// 使
linkType: data.link_type || 1, // 1= 2=
linkId: data.link_id || 0,
authorName: data.link_name || '美融融平台',
authorAvatar: data.link_logo || 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/bbe832bf-aae6-4579-b3cc-246d252488c3',
authorVideoCount: data.link_publish_num || 0, //
date: data.create_time ? data.create_time.substring(0, 10).replace(/-/g, '.') : '',
videoSrc: data.video || '',
poster: data.cover_img || '',
htmlContent: data.detail || ''
};
this.isFavorite = data.collect_state === 1;
request.post("/sj/skill/list", {
page: 1,
limit: 3,
skill_id: data.id
}).then((listRes) => {
if (listRes.code == 200 && listRes.data && listRes.data.list) {
// 使 map
this.recommendList = listRes.data.list.map(item => {
return {
id: item.id,
title: item.name || '暂无标题', //
coverSrc: item.cover_img || '', //
type: item.link_name || '平台课程', // /
typeIcon: item.link_logo || '', //
// /
views: item.total_browse_num || item.real_browse_num || item.browse_num || 0,
//
time: item.create_time ? item.create_time.substring(0, 10).replace(/-/g, '-') : ''
};
});
}
}).catch((err) => {
console.error('获取推荐列表异常', err);
});
} else {
uni.showToast({ title: res.msg || '获取数据失败', icon: 'none' });
}
}).catch((err) => {
console.error('获取技能集市详情异常', err);
}).finally(() => {
uni.hideLoading();
});
},
toggleFavorite() {
if (!this.isLogin) {
if (this.isPlaying) {
this.getVideoContext().pause();
}
this.tipsText = "您现在还未登录,是否确定前往登录?";
this.tipShow = true;
this.tipType = "login";
return;
}
if (this.useMockData) {
this.isFavorite = !this.isFavorite;
uni.showToast({ title: this.isFavorite ? '收藏成功' : '已取消收藏', icon: 'none' });
return;
}
uni.showLoading({ title: '处理中' });
const targetState = this.isFavorite ? 2 : 1;
request.post("/sj/skill/collect", {
id: this.articleId,
state: targetState
}).then(res => {
if (res.code == 200) {
this.isFavorite = !this.isFavorite;
uni.showToast({ title: this.isFavorite ? '收藏成功' : '已取消收藏', icon: 'none' });
} else {
uni.showToast({ title: res.msg || '操作失败', icon: 'none' });
}
}).catch(err => {
uni.showToast({ title: '网络异常', icon: 'none' });
}).finally(() => {
uni.hideLoading();
});
},
refreshList() {
if (this.isRefreshing) return;
this.isRefreshing = true;
uni.showLoading({ title: '加载中' });
// 101+1
if (this.recommendPage >= 10) {
this.recommendPage = 1;
} else {
this.recommendPage++;
}
request.post("/sj/skill/list", {
page: this.recommendPage,
limit: 3,
skill_id: this.article.id
}).then((res) => {
if (res.code == 200 && res.data && res.data.list && res.data.list.length > 0) {
this.recommendList = res.data.list.map(item => ({
id: item.id,
title: item.name || '暂无标题',
coverSrc: item.cover_img || '',
type: item.link_name || '平台课程',
views: item.browse_num || 0,
time: item.create_time ? item.create_time.split(' ')[0] : '',
typeIcon: item.link_logo || '',
video: item.video || ''
}));
} else {
// 1
this.recommendPage = 1;
request.post("/sj/skill/list", {
page: 1,
limit: 3,
skill_id: this.article.id
}).then((res2) => {
if (res2.code == 200 && res2.data && res2.data.list) {
this.recommendList = res2.data.list.map(item => ({
id: item.id,
title: item.name || '暂无标题',
coverSrc: item.cover_img || '',
type: item.link_name || '平台课程',
views: item.browse_num || 0,
time: item.create_time ? item.create_time.split(' ')[0] : '',
typeIcon: item.link_logo || '',
video: item.video || ''
}));
}
});
}
this.isRefreshing = false;
uni.hideLoading();
}).catch(() => {
this.isRefreshing = false;
uni.hideLoading();
});
},
injectMockData() {
this.isFavorite = false;
this.article = {
title: '穿戴甲的操作视频扫码验券操作',
// Mock(linkType2)
linkType: 2,
linkId: 101,
authorName: '蜜丝卡伦官方品牌',
authorAvatar: 'https://images.unsplash.com/photo-1512290923902-8a9f81dc236c?ixlib=rb-4.0.3&auto=format&fit=crop&w=100&q=80',
authorVideoCount: 658,
date: '2026.03.03',
videoSrc: 'https://vjs.zencdn.net/v/oceans.mp4',
poster: 'https://images.unsplash.com/photo-1610992015732-2449b76344bc?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
htmlContent: `
<p>最近一向以平价良心宠粉著称的胖东来突然因为一条毛巾被推上风口浪尖</p>
<p>4月下旬抖音博主惊梦人-连续发布多条探店视频称自己跑遍许昌5家胖东来门店发现一边是几块钱的玻璃水洗衣液等亲民好物另一边却摆着标价249元的高端毛巾价格形成反差</p>
<img src="https://dummyimage.com/800x400/ff6b81/ffffff.png&text=占位图" alt="会员权益"/>
`
};
this.loadMockRecommend();
},
loadMockRecommend() {
this.recommendList = [
{
id: 1,
title: '穿戴甲的操作视频扫码验券操作教程...',
coverSrc: 'https://images.unsplash.com/photo-1604654894610-df63bc536371?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
type: '平台课程',
typeIcon: 'https://dummyimage.com/100x100/e6505f/fff.png&text=融',
views: 836000,
time: '7小时前'
},
{
id: 2,
title: '脸部按摩操作步骤详情',
coverSrc: 'https://images.unsplash.com/photo-1512290923902-8a9f81dc236c?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
type: '脸缘品牌',
typeIcon: 'https://dummyimage.com/100x100/e6505f/fff.png&text=缘',
views: 8334,
time: '1天前'
}
];
}
}
}
</script>
<style lang="scss" scoped>
.skill-detail-page {
background-color: #ffffff;
min-height: 100vh;
padding-bottom: 60rpx;
position: relative;
}
.mock-switch {
position: fixed;
right: 20rpx;
top: 200rpx;
background: #E5505F;
color: #fff;
font-size: 24rpx;
padding: 10rpx 20rpx;
border-radius: 30rpx;
z-index: 999;
box-shadow: 0 4rpx 10rpx rgba(229, 80, 95, 0.4);
}
.video-section {
width: 100%;
height: 420rpx;
.video-wrapper {
position: relative;
width: 100%;
height: 100%;
.main-video {
width: 100%;
height: 100%;
position: relative;
}
.fallback-poster {
z-index: 1;
}
.center-play-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80rpx;
height: 80rpx;
z-index: 99;
}
.custom-control-bar {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 70rpx;
z-index: 99;
.bar-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 70rpx;
z-index: 100;
}
.bar-content {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 70rpx;
z-index: 101;
display: flex;
flex-direction: row;
align-items: center;
padding: 0 10rpx;
box-sizing: border-box;
.ctrl-icon {
width: 25rpx;
height: 30rpx;
margin: 0 10rpx;
flex-shrink: 0;
}
.mute-icon {
width: 38rpx;
height: 30rpx;
flex-shrink: 0;
}
.fullscreen-icon {
width: 30rpx;
height: 30rpx;
flex-shrink: 0;
}
.time-text {
font-size: 22rpx;
color: #ffffff;
line-height: 70rpx;
}
.slider-track {
flex: 1;
height: 70rpx;
margin: 0 20rpx;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
.slider-bg {
position: absolute;
top: 33rpx;
left: 0;
width: 100%;
height: 4rpx;
background-color: rgba(255, 255, 255, 0.4);
}
.slider-active {
position: absolute;
top: 33rpx;
left: 0;
height: 4rpx;
background-color: #FF4767;
}
.slider-thumb {
position: absolute;
top: 25rpx;
margin-left: -10rpx;
width: 20rpx;
height: 20rpx;
border-radius: 50%;
background-color: #FF4767;
}
}
}
}
}
}
.article-header {
padding: 30rpx 30rpx 20rpx;
.title {
font-weight: 500;
font-size: 32rpx;
color: #333333;
line-height: 45rpx;
margin-bottom: 24rpx;
}
.meta-row {
display: flex;
justify-content: space-between;
align-items: center;
.author-info {
display: flex;
align-items: center;
.avatar {
width: 28rpx;
height: 28rpx;
border-radius: 50%;
margin-right: 12rpx;
}
.author-name {
font-weight: 400;
font-size: 24rpx;
color: #666666;
line-height: 33rpx;
margin-right: 16rpx;
}
.date {
font-weight: 400;
font-size: 24rpx;
color: #999999;
line-height: 33rpx;
}
}
.favorite-btn {
display: flex;
align-items: center;
font-weight: 400;
font-size: 24rpx;
color: #999999;
line-height: 33rpx;
.star-icon {
width: 26rpx;
height: 26rpx;
margin-right: 6rpx;
margin-top: -2rpx;
}
.active-fav {
color: #FD5A8C;
}
}
}
}
.article-content-wrapper {
.article-content {
padding: 0 30rpx 40rpx;
font-weight: 400;
font-size: 28rpx;
color: #2A2A2A;
line-height: 40rpx;
::v-deep p {
margin-bottom: 24rpx;
line-height: 40rpx;
}
::v-deep img {
max-width: 100%;
height: auto;
display: block;
border-radius: 10rpx;
margin: 20rpx 0;
}
}
.empty-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 50rpx 0 80rpx;
.empty-icon {
width: 140rpx;
height: 140rpx;
margin-bottom: 24rpx;
opacity: 0.8;
}
.empty-text {
font-size: 26rpx;
color: #A9A9A9;
}
}
}
.recommend-section {
padding: 0 30rpx;
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
.section-title {
font-weight: 500;
font-size: 30rpx;
color: #333333;
line-height: 42rpx;
}
.refresh-btn {
display: flex;
align-items: center;
font-size: 26rpx;
color: #999999;
.refresh-icon {
width: 24rpx;
height: 28rpx;
margin-right: 8rpx;
transition: transform 0.5s ease;
&.is-rotating {
transform: rotate(360deg);
}
}
}
}
}
</style>

View File

@ -405,7 +405,7 @@
.login {
width: 630rpx;
height: 96rpx;
background-color: #E8101E;
background-color: #FF4767;
color: #fff;
font-size: 40rpx;
line-height: 96rpx;

View File

@ -18,7 +18,7 @@
</view>
</view>
</view>
<uv-switch v-model="mainSwitch.switch" activeColor="#E8101E" @change="handleSwitchChange('total',mainSwitch, $event)"></uv-switch>
<uv-switch v-model="mainSwitch.switch" activeColor="#FF4767" @change="handleSwitchChange('total',mainSwitch, $event)"></uv-switch>
</view>
</view>
<view class="settings-title" v-if="kindList.length">
@ -34,7 +34,7 @@
</view>
<view class="card-tip">{{ item.text }}</view>
</view>
<uv-switch v-model="item.switch" activeColor="#E8101E" @change="handleSwitchChange('branch',item, $event)"></uv-switch>
<uv-switch v-model="item.switch" activeColor="#FF4767" @change="handleSwitchChange('branch',item, $event)"></uv-switch>
</view>
</view>
@ -267,7 +267,7 @@
}
.color-red {
color: #E8101E !important;
color: #FF4767 !important;
}
/* 弹窗样式 */
@ -324,7 +324,7 @@
.dialog-btn {
width: 476rpx;
height: 86rpx;
background: #E8101E;
background: #FF4767;
border-radius: 44rpx;
display: flex;
align-items: center;

View File

@ -332,7 +332,7 @@
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background-color: #E8101E;
background-color: #FF4767;
margin-left: 8rpx;
}

View File

@ -18,7 +18,7 @@
</view>
</view>
</view>
<uv-switch v-model="mainSwitch.switch" activeColor="#E8101E"
<uv-switch v-model="mainSwitch.switch" activeColor="#FF4767"
@change="handleSwitchChange('total',mainSwitch, $event)"></uv-switch>
</view>
</view>
@ -35,7 +35,7 @@
</view>
<view class="card-tip">{{ item.text }}</view>
</view>
<uv-switch v-model="item.switch" activeColor="#E8101E"
<uv-switch v-model="item.switch" activeColor="#FF4767"
@change="handleSwitchChange('branch',item, $event)"></uv-switch>
</view>
</view>
@ -289,7 +289,7 @@
}
.color-red {
color: #E8101E !important;
color: #FF4767 !important;
}
/* 弹窗样式 */
@ -346,7 +346,7 @@
.dialog-btn {
width: 476rpx;
height: 86rpx;
background: #E8101E;
background: #FF4767;
border-radius: 44rpx;
display: flex;
align-items: center;
@ -437,7 +437,7 @@
width: 300rpx;
height: 80rpx;
line-height: 80rpx;
background: #E8101E;
background: #FF4767;
border-radius: 40rpx;
margin: 0 auto;
}

View File

@ -29,7 +29,7 @@
</view>
<view class="card-tip"> 开启语音提示避免落单</view>
</view>
<uv-switch v-model="value" @change="changeVoice" activeColor="#E8101E"></uv-switch>
<uv-switch v-model="value" @change="changeVoice" activeColor="#FF4767"></uv-switch>
</view>
</view>
@ -292,7 +292,7 @@
}
.color-red {
color: #E8101E !important;
color: #FF4767 !important;
}
//
@ -348,7 +348,7 @@
.dialog-btn {
width: 400rpx;
height: 88rpx;
background: #E8101E;
background: #FF4767;
border-radius: 44rpx;
display: flex;
align-items: center;

File diff suppressed because it is too large Load Diff

670
pages/my/myFavorite.vue Normal file
View File

@ -0,0 +1,670 @@
<template>
<view class="collection-page">
<custom-navbar title="我的收藏" :showBack="true" backgroundColor="#FFFFFF" :show-headle="true" borderBottom="none"
headleSrc="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/b18bac2f-3e4c-4586-9d45-0f088629c9d1.png"
@onHeadleClick="goToSearch" class="serch-tap">
</custom-navbar>
<view class="tab-bar-wrap" :style="{ top: navHeight + 'px' }">
<view class="tab-bar">
<view class="tab-items">
<view class="tab-item" :class="{ active: currentTab === 'brand' }" @tap="switchTab('brand')">
收藏的品牌
</view>
<view class="tab-item" :class="{ active: currentTab === 'course' }" @tap="switchTab('course')">
收藏的课程
</view>
</view>
</view>
</view>
<view class="filter-panel" :class="{ 'show': filterVisible }" :style="{ top: filterTop + 'px' }"
catchtouchmove="true">
<view class="filter-mask" @tap="closeFilterPanel"></view>
<view class="filter-container">
<scroll-view scroll-y class="filter-scroll" v-if="currentTab === 'brand'">
<view class="filter-group" v-for="group in brandFilterData" :key="group.id">
<view class="group-title">{{ group.title }}</view>
<view class="pill-list">
<view class="pill-item" v-for="sub in group.children" :key="sub.id"
:class="{ active: selectedSecondIds.includes(sub.id) }" @tap="toggleSecondSelect(sub.id)">
{{ sub.title }}
</view>
</view>
</view>
</scroll-view>
<view v-else class="course-filter">
<view class="course-type-item" :class="{ active: selectedCourseType === 'platform' }"
@tap="selectCourseType('platform')">
平台课程
</view>
<view class="course-type-item" :class="{ active: selectedCourseType === 'brand' }"
@tap="selectCourseType('brand')">
品牌课程
</view>
</view>
<view class="filter-footer">
<view class="reset-btn" @tap="resetFilter">重置</view>
<view class="confirm-btn" @tap="applyFilterAndClose">确定</view>
</view>
</view>
</view>
<view class="collection-content" v-if="currentTab === 'brand'">
<view class="brand-grid" v-if="filteredBrandList.length > 0">
<brand-card class="brand-card-wrap" v-for="(item, index) in filteredBrandList" :key="index" :item="item"
:tagKeys="item.tagKeys" @click="goToBrandDetail(item)"></brand-card>
</view>
<view class="empty-state" v-else>
<image class="empty-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/da814ede-1adc-4f90-8cdf-5357a12fab27.png">
</image>
</view>
</view>
<view class="collection-content" v-else>
<view class="course-list" v-if="filteredCourseList.length > 0">
<skill-card v-for="(item, index) in filteredCourseList" :key="index" :item="item"></skill-card>
</view>
<view class="empty-state" v-else>
<image class="empty-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/da814ede-1adc-4f90-8cdf-5357a12fab27.png">
</image>
</view>
</view>
<view style="height: 120rpx;"></view>
</view>
</template>
<script>
import BrandCard from "@/pages/home/components/brand-card.vue"
import SkillCard from "@/pages/home/components/skill-card.vue"
import request from "@/utils/request"
// true=false=使
const USE_REAL_API = true
export default {
components: {
BrandCard,
SkillCard
},
data() {
return {
navHeight: 0, //
filterTop: 0, //
currentTab: 'brand',
//
filterVisible: false,
// ()
brandFilterData: [],
selectedSecondIds: [], // ID
//
selectedCourseType: '', // 'platform' 'brand'
//
rawBrandList: [],
rawCourseList: [],
//
filteredBrandList: [],
filteredCourseList: []
}
},
computed: {
filterIcon() {
return this.filterVisible
? 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e00ab2ac-bccf-42d8-820c-65727327e279.png'
: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/bfe7a104-bef8-4432-9487-42f05e3e9035.png'
}
},
onLoad() {
// Tab
const sysInfo = uni.getSystemInfoSync()
const sbh = sysInfo.statusBarHeight || 0
this.navHeight = sbh
// Tab 106rpx px
this.filterTop = this.navHeight + uni.upx2px(106)
this.loadFilterCategories()
this.loadData()
},
methods: {
//
goToSearch() {
// this.currentTab 'brand' 'course'
uni.navigateTo({
url: `/pages/search_common/search?type=collection_${this.currentTab}`
});
},
goBack() {
uni.navigateBack()
},
addAddress() {
//
},
switchTab(tab) {
if (this.currentTab === tab) return
this.currentTab = tab
// tab
if (tab === 'brand') {
this.resetBrandFilter()
} else {
this.resetCourseFilter()
}
this.loadData()
this.closeFilterPanel()
},
//
toggleFilterPanel() {
this.filterVisible = !this.filterVisible
},
closeFilterPanel() {
this.filterVisible = false
},
// state === 1
async loadFilterCategories() {
try {
const res = await request.post('/sj/firstclass', {})
if ((res.code === 200 || res.state === 1) && res.data) {
const firstClasses = res.data;
//
const promises = firstClasses.map(async (first) => {
try {
const subRes = await request.post('/sj/secondclass', { id: first.id })
return {
id: first.id,
title: first.title,
children: ((subRes.code === 200 || subRes.state === 1) && subRes.data) ? subRes.data : []
}
} catch (e) {
return { id: first.id, title: first.title, children: [] }
}
});
const filterData = await Promise.all(promises);
//
this.brandFilterData = filterData.filter(group => group.children.length > 0);
} else {
this.brandFilterData = []
}
} catch (error) {
console.error('获取分类字典失败', error)
}
},
toggleSecondSelect(secId) {
const index = this.selectedSecondIds.indexOf(secId)
if (index === -1) {
this.selectedSecondIds.push(secId)
} else {
this.selectedSecondIds.splice(index, 1)
}
},
//
selectCourseType(type) {
//
if (this.selectedCourseType === type) {
this.selectedCourseType = ''
} else {
this.selectedCourseType = type
}
},
//
resetBrandFilter() {
this.selectedSecondIds = []
this.applyBrandFilter()
},
resetCourseFilter() {
this.selectedCourseType = ''
this.applyCourseFilter()
},
resetFilter() {
if (this.currentTab === 'brand') {
this.resetBrandFilter()
} else {
this.resetCourseFilter()
}
},
applyFilterAndClose() {
if (this.currentTab === 'brand') {
this.applyBrandFilter()
} else {
this.applyCourseFilter()
}
this.closeFilterPanel()
},
// ID
applyBrandFilter() {
if (!this.rawBrandList.length) {
this.filteredBrandList = []
return
}
if (this.selectedSecondIds.length === 0) {
this.filteredBrandList = [...this.rawBrandList]
} else {
this.filteredBrandList = this.rawBrandList.filter(brand => {
return brand.secondClassIds && brand.secondClassIds.some(id => this.selectedSecondIds.includes(id))
})
}
},
//
applyCourseFilter() {
if (!this.rawCourseList.length) {
this.filteredCourseList = []
return
}
if (!this.selectedCourseType) {
this.filteredCourseList = [...this.rawCourseList]
} else {
this.filteredCourseList = this.rawCourseList.filter(course => {
if (this.selectedCourseType === 'platform') return course.type === '平台课程'
if (this.selectedCourseType === 'brand') return course.type === '品牌课程'
return true
})
}
},
// Tab
loadData() {
if (this.currentTab === 'brand') {
this.loadBrandList()
} else {
this.loadCourseList()
}
},
async loadBrandList() {
if (USE_REAL_API) {
await this.fetchRealBrandList()
} else {
this.mockBrandList()
}
this.applyBrandFilter()
},
async loadCourseList() {
if (USE_REAL_API) {
await this.fetchRealCourseList()
} else {
this.mockCourseList()
}
this.applyCourseFilter()
},
// ============ ============
async fetchRealBrandList() {
try {
// 1.
const res = await request.post('/sj/brand/collectList', { page: 1, limit: 100 })
if ((res.code === 200 || res.state === 1) && res.data && res.data.list) {
// 2. filter(item => item.collect_state === 1)
this.rawBrandList = res.data.list.map(item => {
let tags = []
if (item.franchise_state == 1) tags.push('franchise')
if (item.student_state == 1) tags.push('recruit')
if (tags.length === 0) tags.push('more')
return {
id: item.id,
name: item.name,
bgSrc: item.cover_img || 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/default.jpg',
avatar: item.logo_img || 'https://dummyimage.com/100x100/ccc/fff.png&text=Avatar',
tagKeys: tags,
// 3. split
secondClassIds: item.second_class_ids ? String(item.second_class_ids).split(',').map(Number) : []
}
})
console.log('成功获取品牌收藏列表:', this.rawBrandList)
} else {
this.rawBrandList = []
}
} catch (error) {
console.error('获取品牌收藏失败详情:', error)
}
},
async fetchRealCourseList() {
try {
// 1.
const res = await request.post('/sj/skill/collectList', { page: 1, limit: 100 })
if ((res.code === 200 || res.state === 1) && res.data && res.data.list) {
// 2. filter map
this.rawCourseList = res.data.list.map(item => {
return {
id: item.id,
title: item.name,
coverSrc: item.cover_img,
type: item.link_name || (item.link_type == 1 ? '平台课程' : '品牌课程'),
link_type: item.link_type,
views: item.browse_num || 0,
// 3. 使
time: item.create_time ? this.formatTimeAgo(item.create_time) : '',
typeIcon: item.link_logo || ''
}
})
console.log('成功获取课程收藏列表:', this.rawCourseList)
} else {
this.rawCourseList = []
}
} catch (error) {
console.error('获取课程收藏失败', error)
}
},
formatTimeAgo(timeStr) {
if (!timeStr) return '';
const timeStrSafe = String(timeStr);
const now = new Date()
const time = new Date(timeStrSafe.replace(/-/g, '/'))
const diff = Math.floor((now - time) / 1000)
if (diff < 3600) return Math.floor(diff / 60) + '分钟前'
if (diff < 86400) return Math.floor(diff / 3600) + '小时前'
if (diff < 604800) return Math.floor(diff / 86400) + '天前'
return timeStrSafe.split(' ')[0]
},
goToBrandDetail(item) {
if (item.id) {
uni.navigateTo({ url: `/pages/brand/detail?id=${item.id}` })
}
},
// ============ ============
mockBrandList() {
this.rawBrandList = [
{
id: 1, name: '脸缘品牌', bgSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg',
avatar: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg',
tagKeys: ['franchise', 'recruit'], secondClassIds: [76, 77]
},
{
id: 2, name: '美肌工坊', bgSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg',
avatar: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg',
tagKeys: ['franchise'], secondClassIds: [78]
},
{
id: 3, name: '悦容堂', bgSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg',
avatar: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg',
tagKeys: ['recruit'], secondClassIds: [201, 202]
}
]
},
mockCourseList() {
this.rawCourseList = [
{ id: 1, title: '穿戴甲的操作视频扫码验券操作教程', coverSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg', type: '平台课程', views: 836000, time: '7小时前' },
{ id: 2, title: '全身SPA的操作视频操作教程', coverSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg', type: '平台课程', views: 2344, time: '3天前' },
{ id: 3, title: '高级面部护理手法详解', coverSrc: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e2220528-fdf6-4cad-a25d-ec6cbee1501e.jpg', type: '品牌课程', views: 56700, time: '1周前' }
]
}
}
}
</script>
<style lang="scss" scoped>
.collection-page {
min-height: 100vh;
background: #fff;
}
/* 将页面左右 padding 转移到内容区,保证 Tab 背景贯穿全屏 */
.tab-bar-wrap {
position: sticky;
/* 吸顶,保证滑动页面时一直显示在最前面 */
background: #fff;
padding: 10rpx 24rpx;
z-index: 101;
}
.tab-bar {
display: flex;
align-items: center;
justify-content: space-between;
background: transparent;
padding: 10rpx 0;
.tab-items {
display: flex;
align-items: center;
.tab-item {
font-weight: 400;
font-size: 26rpx;
color: #777777;
background: #f5f5f5;
border-radius: 27rpx;
margin-right: 40rpx;
padding: 9rpx 24rpx;
&.active {
color: #FF4767;
background: #FFECEF;
border: 2rpx solid #FFB0BD;
border-radius: 29rpx;
}
}
}
.filter-area {
display: flex;
align-items: center;
padding: 10rpx 0;
.filter-text {
font-size: 24rpx;
color: #666666;
margin-right: 6rpx;
}
.filter-icon {
width: 24rpx;
height: 24rpx;
/* 展开时切换为上下箭头图标,调整大小 */
&.rotate {
width: 18rpx;
height: 12rpx;
}
}
}
}
/* 核心抽屉动画布局 */
.filter-panel {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
/* 低于 Tab区的101保证隐藏时躲在其后 */
pointer-events: none;
/* 面板关闭时,穿透点击到底部列表 */
overflow: hidden;
/* 防止抽屉向上滑出时外溢 */
&.show {
pointer-events: auto;
/* 打开时恢复点击拦截 */
.filter-mask {
opacity: 1;
}
.filter-container {
transform: translateY(50rpx);
/* 滑出 */
}
}
.filter-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
.filter-container {
position: absolute;
top: 0;
left: 0;
right: 0;
background: #fff;
border-radius: 0 0 30rpx 30rpx;
padding: 40rpx 30rpx;
max-height: 80vh;
display: flex;
flex-direction: column;
box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.05);
transform: translateY(-100%);
/* 默认隐藏在上方即Tab背后 */
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}
}
/* 垂直分组设计的样式图3 */
.filter-scroll {
max-height: 50vh;
}
.filter-group {
margin-bottom: 30rpx;
.group-title {
font-weight: 500;
font-size: 28rpx;
color: #333333;
line-height: 40rpx;
margin-bottom: 20rpx;
}
.pill-list {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
}
}
/* 通用胶囊按钮 */
.pill-item {
width: calc(25% - 16rpx);
/* 25%是一行四个,减去间距 */
height: 58rpx;
background: #F5F5F5;
font-weight: 400;
font-size: 26rpx;
color: #333333;
line-height: 37rpx;
/* 使用 flex 居中 */
display: flex;
align-items: center;
justify-content: center;
border-radius: 12rpx;
box-sizing: border-box;
/* 防止 padding 会撑大盒子 */
&.active {
background: #FFF0F2;
color: #FF4767;
border: 1rpx solid #FFB0BD;
}
}
.course-filter {
display: flex;
gap: 20rpx;
/* padding: 20rpx 0 40rpx; */
.course-type-item {
background: #F5F5F5;
color: #666666;
font-size: 26rpx;
padding: 12rpx 40rpx;
border-radius: 12rpx;
&.active {
background: #FFF0F2;
color: #FF4767;
}
}
}
.filter-footer {
display: flex;
justify-content: space-between;
margin-top: 20rpx;
padding-top: 20rpx;
/* border-top: 1rpx solid #F5F5F5; */
.reset-btn,
.confirm-btn {
width: 47%;
text-align: center;
padding: 20rpx 0;
border-radius: 40rpx;
font-size: 28rpx;
}
.reset-btn {
background: #F5F5F5;
color: #666;
}
.confirm-btn {
background: #FF4767;
color: #fff;
}
}
/* 内容区缩进补充 */
.collection-content {
padding: 0 24rpx 24rpx;
margin-top: 20rpx;
overflow: hidden;
}
.brand-grid {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.brand-card-wrap {
width: 49.2%;
margin-bottom: 20rpx;
}
}
.course-list {
display: flex;
flex-direction: column;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
.empty-icon {
width: 346rpx;
height: 373rpx;
}
}
::v-deep .icon-headle {
width: 36rpx !important;
height: 36rpx !important;
}
</style>

View File

@ -400,7 +400,7 @@ export default {
left: 60rpx;
width: 630rpx;
height: 96rpx;
background: #E8101E;
background: #FF4767;
border-radius: 58rpx;
display: flex;
align-items: center;
@ -499,7 +499,7 @@ export default {
}
.region-tab.active {
color: #E8101E;
color: #FF4767;
font-weight: 500;
}
@ -511,7 +511,7 @@ export default {
transform: translateX(-50%);
width: 40rpx;
height: 4rpx;
background-color: #E8101E;
background-color: #FF4767;
border-radius: 2rpx;
}
@ -552,7 +552,7 @@ export default {
}
.popup-btn.confirm {
background: linear-gradient(90deg, #E8101E 0%, #FF618F 100%);
background: linear-gradient(90deg, #FF4767 0%, #FF618F 100%);
}
.popup-btn .btn-text {

356
pages/notice/detail.vue Normal file
View File

@ -0,0 +1,356 @@
<template>
<view class="notice-detail-page">
<!-- 自定义顶部导航组件 -->
<custom-navbar
title="公告通知详情"
:showBack="true"
backgroundColor="linear-gradient(180deg, #E6ECFB 0%, #FFFFFF 100%)"
:showHeadle="true"
borderBottom="none"
/>
<!-- 页面主要内容 -->
<view class="page-content">
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<view class="loading-icon"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 详情内容 -->
<view v-else-if="detailData.id" class="detail-card">
<!-- 标题 -->
<text class="detail-title">{{ detailData.title }}</text>
<!-- 发布时间 -->
<view class="detail-time">
<text class="time-text">时间{{ formatTime(detailData.create_time) }}</text>
<text class="browse-text"> <image class="browse-icon" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/6133cbef-5861-40f8-80f5-d8a86477c94f.png" mode="aspectFit"></image> {{ detailData.browse_num }}</text>
</view>
<!-- 富文本内容 -->
<view class="detail-content">
<rich-text :nodes="detailData.content"></rich-text>
</view>
</view>
<!-- 空状态或错误 -->
<view v-else-if="!loading && !detailData.id" class="empty-container">
<image class="empty-img" src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/8c5e7b2a-3d4f-4b1e-9a7c-6e8f2d1b4a5c.png" mode="aspectFit"></image>
<text class="empty-text">公告不存在或已删除</text>
</view>
</view>
</view>
</template>
<script>
import CustomNavbar from '@/components/custom-navbar/custom-navbar.vue';
import request from '@/utils/request';
export default {
components: {
CustomNavbar
},
data() {
return {
loading: false, //
detailData: { //
id: null,
title: '',
create_time: '',
content: ''
}
};
},
onLoad(options) {
const id = options.id;
if (id) {
this.fetchNoticeDetail(id);
} else {
uni.showToast({
title: '参数错误',
icon: 'none'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
},
methods: {
//
fetchNoticeDetail(id) {
this.loading = true;
request.post('/sj/notice/details', { id: id })
.then(res => {
if (res.code === 200 && res.data) {
this.detailData = res.data;
//
uni.setNavigationBarTitle({
title: this.detailData.title || '公告详情'
});
} else {
uni.showToast({
title: res.msg || '获取详情失败',
icon: 'none'
});
}
})
.catch(err => {
console.error('获取公告详情失败:', err);
uni.showToast({
title: '网络异常,请稍后重试',
icon: 'none'
});
})
.finally(() => {
this.loading = false;
});
},
formatTime(timeStr) {
if (!timeStr) return '--:--:--';
let formatted = timeStr;
if (timeStr.includes('.') && !timeStr.includes('-')) {
const match = timeStr.match(/(\d{4})\.(\d{2})\.(\d{2})(\d{2}:\d{2}:\d{2})/);
if (match) {
formatted = `${match[1]}-${match[2]}-${match[3]} ${match[4]}`;
} else {
formatted = timeStr.replace(/\./g, '-');
}
}
//
if (formatted.includes('-') && formatted.includes(':')) {
// : 2026.05.08 15:00:00
const datePart = formatted.split(' ')[0];
const timePart = formatted.split(' ')[1] || '';
if (datePart && datePart.includes('-')) {
const dotDate = datePart.replace(/-/g, '.');
return `${dotDate} ${timePart}`.trim();
}
return formatted;
}
return timeStr;
},
}
};
</script>
<style lang="scss" scoped>
.notice-detail-page {
min-height: 100vh;
background-color: #fff;
}
.page-content {
// padding: 20rpx 24rpx 60rpx;
}
//
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
.loading-icon {
width: 60rpx;
height: 60rpx;
border: 4rpx solid #e0e0e0;
border-top-color: #FF4767;
border-radius: 50%;
animation: spin 0.8s linear infinite;
margin-bottom: 20rpx;
}
.loading-text {
font-size: 26rpx;
color: #999;
}
}
@keyframes spin {
to { transform: rotate(360deg); }
}
//
.detail-card {
background: #ffffff;
border-radius: 24rpx;
padding: 20rpx 30rpx;
margin-bottom: 30rpx;
.detail-title {
display: block;
margin-bottom: 14rpx;
font-weight: 500;
font-size: 40rpx;
color: #333333;
line-height: 56rpx;
}
.detail-time {
display: flex;
align-items: center;
margin-bottom: 30rpx;
justify-content: space-between; //
// border-bottom: 1rpx solid #f0f0f0;
.time-icon {
width: 28rpx;
height: 28rpx;
margin-right: 10rpx;
opacity: 0.6;
}
.time-text {
font-weight: 400;
font-size: 28rpx;
color: #B6B6B6;
line-height: 40rpx;
}
.browse-icon {
width: 38rpx;
height: 22rpx;
// margin-right: 10rpx;
}
.browse-text {
margin-right: 10rpx;
font-weight: 400;
font-size: 28rpx;
color: #B08463;
line-height: 40rpx;
}
}
.detail-content {
font-weight: 400;
font-size: 28rpx;
color: #2A2A2A;
line-height: 40rpx;
overflow-x: hidden;
// rich-text
::v-deep img {
max-width: 100%;
height: auto;
display: block;
margin: 30rpx 0;
}
::v-deep p {
margin-bottom: 20rpx; //
padding: 0;
}
//
::v-deep br {
display: block; // <br>
margin-bottom: 30rpx; //
}
}
}
//
.empty-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 150rpx 0;
.empty-img {
width: 200rpx;
height: 200rpx;
margin-bottom: 30rpx;
opacity: 0.6;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
}
//
.member-card {
background: linear-gradient(135deg, #FFE6E8 0%, #FFF0E6 100%);
border-radius: 24rpx;
padding: 30rpx;
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
.member-info {
flex: 1;
.member-price {
display: block;
font-size: 32rpx;
font-weight: bold;
color: #FF4767;
line-height: 44rpx;
margin-bottom: 8rpx;
}
.member-desc {
display: block;
font-size: 24rpx;
color: #333;
line-height: 34rpx;
margin-bottom: 12rpx;
}
.member-tag {
display: flex;
align-items: center;
.tag-text, .tag-gift {
font-size: 20rpx;
background: rgba(255, 71, 103, 0.15);
padding: 4rpx 12rpx;
border-radius: 20rpx;
color: #FF4767;
margin-right: 12rpx;
}
.tag-gift {
background: rgba(255, 193, 7, 0.2);
color: #E6A100;
}
}
}
.member-btn {
background: linear-gradient(135deg, #FF4767, #FF6B8A);
border-radius: 48rpx;
padding: 14rpx 28rpx;
text-align: center;
min-width: 160rpx;
box-shadow: 0 4rpx 12rpx rgba(255, 71, 103, 0.3);
.btn-text {
display: block;
font-size: 26rpx;
font-weight: 600;
color: #FFFFFF;
line-height: 36rpx;
}
.btn-sub {
display: block;
font-size: 20rpx;
color: rgba(255, 255, 255, 0.9);
line-height: 28rpx;
}
}
}
::v-deep .icon-headle {
width: 32rpx !important;
height: 32rpx !important;
}
</style>

View File

@ -0,0 +1,213 @@
<template>
<view class="notice-page">
<!-- 自定义顶部导航组件 -->
<custom-navbar title="公告通知" :showBack="true" backgroundColor="#FFFFFF" :show-headle="true"
headleSrc="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/51ebca5d-95c5-4fb8-8aa3-560a79100d25.png"
@onHeadleClick="goToSearch" class="nav-img-size" />
<view class="page-content">
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<view class="loading-icon"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 公告列表 -->
<view v-else-if="noticeList.length > 0" class="notice-list">
<NoticeCard v-for="item in noticeList" :key="item.id" :item="item" @click="goToDetail" />
<!-- 加载更多状态 -->
<view v-if="loadingMore" class="load-more">
<text>加载更多...</text>
</view>
<view v-else-if="!hasMore && noticeList.length > 0" class="no-more">
<text>没有更多了</text>
</view>
</view>
<!-- 空状态 -->
<view v-else class="empty-container">
<image class="empty-img"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/8c5e7b2a-3d4f-4b1e-9a7c-6e8f2d1b4a5c.png"
mode="aspectFit"></image>
<text class="empty-text">暂无公告通知</text>
</view>
</view>
</view>
</template>
<script>
import CustomNavbar from '@/components/custom-navbar/custom-navbar.vue';
import NoticeCard from '@/pages/home/components/notice-card.vue';
import request from '@/utils/request';
export default {
components: {
CustomNavbar,
NoticeCard //
},
data() {
return {
loading: false,
loadingMore: false,
noticeList: [],
page: 1,
limit: 10,
total: 0,
hasMore: true
};
},
onLoad() {
this.fetchNoticeList(true);
},
onPullDownRefresh() {
this.fetchNoticeList(true);
},
onReachBottom() {
if (!this.loadingMore && this.hasMore && !this.loading) {
this.fetchNoticeList(false);
}
},
methods: {
//
goToSearch() {
uni.navigateTo({
url: '/pages/search_common/search?type=notice'
});
},
//
fetchNoticeList(reset = true) {
if (reset) {
this.page = 1;
this.hasMore = true;
this.loading = true;
} else {
if (!this.hasMore) return;
this.loadingMore = true;
// +1
this.page++;
}
request.post('/sj/notice/list', {
page: this.page,
limit: this.limit
}).then(res => {
if (res.code === 200 && res.data) {
const list = res.data.list || [];
const total = res.data.total || 0;
this.total = total;
if (reset) {
this.noticeList = list;
} else {
this.noticeList = [...this.noticeList, ...list];
}
//
this.hasMore = this.noticeList.length < total;
} else {
if (reset) this.noticeList = [];
this.hasMore = false;
//
if (!reset && this.page > 1) this.page--;
}
}).catch(err => {
console.error('获取公告列表失败:', err);
if (!reset && this.page > 1) this.page--; //
uni.showToast({
title: '加载失败,请稍后重试',
icon: 'none'
});
}).finally(() => {
this.loading = false;
this.loadingMore = false;
if (reset) {
uni.stopPullDownRefresh();
}
});
},
// NoticeCard $emit item
goToDetail(item) {
if (item.id) {
uni.navigateTo({
url: `/pages/notice/detail?id=${item.id}`
});
}
}
}
};
</script>
<style lang="scss" scoped>
.notice-page {
min-height: 100vh;
background-color: #f5f6f8;
}
.page-content {
padding: 20rpx 20rpx 40rpx;
}
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
.loading-icon {
width: 60rpx;
height: 60rpx;
border: 4rpx solid #e0e0e0;
border-top-color: #FF4767;
border-radius: 50%;
animation: spin 0.8s linear infinite;
margin-bottom: 20rpx;
}
.loading-text {
font-size: 26rpx;
color: #999;
}
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.load-more,
.no-more {
text-align: center;
padding: 30rpx 0;
font-size: 24rpx;
color: #999;
}
.empty-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 150rpx 0;
.empty-img {
width: 200rpx;
height: 200rpx;
margin-bottom: 30rpx;
opacity: 0.6;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
}
.nav-img-size::v-deep .icon-headle {
width: 36rpx !important;
height: 36rpx !important;
}
</style>

View File

@ -275,18 +275,18 @@ export default {
.rating-score {
font-size: 16rpx;
color: #E8101E;
color: #FF4767;
margin-left: 68rpx;
}
.currency {
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
}
.amount {
font-size: 32rpx;
color: #E8101E;
color: #FF4767;
font-weight: 500;
}
@ -328,7 +328,7 @@ export default {
}
.load-date {
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
}
.load-text {

View File

@ -716,7 +716,7 @@ page {
.real-price {
font-size: 32rpx;
font-weight: 500;
color: #E8101E;
color: #FF4767;
}
/* 订单信息卡片 */
.order-card {
@ -788,13 +788,13 @@ page {
margin-left: 20rpx;
}
.btn-border {
border: 1rpx solid #E8101E;
border: 1rpx solid #FF4767;
background-color: #fff;
box-sizing: border-box;
margin-left: 20rpx;
}
.btn-bg {
background-color: #E8101E;
background-color: #FF4767;
}
.button-text {
font-size: 28rpx;
@ -804,7 +804,7 @@ page {
.btn-border .button-text {
font-size: 28rpx;
font-weight: 400;
color: #E8101E;
color: #FF4767;
}
/* APP适配 */
/* #ifdef APP-PLUS */

View File

@ -24,7 +24,7 @@
<image :src="item.icon" mode="aspectFit" class="method-icon"></image>
<text class="method-name">{{ item.name }}</text>
</view>
<agree-radio :isAgree="item.selected" :yesImg="`/static/images/icons/yes_icon.png`" :radioSize="`40rpx`" :noImg="`/static/images/icons/no_icon.png`"></agree-radio>
<agree-radio :isAgree="item.selected" :yesImg="`/static/images/agree_y.png`" :radioSize="`40rpx`" :noImg="`/static/images/icons/no_icon.png`"></agree-radio>
</view>
</view>
</view>
@ -494,7 +494,7 @@
::v-deep .uv-count-down__text {
font-weight: 400;
font-size: 26rpx;
color: #E8101E;;
color: #FF4767;;
line-height: 37rpx;
text-align: center;
font-style: normal;
@ -559,13 +559,13 @@
.total-amount .symbol {
font-size: 24rpx;
font-weight: 500;
color: #E8101E;
color: #FF4767;
}
.total-amount .amount {
font-size: 28rpx;
font-weight: 500;
color: #E8101E;
color: #FF4767;
}
.amount-lable {
@ -576,7 +576,7 @@
.pay-btn {
width: 680rpx;
height: 88rpx;
background: linear-gradient( 180deg, #F52540 0%, #E8101E 100%);
background: linear-gradient( 180deg, #F52540 0%, #FF4767 100%);
border-radius: 43rpx;
font-weight: 500;
font-size: 30rpx;

View File

@ -623,7 +623,7 @@ export default {
height: 28rpx;
border: 3rpx;
margin-right: 16rpx;
background-color: #E8101E;
background-color: #FF4767;
}
.service-item {
@ -766,7 +766,7 @@ export default {
.price-total .price {
font-size: 28rpx;
font-weight: 500;
color: #E8101E;
color: #FF4767;
}
.amount-lable {
@ -777,7 +777,7 @@ export default {
.submit-btn {
width: 492rpx;
height: 64rpx;
background: #E8101E;
background: #FF4767;
border-radius: 32rpx;
color: #ffffff;
font-size: 28rpx;
@ -984,8 +984,8 @@ export default {
.isActive {
background: rgba(252, 67, 124, 0.08);
border-radius: 10rpx;
border: 1rpx solid #E8101E;
color: #E8101E;
border: 1rpx solid #FF4767;
color: #FF4767;
/* 选中状态 */
}
@ -1010,7 +1010,7 @@ export default {
&__box {
width: 648rpx;
height: 94rpx;
background: linear-gradient(140deg, #fa4e9a 0%, #E8101E 100%);
background: linear-gradient(140deg, #fa4e9a 0%, #FF4767 100%);
border-radius: 48rpx;
display: flex;
align-items: center;

View File

@ -18,7 +18,7 @@
<view class="group-invite"
v-if="orderInfo.state !=1 && orderInfo.team_buy_id && (orderInfo.team_buy_order_state==1||orderInfo.team_buy_order_state==2)">
<view class="invite-text" v-if="orderInfo.team_buy_order_state == 1">参团成功需邀请<text
style="color: #E8101E">{{orderInfo.team_buy.person_num - orderInfo.team_buy.pay_person_num}}</text>人才可完成拼团
style="color: #FF4767">{{orderInfo.team_buy.person_num - orderInfo.team_buy.pay_person_num}}</text>人才可完成拼团
</view>
<view class="invite-text" v-if="orderInfo.team_buy_order_state == 2">参团成功已拼团成功</view>
@ -266,16 +266,16 @@
formatPriceNumber(this.orderInfo.cost_money-this.orderInfo.order_money),
18,
2
)}rpx;color: #E8101E;line-height: 25rpx;text-align: left;font-style: normal;`" />
)}rpx;color: #FF4767;line-height: 25rpx;text-align: left;font-style: normal;`" />
</l-painter-view>
<l-painter-view css="margin-top:44rpx;">
<l-painter-text text="¥"
css="font-weight: 500;font-size: 26rpx;color: #E8101E;line-height: 48rpx;text-align: left;font-style: normal;" />
css="font-weight: 500;font-size: 26rpx;color: #FF4767;line-height: 48rpx;text-align: left;font-style: normal;" />
<l-painter-text :text=" formatPriceNumber(this.orderInfo.order_money)" :css="`font-weight: 500;font-size:${formatPriceSize(
formatPriceNumber(this.orderInfo.order_money),
56,
3
)}rpx;color: #E8101E;line-height: 34rpx;text-align: left;font-style: normal;`" />
)}rpx;color: #FF4767;line-height: 34rpx;text-align: left;font-style: normal;`" />
</l-painter-view>
<l-painter-view css="margin-top:0rpx;">
<l-painter-text :text="`原价¥${formatPriceNumber(this.orderInfo.cost_money)}`" :css="`text-decoration: line-through;font-weight: 400;font-size:${formatPriceSize(
@ -1005,7 +1005,7 @@
.phone-btn {
width: 64rpx;
height: 64rpx;
background-color: #E8101E;
background-color: #FF4767;
border-radius: 32rpx;
}
@ -1249,7 +1249,7 @@
.service-price2 {
font-weight: 500;
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
line-height: 34rpx;
text-align: left;
font-style: normal;
@ -1282,7 +1282,7 @@
font-family: DINPro, DINPro;
font-weight: 500;
font-size: 36rpx;
color: #E8101E;
color: #FF4767;
line-height: 34rpx;
text-align: left;
font-style: normal;
@ -1352,7 +1352,7 @@
}
.btn-bg {
background-color: #E8101E;
background-color: #FF4767;
}
.button-text {
@ -1444,7 +1444,7 @@
align-items: center;
font-weight: 500;
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
line-height: 33rpx;
text-align: left;
font-style: normal;

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@
<view>
<view class="serve-card-right__content__title">{{item.title}}</view>
<view class="serve-card-right__content__price"><text
style="color: #E8101E;font-weight: 500;">¥{{item.server_price}} </text> {{' '}}|
style="color: #FF4767;font-weight: 500;">¥{{item.server_price}} </text> {{' '}}|
{{item.server_time}}分钟
</view>
</view>

View File

@ -25,7 +25,7 @@
<text class="service-name">{{ item.server_title }}</text>
<text class="order-time">下单时间{{ item.pay_time }}</text>
<text class="order-status">
订单状态<text style="color: #E8101E;">{{ getDescriptionByNumber(item.state) }}</text>
订单状态<text style="color: #FF4767;">{{ getDescriptionByNumber(item.state) }}</text>
</text>
</view>
</view>

View File

@ -1,7 +1,7 @@
<template>
<view class="promotion">
<custom-navbar title="推广服务" :leftImg="'/static/images/whiteBack.png'" :showUser="true"
backgroundColor="linear-gradient( 134deg, #F52540 0%, #E8101E 100%)" titleColor="#fff"
backgroundColor="linear-gradient( 134deg, #F52540 0%, #FF4767 100%)" titleColor="#fff"
borderBottom="none"></custom-navbar>
<view class="promotion-search">
<view class="promotion-add">
@ -224,7 +224,7 @@
<style lang="less">
.promotion {
.promotion-search {
background: linear-gradient(134deg, #f52540 0%, #e8101e 100%);
background: linear-gradient(134deg, #f52540 0%, #FF4767 100%);
box-sizing: border-box;
padding: 31rpx 20rpx 60rpx 20rpx;
@ -292,7 +292,7 @@
position: relative;
&.active {
color: #E8101E;
color: #FF4767;
&::after {
content: '';
@ -325,7 +325,7 @@
width: 100%;
font-weight: 400;
font-size: 22rpx;
color: #E8101E;
color: #FF4767;
line-height: 30rpx;
text-align: left;
font-style: normal;
@ -374,7 +374,7 @@
border: 1rpx solid rgba(232, 16, 30, 0.2);
font-weight: 400;
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
line-height: 33rpx;
text-align: left;
font-style: normal;
@ -398,7 +398,7 @@
font-family: DINPro, DINPro;
font-weight: 500;
font-size: 36rpx;
color: #E8101E;
color: #FF4767;
line-height: 46rpx;
text-align: left;
font-style: normal;
@ -468,12 +468,12 @@
}
.closeModal {
border: 2rpx solid #E8101E;
color: #E8101E;
border: 2rpx solid #FF4767;
color: #FF4767;
}
.sureModal {
background: #E8101E;
background: #FF4767;
border-radius: 40rpx;
color: #FFFFFF;
}

View File

@ -2,7 +2,7 @@
<view class="select-serve">
<custom-navbar title="选择服务" backgroundColor="#FFFFFF"></custom-navbar>
<view class="select-tip">
<text style="color: #E8101E;">温馨提示</text>您可自主勾选需推广的服务灵活设置比例形式的推广佣金
<text style="color: #FF4767;">温馨提示</text>您可自主勾选需推广的服务灵活设置比例形式的推广佣金
配置完成后服务将自动进入推广平台曝光触达更多潜在客户后续通过推广渠道如达人链接分享码成功成交的订单
系统会从该笔订单中自动扣除您预设的佣金全程无需手动操作
</view>
@ -85,7 +85,7 @@
&__btn {
width: 690rpx;
height: 78rpx;
background: #E8101E;
background: #FF4767;
border-radius: 798rpx;
display: flex;
align-items: center;

View File

@ -2,7 +2,7 @@
<view class="select-serve">
<custom-navbar title="设置佣金" backgroundColor="#FFFFFF"></custom-navbar>
<view class="select-tip">
<text style="color: #E8101E;">温馨提示</text>支持 快速设置百分比如每单返 2%佣金成功成交的订单系统会按照实付金额*佣金比例进行扣减
<text style="color: #FF4767;">温馨提示</text>支持 按1%~30%区间设置佣金比例订单成功成交后系统将按实付金额 × 佣金比例自动扣减
</view>
<view v-for="(item,index) in selectedItems" :key="index" class="serve-card">
<image :src="item.photo[0]" mode="aspectFill" class="serve-card__img"></image>
@ -34,7 +34,7 @@
<uv-modal ref="modal" title="提示" width="620rpx">
<view class="slot-content">
预估佣金按<text style="color: #E8101E;">订单实付金额 × 佣金比例</text>计算因服务实付金额受活动参与程度影响存在波动最终佣金以订单实际结算结果为准
预估佣金按<text style="color: #FF4767;">订单实付金额 × 佣金比例</text>计算因服务实付金额受活动参与程度影响存在波动最终佣金以订单实际结算结果为准
</view>
<template v-slot:confirmButton>
<view type="default" @click="closeModal" class="closeModal">确定</view>
@ -271,7 +271,7 @@
font-family: DINPro, DINPro;
font-weight: 500;
font-size: 22rpx;
color: #E8101E;
color: #FF4767;
line-height: 34rpx;
text-align: left;
font-style: normal;
@ -328,7 +328,7 @@
&__btn {
width: 690rpx;
height: 78rpx;
background: #E8101E;
background: #FF4767;
border-radius: 798rpx;
display: flex;
align-items: center;
@ -347,7 +347,7 @@
.closeModal {
width: 556rpx;
height: 80rpx;
background: #E8101E;
background: #FF4767;
border-radius: 40rpx;
font-weight: 500;
font-size: 28rpx;

View File

@ -1386,7 +1386,7 @@
.title-bar {
width: 6rpx;
height: 48rpx;
background-color: #E8101E;
background-color: #FF4767;
margin-right: 16rpx;
border-radius: 3rpx;
}
@ -1585,7 +1585,7 @@
.promise-title {
font-size: 28rpx;
color: #E8101E;
color: #FF4767;
font-weight: 400;
margin-bottom: 24rpx;
text-align: center;
@ -1603,7 +1603,7 @@
width: 12rpx;
height: 12rpx;
border-radius: 6rpx;
background-color: #E8101E;
background-color: #FF4767;
margin-right: 24rpx;
}
@ -1711,8 +1711,8 @@
}
.checkbox.checked {
background-color: #E8101E;
border-color: #E8101E;
background-color: #FF4767;
border-color: #FF4767;
}
.check-icon {
@ -1745,7 +1745,7 @@
}
.popup-btn.confirm {
background: linear-gradient(90deg, #E8101E 0%, #FF618F 100%);
background: linear-gradient(90deg, #FF4767 0%, #FF618F 100%);
}
.popup-btn .btn-text {
@ -1783,7 +1783,7 @@
}
.region-tab.active {
color: #E8101E;
color: #FF4767;
font-weight: 500;
}
@ -1795,7 +1795,7 @@
transform: translateX(-50%);
width: 40rpx;
height: 4rpx;
background-color: #E8101E;
background-color: #FF4767;
border-radius: 2rpx;
}
@ -1858,7 +1858,7 @@
.confirm-text {
font-size: 32rpx;
color: #E8101E;
color: #FF4767;
font-weight: 500;
}
@ -1900,7 +1900,7 @@
}
.time-block.active .time-value {
color: #E8101E;
color: #FF4767;
}
.time-separator {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -73,7 +73,7 @@
}
.btn-wallet {
font-size: 24rpx;
color: #E8101E;
color: #FF4767;
margin-top: 40rpx;
}
.btn-contact {

View File

@ -135,31 +135,30 @@ export default {
return;
}
// type
let type;
if (this.identity == 1) {
type = 2; //
} else if (this.identity == 2) {
type = 3; //
} else {
console.error('未知的用户类型identity:', this.identity);
this.backMag = '用户类型错误,请联系客服';
return;
}
console.log('请求参数 type:', type);
request.post('/user/getuser', {type}).then(result => {
console.log('接口返回数据:', result);
if (result.data && result.data.back_text) {
request.post('/sj/userSjAuth/details', {
type: this.identity // 1=2=
}).then(result => {
console.log('申请详情接口返回数据:', result);
if (result.code === 200) {
if (result.data) {
if (result.data.back_text) {
this.backMag = result.data.back_text;
console.log('设置backMag为:', this.backMag);
} else {
this.backMag = '未知原因,请联系客服';
console.log('接口返回无back_text字段');
}
} else {
//
console.log('接口返回成功但data为空');
this.backMag = '暂无驳回原因信息';
}
} else {
this.backMag = '获取驳回原因失败,请联系客服';
console.log('接口返回错误:', result?.msg || '未知错误');
}
}).catch(err => {
console.error('获取用户信息失败:', err);
console.error('获取申请详情失败:', err);
this.backMag = '获取驳回原因失败,请联系客服';
}).finally(() => {
console.log('getUserInfo执行完成');
@ -248,7 +247,7 @@ export default {
.btn-back {
width: 630rpx;
height: 96rpx;
background: #E8101E;
background: #FF4767;
border-radius: 48rpx;
display: flex;
align-items: center;
@ -268,11 +267,11 @@ export default {
display: flex;
align-items: center;
justify-content: center;
border: 1rpx solid #E8101E;
border: 1rpx solid #FF4767;
margin-top: 32rpx;
}
.fail-back-btn .btn-text {
color: #E8101E;
color: #FF4767;
}
.submitres-page /deep/ .back-btn {

View File

@ -0,0 +1,131 @@
<template>
<view class="pages">
<custom-navbar :title="infoData.top" :showBack="true"></custom-navbar>
<view class="title-icon">{{ infoData.title }}</view>
<view class="title-content">{{ infoData.notice }}</view>
<view class="erweimabox">
<view class="erweimab">
<image :src="infoData.photo" mode="widthFix"
style="width: 404rpx;border-radius:25rpx ;margin: 37rpx 0 0 36rpx;"></image>
</view>
<view class="kefubtn">扫码联系专属客服</view>
</view>
</view>
</template>
<script>
import request from '../../utils/request';
export default {
props: {
data: {
type: Object,
default: () => ({})
}
},
data() {
return {
infoData: {}
}
},
mounted() {
this.getCustomerService();
},
methods: {
async getCustomerService() {
try {
const res = await request.post('/sj/userSjAuth/getCustomerService');
console.log('客服接口返回:', res);
if (res && (res.state == 1 || res.code == 200)) {
if (res.data.state != 3) {
this.infoData = res.data || {};
} else {
this.infoData = {
top: '审核结果',
title: '审核未通过',
photo: res.data.photo
};
this.$emit('stateChange', 3);
request.post('/sj/userSjAuth/details', {
type: 1
}).then(res => {
if (res && (res.state == 1 || res.code == 200)) {
console.log(res.data.back_text, '驳回res')
this.infoData = {
...this.infoData,
notice: res.data.back_text || '您的入驻申请未通过,请重新提交申请'
};
}
})
}
}
} catch (e) {
console.error('获取客服信息失败:', e);
}
}
}
}
</script>
<style>
.kefubtn {
height: 42rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 30rpx;
color: #5A5A5A;
line-height: 42rpx;
text-align: center;
font-style: normal;
margin-top: 23rpx;
}
.pages {
background-image: url('https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/467f6b5f-32dc-4ba3-847a-22be4569c45e.png');
background-size: 100%;
background-repeat: no-repeat;
}
.erweimab {
margin: 70rpx 0 0 103rpx;
width: 477rpx;
height: 477rpx;
background-image: url('https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/e5461570-10e3-4e11-a6ca-8543f106639a.png');
background-size: 100%;
background-repeat: no-repeat;
}
.erweimabox {
margin: 71rpx 0 0 6rpx;
width: 690rpx;
height: 690rpx;
background: #FFFFFF;
border-radius: 62rpx;
border: 1rpx solid #F2F4F6;
}
.title-icon {
/* margin-left: 283rpx; */
height: 67rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 48rpx;
color: #333333;
line-height: 67rpx;
text-align: center;
font-style: normal;
margin-top: -40rpx;
}
.title-content {
margin: 14rpx 0 0 0rpx;
height: 42rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 30rpx;
color: #666666;
line-height: 42rpx;
text-align: center;
font-style: normal;
}
</style>

View File

@ -116,6 +116,6 @@ export default {
}
.delete {
color: #e8101e;
color: #FF4767;
}
</style>

View File

@ -279,10 +279,10 @@
getIconColor(index) {
if (this.activeIndex === index) {
return '#E8101E';
return '#FF4767';
}
if (this.hasValue(this.filterTabs[index].key)) {
return '#E8101E';
return '#FF4767';
}
return '#666';
},
@ -527,7 +527,7 @@
}
&.active {
color: #E8101E;
color: #FF4767;
}
.check-icon {
@ -567,7 +567,7 @@
}
&.active {
background: #FFECF0;
color: #E8101E;
color: #FF4767;
}
}
@ -620,7 +620,7 @@
height: 100%;
.custom-price-container.active & {
color: #E8101E;
color: #FF4767;
}
}

View File

@ -192,7 +192,7 @@ export default {
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(306deg, #e8101e 0%, #fc563b 100%);
background: linear-gradient(306deg, #FF4767 0%, #fc563b 100%);
background-size: cover;
color: #fff;
font-size: 26rpx;

Some files were not shown because too many files have changed in this diff Show More