Compare commits
244 Commits
custom/com
...
merge/tmp-
| Author | SHA1 | Date | |
|---|---|---|---|
| 50908a274b | |||
| 9e68562de5 | |||
| 8e5561c2a7 | |||
| 8d5c543a4f | |||
| cf411407dd | |||
| 86e4be302b | |||
| 2fd06796e1 | |||
| 60c51e551a | |||
| d0d3ac47c6 | |||
| af677f6565 | |||
| f889efeb16 | |||
| d026bb6180 | |||
| 82b856f5d9 | |||
| 7f33b6948a | |||
| 3489b89a0b | |||
| dc4f2769f9 | |||
| 5cf87e4d56 | |||
| c9ca63cff5 | |||
| a6072f4895 | |||
| 38947ddc62 | |||
| cfe83ada38 | |||
| 1e9e0ffea8 | |||
| 28acdfb621 | |||
| ad9f1c495b | |||
| 7c2a7468e7 | |||
| b2051dcf92 | |||
| e3c86de84a | |||
| 6e0eb5d4f3 | |||
| 9d186ff094 | |||
| 8611ec4dc3 | |||
| 89bfd94a07 | |||
| 5cf8b7b467 | |||
| 2cc0162435 | |||
| d8e75a0c9e | |||
| 8af3c0ccb9 | |||
| d8b78168a8 | |||
| 7675e8e04d | |||
| 88d05048a6 | |||
| 28a92c0635 | |||
| b5a47b8b4e | |||
| 4f99e8b5f2 | |||
| 87fc49146c | |||
| 826fcfb024 | |||
| 7174d87b79 | |||
| 4bc747cd9c | |||
| e046868665 | |||
| 19c3ec4f9f | |||
| ffbc15929d | |||
| 524280d816 | |||
| 6e736e0733 | |||
| 4891566793 | |||
| b02f5fbc21 | |||
| 4f5a300afa | |||
| aae6e76797 | |||
| 3162d6a9f2 | |||
| 8010ac45a6 | |||
| baebba4cef | |||
| 1be7155fe8 | |||
| 611c0b930b | |||
| ec1903519d | |||
| b1953a020d | |||
| 3ce26322e0 | |||
| 588ec60948 | |||
| 962ea124fa | |||
| 0ca0fe3b0d | |||
| cef2b6e02e | |||
| e018f9a8b9 | |||
| def8a6cf6f | |||
| 857f295923 | |||
| 50ee885b74 | |||
| 410f248510 | |||
| ba5fcfaab1 | |||
| 5a46bb8d69 | |||
| e33ad16d10 | |||
| d8ba7ace0f | |||
| 875072bb68 | |||
| 0912b9b031 | |||
| 7ffe4acc0f | |||
| 4bcf0cbd2d | |||
| fdf85dfec7 | |||
| 1d62b37060 | |||
| 4ad93ceace | |||
| 294856d9ef | |||
| ba2c7d6d64 | |||
| ec9e76fda7 | |||
| 5dba8071e4 | |||
| 19a53ef414 | |||
| e943b11c8f | |||
| e9db54980e | |||
| 3ddcc665b8 | |||
| a981c02baf | |||
| 1c3f759b35 | |||
| bb58866192 | |||
| 069b3c2c07 | |||
| c6fd13956a | |||
| a489e24988 | |||
| 6897fc631b | |||
| 6d23a64aef | |||
| f34533b0c7 | |||
| 72c74f44f0 | |||
| 7f3d9c0f10 | |||
| 37f95e7267 | |||
| d3c630e860 | |||
| c5b57cedeb | |||
| bf24746a7c | |||
| 5ff2925ba1 | |||
| 3dc81e7f53 | |||
| 0417c5c5ed | |||
| db26a85c8b | |||
| 54c39259a7 | |||
| 15361211b1 | |||
| e301ddc6ec | |||
| a560ad4a5d | |||
| 7f7a18803f | |||
| 42563d7184 | |||
| 14a903f42a | |||
| c9f8614927 | |||
| fe9303bf4c | |||
| e4dfd0ae11 | |||
| 7037d0b083 | |||
| e8a79bd245 | |||
| 047cf765da | |||
| 3a30b0817d | |||
| a464a8731f | |||
| 7599a80943 | |||
| 992a01cc76 | |||
| dd09c66215 | |||
| e2b89c348f | |||
| 475edc93a6 | |||
| cb86cba389 | |||
| 0dc4dec616 | |||
| 2a5214df11 | |||
| 09e410df00 | |||
|
|
75dfe80bde | ||
|
|
2a1f33323c | ||
|
|
b441c46993 | ||
| 38ade75046 | |||
| 1e51abd7cd | |||
| 0939449aa7 | |||
| 29b5cfda6f | |||
| ceca4e5956 | |||
| aa9d2e64d2 | |||
| e8ccb87266 | |||
| 153c84266a | |||
| 7ae7a1d3bd | |||
| 1ebb94e9e2 | |||
| 9b53540f91 | |||
| 2a36fa2b60 | |||
| 2223636184 | |||
| 6144dc72b8 | |||
| 47e1c2372d | |||
| 5b9bef9214 | |||
| 08880a15df | |||
| e40e6e73b2 | |||
| dd4176998b | |||
| 2896817435 | |||
| dc956761a0 | |||
| 36fd0621fd | |||
| 1e6cd55f0a | |||
| f7fcf7fb27 | |||
| 28359f2f16 | |||
| b3cc86d088 | |||
| c6a8bc04f2 | |||
| 0ed0789528 | |||
| 16d1a3a945 | |||
| 82df9ddbac | |||
| 392a8e2952 | |||
| db7a8d7202 | |||
| bb87f4b1d2 | |||
| fe039ed126 | |||
| 03c1a8d71a | |||
| e8fb35b310 | |||
| 286fcc2142 | |||
| 2280ff68fa | |||
| 9e0cab7ec4 | |||
| a31c004508 | |||
| ee8c24cdf8 | |||
| b043601a77 | |||
| e20968aee2 | |||
| 8ceeca33b4 | |||
| 02e9cfa313 | |||
| 3f894bcb20 | |||
| 8d5d6e3da8 | |||
| ce1ea6eadf | |||
| 191c803f66 | |||
| 4e1448d2c4 | |||
| ce26a9be5f | |||
| 8b01d8f8ec | |||
| 6ab2834fdc | |||
| 85816e416c | |||
| bf8e177c30 | |||
| 57111a00c2 | |||
| aa7c7cbe60 | |||
| d012c7fff7 | |||
| 128d75b921 | |||
| 7e0d61c7e5 | |||
| 26af083839 | |||
| 68bc853dff | |||
| 13166132c7 | |||
| f87c7d963e | |||
| 6f6899deaa | |||
| 7f54666629 | |||
| 5747b256c6 | |||
| 09b285016c | |||
| 72c12a79fd | |||
| 84f4ad83c2 | |||
| 1512c60109 | |||
| 5b41dee46c | |||
| 9ed337b59a | |||
| 8fedeafe63 | |||
| 31336f9a80 | |||
| 2f7c9bf0af | |||
| f8257361b5 | |||
| ec05534ff1 | |||
| 4f9c353e9a | |||
| 52b5f5b006 | |||
| 5a8663eb48 | |||
| 8108802204 | |||
| 6fe3bceb4b | |||
| ee4fbb277c | |||
| 6718010ceb | |||
| faf1f6d3d8 | |||
| 2d98a32efc | |||
| 7fde30063b | |||
| 10465b7cf2 | |||
| c7e9b497ae | |||
| de63cd4c6a | |||
| 2f230d3b2c | |||
| 6f01b3c1c6 | |||
| b10fcf02d4 | |||
| 6956841c8b | |||
| 90820ade30 | |||
| 8df59abd03 | |||
| f3bb6a1782 | |||
| 2a0489d4b2 | |||
| ca74d4f8e5 | |||
| d9c9599cb2 | |||
| 142a97d65c | |||
| b945583857 | |||
| 88debacf8c | |||
| 9d12b02463 | |||
| 23e81114e9 | |||
| 08583aa8aa | |||
| 5e536afeae |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@
|
|||||||
/.hbuilderx
|
/.hbuilderx
|
||||||
/.idea
|
/.idea
|
||||||
/node_modules
|
/node_modules
|
||||||
|
/iconfont-preview.html
|
||||||
|
|||||||
@@ -38,6 +38,22 @@ const localDevConfig = ({
|
|||||||
uniacid: 1,
|
uniacid: 1,
|
||||||
domain: 'https://test.aigc-quickapp.com',
|
domain: 'https://test.aigc-quickapp.com',
|
||||||
},
|
},
|
||||||
})['2811']; // 选择要使用的环境配置
|
'local-2': { // 测试平台
|
||||||
|
uniacid: 2,
|
||||||
|
domain: 'http://localhost:8050/',
|
||||||
|
},
|
||||||
|
'100': {
|
||||||
|
uniacid: 100,
|
||||||
|
domain: 'https://5g.aigc-quickapp.com/',
|
||||||
|
},
|
||||||
|
'1': {
|
||||||
|
uniacid: 1,
|
||||||
|
domain: 'https://5g.aigc-quickapp.com/',
|
||||||
|
},
|
||||||
|
'115': {
|
||||||
|
uniacid: 115,
|
||||||
|
domain: 'https://5g.aigc-quickapp.com/',
|
||||||
|
},
|
||||||
|
})['1']; // 选择要使用的环境配置
|
||||||
|
|
||||||
export default localDevConfig;
|
export default localDevConfig;
|
||||||
@@ -18,6 +18,10 @@ const localDevConfig = ({
|
|||||||
uniacid: 2811,
|
uniacid: 2811,
|
||||||
domain: 'https://xcx6.aigc-quickapp.com/',
|
domain: 'https://xcx6.aigc-quickapp.com/',
|
||||||
},
|
},
|
||||||
|
'2812': { // IVD数商模式
|
||||||
|
uniacid: 2812,
|
||||||
|
domain: 'https://xcx6.aigc-quickapp.com/',
|
||||||
|
},
|
||||||
'2724': { // 生物菌肥
|
'2724': { // 生物菌肥
|
||||||
uniacid: 2724,
|
uniacid: 2724,
|
||||||
domain: 'https://xcx.aigc-quickapp.com/',
|
domain: 'https://xcx.aigc-quickapp.com/',
|
||||||
@@ -38,6 +42,14 @@ const localDevConfig = ({
|
|||||||
uniacid: 1,
|
uniacid: 1,
|
||||||
domain: 'https://test.aigc-quickapp.com',
|
domain: 'https://test.aigc-quickapp.com',
|
||||||
},
|
},
|
||||||
})['2811']; // 选择要使用的环境配置
|
'local-2': { // 测试平台
|
||||||
|
uniacid: 2,
|
||||||
|
domain: 'http://localhost:8050/',
|
||||||
|
},
|
||||||
|
'local-2-dev': { // 本地开发测试平台
|
||||||
|
uniacid: 2,
|
||||||
|
domain: 'http://localhost:8050/',
|
||||||
|
},
|
||||||
|
})['2812']; // 选择要使用的环境配置
|
||||||
|
|
||||||
export default localDevConfig;
|
export default localDevConfig;
|
||||||
2
App.vue
2
App.vue
@@ -314,7 +314,7 @@
|
|||||||
@import './common/css/iconfont.css';
|
@import './common/css/iconfont.css';
|
||||||
@import './common/css/icondiy.css'; // 自定义图标库
|
@import './common/css/icondiy.css'; // 自定义图标库
|
||||||
@import './common/css/icon/extend.css'; // 扩展图标库
|
@import './common/css/icon/extend.css'; // 扩展图标库
|
||||||
page{
|
page {
|
||||||
background: #f4f6fa;
|
background: #f4f6fa;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -123,7 +123,7 @@ image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.choose-store {
|
.choose-store {
|
||||||
/deep/ .uni-popup__wrapper{
|
::v-deep .uni-popup__wrapper{
|
||||||
background: none!important;
|
background: none!important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -870,13 +870,13 @@
|
|||||||
// 海报
|
// 海报
|
||||||
// .uni-popup__wrapper-box
|
// .uni-popup__wrapper-box
|
||||||
.poster-layer {
|
.poster-layer {
|
||||||
/deep/ .uni-popup__wrapper.center {
|
::v-deep .uni-popup__wrapper.center {
|
||||||
width: 100vw!important;
|
width: 100vw!important;
|
||||||
height: 100vh!important;
|
height: 100vh!important;
|
||||||
background: none!important;
|
background: none!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .uni-popup__wrapper.uni-custom.center .uni-popup__wrapper-box {
|
::v-deep .uni-popup__wrapper.uni-custom.center .uni-popup__wrapper-box {
|
||||||
max-width: 100vw!important;
|
max-width: 100vw!important;
|
||||||
max-height: 100vh!important;
|
max-height: 100vh!important;
|
||||||
background: none!important;
|
background: none!important;
|
||||||
|
|||||||
@@ -559,7 +559,7 @@ scroll-view ::-webkit-scrollbar {
|
|||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/::-webkit-scrollbar {
|
::v-deep ::-webkit-scrollbar {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
@@ -609,7 +609,7 @@ scroll-view ::-webkit-scrollbar {
|
|||||||
font-weight: 500!important;
|
font-weight: 500!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .reward-popup .uni-popup__wrapper-box {
|
::v-deep .reward-popup .uni-popup__wrapper-box {
|
||||||
background: none !important;
|
background: none !important;
|
||||||
max-width: unset !important;
|
max-width: unset !important;
|
||||||
max-height: unset !important;
|
max-height: unset !important;
|
||||||
@@ -618,7 +618,7 @@ scroll-view ::-webkit-scrollbar {
|
|||||||
|
|
||||||
// #ifdef H5
|
// #ifdef H5
|
||||||
// 下拉加载动画【页面】
|
// 下拉加载动画【页面】
|
||||||
/deep/ body uni-page-refresh div{
|
::v-deep body uni-page-refresh div{
|
||||||
width: 14rpx !important;
|
width: 14rpx !important;
|
||||||
height: 14rpx !important;
|
height: 14rpx !important;
|
||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
@@ -626,7 +626,7 @@ scroll-view ::-webkit-scrollbar {
|
|||||||
clip: rect(-152rpx, 90rpx, 90rpx, -30rpx) !important;
|
clip: rect(-152rpx, 90rpx, 90rpx, -30rpx) !important;
|
||||||
animation:.6s backgroundChange linear infinite;
|
animation:.6s backgroundChange linear infinite;
|
||||||
}
|
}
|
||||||
/deep/ body uni-page-refresh div::after{
|
::v-deep body uni-page-refresh div::after{
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -22rpx;
|
left: -22rpx;
|
||||||
@@ -636,7 +636,7 @@ scroll-view ::-webkit-scrollbar {
|
|||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
animation:.5s backgroundChange linear infinite;
|
animation:.5s backgroundChange linear infinite;
|
||||||
}
|
}
|
||||||
/deep/ body uni-page-refresh div::before{
|
::v-deep body uni-page-refresh div::before{
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -22rpx;
|
right: -22rpx;
|
||||||
@@ -646,15 +646,15 @@ scroll-view ::-webkit-scrollbar {
|
|||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
animation:.7s backgroundChange linear infinite;
|
animation:.7s backgroundChange linear infinite;
|
||||||
}
|
}
|
||||||
/deep/ body uni-page-refresh > div > div{
|
::v-deep body uni-page-refresh > div > div{
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下拉加载动画【scroll-view】
|
// 下拉加载动画【scroll-view】
|
||||||
/deep/ body .uni-scroll-view-refresher{
|
::v-deep body .uni-scroll-view-refresher{
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
/deep/ body .uni-scroll-view-refresher div{
|
::v-deep body .uni-scroll-view-refresher div{
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
@@ -666,7 +666,7 @@ scroll-view ::-webkit-scrollbar {
|
|||||||
clip: rect(-152rpx, 90rpx, 90rpx, -30rpx) !important;
|
clip: rect(-152rpx, 90rpx, 90rpx, -30rpx) !important;
|
||||||
animation:.6s backgroundChange linear infinite;
|
animation:.6s backgroundChange linear infinite;
|
||||||
}
|
}
|
||||||
/deep/ body .uni-scroll-view-refresher div::after{
|
::v-deep body .uni-scroll-view-refresher div::after{
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -22rpx;
|
left: -22rpx;
|
||||||
@@ -676,7 +676,7 @@ scroll-view ::-webkit-scrollbar {
|
|||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
animation:.5s backgroundChange linear infinite;
|
animation:.5s backgroundChange linear infinite;
|
||||||
}
|
}
|
||||||
/deep/ body .uni-scroll-view-refresher div::before{
|
::v-deep body .uni-scroll-view-refresher div::before{
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -22rpx;
|
right: -22rpx;
|
||||||
@@ -686,7 +686,7 @@ scroll-view ::-webkit-scrollbar {
|
|||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
animation:.7s backgroundChange linear infinite;
|
animation:.7s backgroundChange linear infinite;
|
||||||
}
|
}
|
||||||
/deep/ body .uni-scroll-view-refresher > div > div{
|
::v-deep body .uni-scroll-view-refresher > div > div{
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
@keyframes backgroundChange {
|
@keyframes backgroundChange {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// 修复图片垂直对齐问题,解决两张图片上下有空白缝隙问题
|
// 修复图片垂直对齐问题,解决两张图片上下有空白缝隙问题
|
||||||
/deep/ ._img {
|
::v-deep ._img {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
@@ -377,7 +377,7 @@ view {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .goods-form {
|
::v-deep .goods-form {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -1354,7 +1354,7 @@ view {
|
|||||||
border-bottom: 2rpx solid #F4F4F6;
|
border-bottom: 2rpx solid #F4F4F6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .form-wrap {
|
::v-deep .form-wrap {
|
||||||
margin: 0 24rpx;
|
margin: 0 24rpx;
|
||||||
|
|
||||||
.icon-right {
|
.icon-right {
|
||||||
|
|||||||
@@ -5,6 +5,17 @@ import store from '@/store/index.js'
|
|||||||
let currentConversationId = null;
|
let currentConversationId = null;
|
||||||
const CONVERSATION_KEY = 'ai_conversation_id';
|
const CONVERSATION_KEY = 'ai_conversation_id';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取域名配置参数(避免重复代码)
|
||||||
|
*/
|
||||||
|
function getDomainParams() {
|
||||||
|
return {
|
||||||
|
imgDomain: Config.imgDomain,
|
||||||
|
h5Domain: Config.h5Domain,
|
||||||
|
baseUrl: Config.baseUrl
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化时从本地读取会话ID
|
// 初始化时从本地读取会话ID
|
||||||
try {
|
try {
|
||||||
const saved = uni.getStorageSync(CONVERSATION_KEY);
|
const saved = uni.getStorageSync(CONVERSATION_KEY);
|
||||||
@@ -16,12 +27,28 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取uniacid
|
||||||
|
* @returns {number} uniacid
|
||||||
|
*/
|
||||||
|
getUniacid() {
|
||||||
|
return uni.getStorageSync('uniacid') || Config.uniacid; // 从缓存中获取uniacid,或使用默认uniacid
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送普通消息(blocking 模式)
|
* 发送普通消息(blocking 模式)
|
||||||
*/
|
*/
|
||||||
async sendMessage(message, options = {}) {
|
async sendMessage(message, options = {}) {
|
||||||
try {
|
try {
|
||||||
|
// 调试:打印关键状态
|
||||||
|
console.log('[AI Service] sendMessage 开始执行');
|
||||||
|
console.log('[AI Service] store.state.memberInfo:', store.state.memberInfo);
|
||||||
|
console.log('[AI Service] uniacid:', this.getUniacid());
|
||||||
|
console.log('[AI Service] currentConversationId:', this.getConversationId());
|
||||||
|
|
||||||
const aiConfig = store.getters.globalAIKefuConfig;
|
const aiConfig = store.getters.globalAIKefuConfig;
|
||||||
|
console.log('[AI Service] aiConfig:', aiConfig);
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
url: '/api/kefu/chat',
|
url: '/api/kefu/chat',
|
||||||
data: {
|
data: {
|
||||||
@@ -31,28 +58,38 @@ export default {
|
|||||||
query: message,
|
query: message,
|
||||||
response_mode: 'blocking', // 强制 blocking
|
response_mode: 'blocking', // 强制 blocking
|
||||||
user: store.state.memberInfo?.id || 'anonymous',
|
user: store.state.memberInfo?.id || 'anonymous',
|
||||||
conversation_id: this.getConversationId() || ''
|
conversation_id: this.getConversationId() || '',
|
||||||
|
...getDomainParams()
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('Sending blocking request to:', params.url);
|
console.log('[AI Service] 发送请求到:', params.url);
|
||||||
console.log('Request data:', params.data);
|
console.log('[AI Service] 请求参数:', JSON.stringify(params.data, null, 2));
|
||||||
|
|
||||||
const response = await http.sendRequest({
|
const response = await http.sendRequest({
|
||||||
...params,
|
...params,
|
||||||
async: false
|
async: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('[AI Service] 收到响应:', response);
|
||||||
|
|
||||||
const result = this.handleResponse(response);
|
const result = this.handleResponse(response);
|
||||||
if (result.conversationId) {
|
if (result.conversationId) {
|
||||||
this.setConversationId(result.conversationId);
|
this.setConversationId(result.conversationId);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Dify API请求失败:', error);
|
console.error('[AI Service] Dify API请求失败,详细错误:', error);
|
||||||
|
console.error('[AI Service] 错误堆栈:', error.stack);
|
||||||
|
// 打印更多信息帮助调试
|
||||||
|
console.error('[AI Service] store 状态:', {
|
||||||
|
memberInfo: store.state.memberInfo,
|
||||||
|
uniacid: this.getUniacid(),
|
||||||
|
token: store.state.token
|
||||||
|
});
|
||||||
throw new Error('AI服务暂时不可用,请稍后重试');
|
throw new Error('AI服务暂时不可用,请稍后重试');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -60,55 +97,112 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* 流式消息入口(自动适配平台)
|
* 流式消息入口(自动适配平台)
|
||||||
*/
|
*/
|
||||||
async sendStreamMessage(message, onChunk, onComplete) {
|
async sendStreamMessage(message, onChunk, onComplete) {
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
// 微信小程序:降级为普通请求 + 前端打字模拟
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
const match = Config.baseUrl.match(/^(?:https?:\/\/|wss?:\/\/)([^\/\s:]+)/);
|
||||||
const result = await this.sendMessage(message);
|
const domain = match ? match[1] : '';
|
||||||
const content = result.content || '';
|
const wssUrl = `wss://${domain}/ws/aikefu`;
|
||||||
const conversationId = result.conversationId || '';
|
console.log(`wssUrl = ${wssUrl}`);
|
||||||
|
const socketTask = wx.connectSocket({
|
||||||
|
url: wssUrl,
|
||||||
|
header: {}
|
||||||
|
});
|
||||||
|
|
||||||
// 保存会话ID(确保连续对话)
|
let content = '';
|
||||||
if (conversationId) {
|
let conversationId = '';
|
||||||
this.setConversationId(conversationId);
|
let isAuthenticated = false;
|
||||||
}
|
|
||||||
|
|
||||||
// 模拟打字效果
|
socketTask.onOpen(() => {
|
||||||
let index = 0;
|
console.log('WebSocket 连接成功,开始认证...');
|
||||||
const chunkSize = 2; // 每次显示2个字符
|
socketTask.send({
|
||||||
return new Promise((resolve) => {
|
data: JSON.stringify({
|
||||||
const timer = setInterval(() => {
|
action: 'auth',
|
||||||
if (index < content.length) {
|
uniacid: this.getUniacid(),
|
||||||
const chunk = content.substring(index, index + chunkSize);
|
token: store.state.token || 'test_token',
|
||||||
index += chunkSize;
|
user_id: store.state.memberInfo?.id || 'anonymous'
|
||||||
if (onChunk) onChunk(chunk);
|
})
|
||||||
} else {
|
});
|
||||||
clearInterval(timer);
|
});
|
||||||
if (onComplete) {
|
|
||||||
onComplete({
|
socketTask.onMessage((res) => {
|
||||||
content: content,
|
try {
|
||||||
conversation_id: conversationId
|
const data = JSON.parse(res.data);
|
||||||
});
|
console.log('收到 WebSocket 消息:', data);
|
||||||
}
|
|
||||||
resolve({ content, conversation_id: conversationId });
|
if (data.type === 'auth_success') {
|
||||||
}
|
console.log('认证成功,发送聊天消息...');
|
||||||
}, 80); // 打字速度:80ms/次
|
isAuthenticated = true;
|
||||||
});
|
socketTask.send({
|
||||||
} catch (error) {
|
data: JSON.stringify({
|
||||||
console.error('小程序流式消息降级失败:', error);
|
action: 'chat',
|
||||||
if (onComplete) {
|
uniacid: this.getUniacid(),
|
||||||
onComplete({ error: error.message || '发送失败' });
|
query: message,
|
||||||
}
|
user_id: store.state.memberInfo?.id || 'anonymous',
|
||||||
throw error;
|
conversation_id: this.getConversationId() || '',
|
||||||
}
|
...getDomainParams()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} else if (data.type === 'auth_failed') {
|
||||||
|
const errorMsg = '认证失败,请重新登录';
|
||||||
|
console.error(errorMsg, data);
|
||||||
|
reject(new Error(errorMsg));
|
||||||
|
if (onComplete) onComplete({ error: errorMsg });
|
||||||
|
socketTask.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理流式消息块
|
||||||
|
else if (data.event === 'message') {
|
||||||
|
const text = data.answer || data.content || data.text || '';
|
||||||
|
content += text;
|
||||||
|
if (onChunk) onChunk(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理流结束
|
||||||
|
else if (data.event === 'message_end') {
|
||||||
|
conversationId = data.conversation_id || '';
|
||||||
|
if (conversationId) {
|
||||||
|
this.setConversationId(conversationId);
|
||||||
|
}
|
||||||
|
if (onComplete) {
|
||||||
|
onComplete({ content, conversation_id: conversationId });
|
||||||
|
}
|
||||||
|
resolve({ content, conversation_id: conversationId });
|
||||||
|
socketTask.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 可选:处理 done
|
||||||
|
else if (data.type === 'done') {
|
||||||
|
console.log('对话完成:', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error('WebSocket 消息解析失败:', e, '原始数据:', res.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socketTask.onError((err) => {
|
||||||
|
const errorMsg = 'WebSocket 连接失败';
|
||||||
|
console.error(errorMsg, err);
|
||||||
|
reject(new Error(errorMsg));
|
||||||
|
if (onComplete) onComplete({ error: errorMsg });
|
||||||
|
});
|
||||||
|
|
||||||
|
socketTask.onClose(() => {
|
||||||
|
console.log('WebSocket 连接已关闭');
|
||||||
|
});
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
if (!isAuthenticated || content === '') {
|
||||||
|
console.warn('WebSocket 超时,强制关闭');
|
||||||
|
socketTask.close();
|
||||||
|
reject(new Error('AI服务响应超时'));
|
||||||
|
if (onComplete) onComplete({ error: 'AI服务响应超时' });
|
||||||
|
}
|
||||||
|
}, 10000);
|
||||||
|
});
|
||||||
// #endif
|
// #endif
|
||||||
|
},
|
||||||
// #ifdef H5
|
|
||||||
// H5:使用真实流式(EventSource / Fetch)
|
|
||||||
return this.sendHttpStream(message, onChunk, onComplete);
|
|
||||||
// #endif
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP 流式请求(仅 H5 使用)
|
* HTTP 流式请求(仅 H5 使用)
|
||||||
*/
|
*/
|
||||||
@@ -123,20 +217,19 @@ export default {
|
|||||||
'Accept': 'text/event-stream',
|
'Accept': 'text/event-stream',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
uniacid: store.state.uniacid || '1',
|
uniacid: this.getUniacid(),
|
||||||
|
token: store.state.token || 'test_token',
|
||||||
inputs: {},
|
inputs: {},
|
||||||
query: message,
|
query: message,
|
||||||
response_mode: 'streaming',
|
response_mode: 'streaming',
|
||||||
user_id: store.state.memberInfo?.id || 'anonymous',
|
user_id: store.state.memberInfo?.id || 'anonymous',
|
||||||
conversation_id: this.getConversationId() || ''
|
conversation_id: this.getConversationId() || '',
|
||||||
|
...getDomainParams()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok || !response.body) {
|
||||||
throw new Error(`HTTP ${response.status}`);
|
throw new Error('无效响应');
|
||||||
}
|
|
||||||
if (!response.body) {
|
|
||||||
throw new Error('响应体不可用');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const reader = response.body.getReader();
|
const reader = response.body.getReader();
|
||||||
@@ -145,50 +238,64 @@ export default {
|
|||||||
let content = '';
|
let content = '';
|
||||||
let conversationId = '';
|
let conversationId = '';
|
||||||
|
|
||||||
function processBuffer(buf, callback) {
|
while (true) {
|
||||||
const lines = buf.split('\n');
|
const { done, value } = await reader.read();
|
||||||
buf = lines.pop() || '';
|
if (done) break;
|
||||||
|
|
||||||
|
buffer += decoder.decode(value, { stream: true });
|
||||||
|
|
||||||
|
// 按行分割,保留不完整的最后一行
|
||||||
|
const lines = buffer.split('\n');
|
||||||
|
buffer = lines.pop() || ''; // 未完成的行留到下次
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const trimmed = line.trim();
|
const trimmed = line.trim();
|
||||||
if (trimmed.startsWith('data:')) {
|
if (trimmed.startsWith('data:')) {
|
||||||
const jsonStr = trimmed.slice(5).trim();
|
try {
|
||||||
if (jsonStr) {
|
const jsonStr = trimmed.slice(5).trim();
|
||||||
try {
|
if (jsonStr && jsonStr !== '[DONE]') {
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
if (data.event === 'message') {
|
if (data.event === 'message') {
|
||||||
const text = data.answer || data.text || '';
|
const text = data.answer || data.text || '';
|
||||||
content += text;
|
content += text;
|
||||||
callback(text);
|
if (onChunk) onChunk(text);
|
||||||
}
|
}
|
||||||
if (data.conversation_id) {
|
if (data.conversation_id) {
|
||||||
conversationId = data.conversation_id;
|
conversationId = data.conversation_id;
|
||||||
}
|
}
|
||||||
if (data.event === 'message_end') {
|
if (data.event === 'message_end') {
|
||||||
// 可选:提前完成
|
// 可提前结束
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.warn('解析流数据失败:', e);
|
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('解析失败:', e, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return buf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
// 处理最后残留的 buffer(如果有)
|
||||||
const { done, value } = await reader.read();
|
if (buffer.trim().startsWith('data:')) {
|
||||||
if (done) break;
|
try {
|
||||||
buffer += decoder.decode(value, { stream: true });
|
const jsonStr = buffer.trim().slice(5);
|
||||||
buffer = processBuffer(buffer, (chunk) => {
|
if (jsonStr) {
|
||||||
if (onChunk) onChunk(chunk);
|
const data = JSON.parse(jsonStr);
|
||||||
});
|
if (data.event === 'message') {
|
||||||
|
const text = data.answer || '';
|
||||||
|
content += text;
|
||||||
|
if (onChunk) onChunk(text);
|
||||||
|
}
|
||||||
|
if (data.conversation_id) {
|
||||||
|
conversationId = data.conversation_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('最后 buffer 解析失败:', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onComplete) {
|
if (onComplete) {
|
||||||
onComplete({
|
onComplete({ content, conversation_id: conversationId });
|
||||||
content,
|
|
||||||
conversation_id: conversationId
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return { content, conversation_id: conversationId };
|
return { content, conversation_id: conversationId };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -196,11 +303,6 @@ export default {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
// 理论上不会执行到这里,但防止 fallback
|
|
||||||
return this.sendStreamMessage(message, onChunk, onComplete);
|
|
||||||
// #endif
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -256,17 +358,19 @@ export default {
|
|||||||
* 生成新会话(如需)
|
* 生成新会话(如需)
|
||||||
*/
|
*/
|
||||||
async generateConversationId() {
|
async generateConversationId() {
|
||||||
const params = {
|
const params = {
|
||||||
url: '/api/kefu/createConversation',
|
url: '/api/kefu/createConversation',
|
||||||
data: {
|
data: {
|
||||||
uniacid: store.state.uniacid,
|
uniacid: this.getUniacid(),
|
||||||
user_id: store.state.memberInfo?.id || 'anonymous',
|
token: store.state.token || 'test_token',
|
||||||
member_id: store.state.memberInfo?.id || '',
|
user_id: store.state.memberInfo?.id || 'anonymous',
|
||||||
},
|
member_id: store.state.memberInfo?.id || '',
|
||||||
header: {
|
...getDomainParams()
|
||||||
'Content-Type': 'application/json'
|
},
|
||||||
}
|
header: {
|
||||||
};
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
};
|
||||||
const response = await http.sendRequest({
|
const response = await http.sendRequest({
|
||||||
...params,
|
...params,
|
||||||
async: false
|
async: false
|
||||||
@@ -284,6 +388,7 @@ export default {
|
|||||||
async: false,
|
async: false,
|
||||||
data: {}
|
data: {}
|
||||||
});
|
});
|
||||||
|
console.log('/api/kefu/health response', response);
|
||||||
const available = [response?.data?.status, response?.data?.components?.ai_service_config?.status]
|
const available = [response?.data?.status, response?.data?.components?.ai_service_config?.status]
|
||||||
.filter(item => item === 'healthy').length > 0;
|
.filter(item => item === 'healthy').length > 0;
|
||||||
return {
|
return {
|
||||||
@@ -305,7 +410,10 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const response = await http.sendRequest({
|
const response = await http.sendRequest({
|
||||||
url: '/api/kefu/clear-conversation',
|
url: '/api/kefu/clear-conversation',
|
||||||
data: { conversation_id: conversationId },
|
data: {
|
||||||
|
conversation_id: conversationId,
|
||||||
|
...getDomainParams()
|
||||||
|
},
|
||||||
async: false
|
async: false
|
||||||
});
|
});
|
||||||
return response.success;
|
return response.success;
|
||||||
@@ -324,13 +432,14 @@ export default {
|
|||||||
throw new Error('会话ID(conversation_id)是必填参数');
|
throw new Error('会话ID(conversation_id)是必填参数');
|
||||||
}
|
}
|
||||||
const requestData = {
|
const requestData = {
|
||||||
uniacid: params.uniacid || store.state.uniacid || '1',
|
uniacid: params.uniacid || this.getUniacid(),
|
||||||
conversation_id: params.conversation_id,
|
conversation_id: params.conversation_id,
|
||||||
user_id: params.user_id || store.state.memberInfo?.id || 'anonymous',
|
user_id: params.user_id || store.state.memberInfo?.id || 'anonymous',
|
||||||
limit: params.limit || 20,
|
limit: params.limit || 20,
|
||||||
offset: params.offset || 0,
|
offset: params.offset || 0,
|
||||||
member_id: params.member_id || store.state.memberInfo?.id || '',
|
member_id: params.member_id || store.state.memberInfo?.id || '',
|
||||||
token: params.token || ''
|
token: params.token || '',
|
||||||
|
...getDomainParams()
|
||||||
};
|
};
|
||||||
const response = await http.sendRequest({
|
const response = await http.sendRequest({
|
||||||
url: '/api/kefu/getHistory',
|
url: '/api/kefu/getHistory',
|
||||||
@@ -369,13 +478,15 @@ export default {
|
|||||||
if (window.currentEventSource) {
|
if (window.currentEventSource) {
|
||||||
window.currentEventSource.close();
|
window.currentEventSource.close();
|
||||||
}
|
}
|
||||||
const params = new URLSearchParams({
|
const paramsObj = {
|
||||||
uniacid: store.state.uniacid || '1',
|
uniacid: this.getUniacid(),
|
||||||
user_id: store.state.memberInfo?.id || '123456',
|
user_id: store.state.memberInfo?.id,
|
||||||
query: message,
|
query: message,
|
||||||
conversation_id: conversationId || '',
|
conversation_id: conversationId || '',
|
||||||
stream: 'true'
|
stream: 'true',
|
||||||
});
|
...getDomainParams()
|
||||||
|
};
|
||||||
|
const params = new URLSearchParams(paramsObj);
|
||||||
const url = `${Config.baseUrl}/api/kefu/chat?${params.toString()}`;
|
const url = `${Config.baseUrl}/api/kefu/chat?${params.toString()}`;
|
||||||
try {
|
try {
|
||||||
const eventSource = new EventSource(url);
|
const eventSource = new EventSource(url);
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class ConfigExternal {
|
|||||||
try {
|
try {
|
||||||
// 动态加载主题配置
|
// 动态加载主题配置
|
||||||
const themeData = require(`@/common/js/style_color.js`)['default'][theme];
|
const themeData = require(`@/common/js/style_color.js`)['default'][theme];
|
||||||
console.log('async themeData => ', themeData);
|
// console.log('async themeData => ', themeData);
|
||||||
this.loadedConfigs[`theme_${theme}`] = themeData;
|
this.loadedConfigs[`theme_${theme}`] = themeData;
|
||||||
resolve(themeData);
|
resolve(themeData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ try {
|
|||||||
// 默认域名, 自定义发行时可以修改
|
// 默认域名, 自定义发行时可以修改
|
||||||
let defaultDomain = '';
|
let defaultDomain = '';
|
||||||
|
|
||||||
|
// #ifdef H5_XCX_5G_QUICKAPP_COM
|
||||||
|
defaultDomain = 'https://xcx20.5g-quickapp.com';
|
||||||
|
// #endif
|
||||||
|
|
||||||
// #ifdef H5_XCX_AIGC_QUICKAPP_COM
|
// #ifdef H5_XCX_AIGC_QUICKAPP_COM
|
||||||
defaultDomain = 'https://xcx.aigc-quickapp.com/';
|
defaultDomain = 'https://xcx.aigc-quickapp.com/';
|
||||||
// #endif
|
// #endif
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
* 客服统一处理服务
|
* 客服统一处理服务
|
||||||
* 整合各种客服方式,提供统一的调用接口
|
* 整合各种客服方式,提供统一的调用接口
|
||||||
*/
|
*/
|
||||||
|
import IMAGE_CONFIG from './image-config.js';
|
||||||
|
|
||||||
export class CustomerService {
|
export class CustomerService {
|
||||||
constructor(vueInstance, externalConfig = null) {
|
constructor(vueInstance, externalConfig = null) {
|
||||||
this.vm = vueInstance;
|
this.vm = vueInstance;
|
||||||
@@ -28,28 +30,29 @@ export class CustomerService {
|
|||||||
return this.latestPlatformConfig;
|
return this.latestPlatformConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先级:外部传入的最新配置 > vuex配置 > 空对象
|
// 优先级:外部传入 > vuex store > 空对象
|
||||||
const servicerConfig = this.externalConfig || this.vm.$store.state.servicerConfig || {};
|
const servicerConfig = this.externalConfig || this.vm.$store.state.servicerConfig || {};
|
||||||
console.log(`【实时客服配置】`, servicerConfig);
|
console.log(`【实时客服配置】`, servicerConfig);
|
||||||
|
|
||||||
let platformConfig = null;
|
let platformConfig = null;
|
||||||
|
|
||||||
// #ifdef H5
|
// #ifdef H5
|
||||||
platformConfig = servicerConfig.h5 ? (typeof servicerConfig.h5 === 'object' ? servicerConfig.h5 : null) : null;
|
platformConfig = servicerConfig.h5 && typeof servicerConfig.h5 === 'object' ? servicerConfig.h5 : null;
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
platformConfig = servicerConfig.weapp ? (typeof servicerConfig.weapp === 'object' ? servicerConfig.weapp : null) : null;
|
platformConfig = servicerConfig.weapp && typeof servicerConfig.weapp === 'object' ? servicerConfig.weapp : null;
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifdef MP-ALIPAY
|
// #ifdef MP-ALIPAY
|
||||||
platformConfig = servicerConfig.aliapp ? (typeof servicerConfig.aliapp === 'object' ? servicerConfig.aliapp : null) : null;
|
platformConfig = servicerConfig.aliapp && typeof servicerConfig.aliapp === 'object' ? servicerConfig.aliapp : null;
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifdef PC
|
// #ifdef PC
|
||||||
platformConfig = servicerConfig.pc ? (typeof servicerConfig.pc === 'object' ? servicerConfig.pc : null) : null;
|
platformConfig = servicerConfig.pc && typeof servicerConfig.pc === 'object' ? servicerConfig.pc : null;
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// 处理空数组情况(你的配置中pc/aliapp是空数组,转为null)
|
// 防止空数组被当作有效配置
|
||||||
if (Array.isArray(platformConfig)) {
|
if (Array.isArray(platformConfig)) {
|
||||||
platformConfig = null;
|
platformConfig = null;
|
||||||
}
|
}
|
||||||
@@ -75,6 +78,15 @@ export class CustomerService {
|
|||||||
return config && typeof config === 'object' && config.type;
|
return config && typeof config === 'object' && config.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有有效的客服配置(用于判断是否显示客服按钮)
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
hasValidKefuConfig() {
|
||||||
|
const config = this.getPlatformConfig();
|
||||||
|
return config && typeof config === 'object' && config.type && config.type !== 'none';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证客服配置完整性
|
* 验证客服配置完整性
|
||||||
* @returns {Object} 验证结果
|
* @returns {Object} 验证结果
|
||||||
@@ -89,32 +101,18 @@ export class CustomerService {
|
|||||||
warnings: []
|
warnings: []
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!config) {
|
if (!config || !config.type) {
|
||||||
result.isValid = false;
|
|
||||||
result.errors.push('客服配置不存在');
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.type === 'aikefu') {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.type) {
|
|
||||||
result.isValid = false;
|
result.isValid = false;
|
||||||
result.errors.push('客服类型未配置');
|
result.errors.push('客服类型未配置');
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.type === 'wxwork') {
|
if (config.type === 'wxwork') {
|
||||||
if (!wxworkConfig) {
|
if (!wxworkConfig || !wxworkConfig.enable) {
|
||||||
result.isValid = false;
|
result.warnings.push('企业微信未启用');
|
||||||
result.errors.push('企业微信配置不存在');
|
}
|
||||||
} else {
|
if (!wxworkConfig.contact_url) {
|
||||||
if (!wxworkConfig.enable) {
|
result.warnings.push('企业微信活码链接未配置');
|
||||||
result.warnings.push('企业微信功能未启用');
|
|
||||||
}
|
|
||||||
if (!wxworkConfig.contact_url) {
|
|
||||||
result.warnings.push('企业微信活码链接未配置,将使用原有客服方式');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,40 +120,39 @@ export class CustomerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 跳转到Dify客服页面
|
* 跳转到 AI 客服页面(Dify)
|
||||||
*/
|
*/
|
||||||
openDifyService() {
|
openDifyService() {
|
||||||
try {
|
try {
|
||||||
if (this.vm.setAiUnreadCount) {
|
// 清除未读数(如果存在)
|
||||||
|
if (typeof this.vm.setAiUnreadCount === 'function') {
|
||||||
this.vm.setAiUnreadCount(0);
|
this.vm.setAiUnreadCount(0);
|
||||||
}
|
}
|
||||||
// 强制跳转,忽略框架层的封装
|
|
||||||
uni.redirectTo({
|
// ✅ 修正路径:必须与 pages.json 中注册的路径一致
|
||||||
url: '/pages_tool/ai-chat/index',
|
const aiChatUrl = '/pages_tool/ai-chat/index';
|
||||||
|
|
||||||
|
// ✅ 使用 navigateTo 保留返回栈(体验更好)
|
||||||
|
uni.navigateTo({
|
||||||
|
url: aiChatUrl,
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
// 兜底:使用window.location跳转(H5)
|
console.error('跳转 AI 客服失败:', err);
|
||||||
|
// H5 兜底
|
||||||
// #ifdef H5
|
// #ifdef H5
|
||||||
window.location.href = '/pages_tool/ai-chat/index';
|
window.location.href = aiChatUrl;
|
||||||
// #endif
|
// #endif
|
||||||
console.error('跳转Dify客服失败:', err);
|
uni.showToast({ title: '打开客服失败', icon: 'none' });
|
||||||
uni.showToast({
|
|
||||||
title: '跳转客服失败',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('跳转Dify客服异常:', e);
|
console.error('跳转 AI 客服异常:', e);
|
||||||
uni.showToast({
|
uni.showToast({ title: '打开客服失败', icon: 'none' });
|
||||||
title: '跳转客服失败',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理客服点击事件
|
* 处理客服点击事件(统一入口)
|
||||||
* @param {Object} options 选项参数
|
* @param {Object} options 选项参数(用于消息卡片等)
|
||||||
*/
|
*/
|
||||||
handleCustomerClick(options = {}) {
|
handleCustomerClick(options = {}) {
|
||||||
const validation = this.validateConfig();
|
const validation = this.validateConfig();
|
||||||
@@ -170,6 +167,9 @@ export class CustomerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const config = this.getPlatformConfig();
|
const config = this.getPlatformConfig();
|
||||||
|
console.log('【当前客服配置】', config);
|
||||||
|
console.log('【客服类型】', config.type);
|
||||||
|
|
||||||
const { niushop = {}, sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options;
|
const { niushop = {}, sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options;
|
||||||
|
|
||||||
if (config.type === 'none') {
|
if (config.type === 'none') {
|
||||||
@@ -177,44 +177,51 @@ export class CustomerService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 核心分支:根据最新的type处理
|
// 核心路由:根据 type 决定行为
|
||||||
switch (config.type) {
|
switch (config.type) {
|
||||||
case 'aikefu':
|
case 'aikefu':
|
||||||
|
console.log('【跳转 AI 客服】目标路径: /pages_tool/ai-chat/index');
|
||||||
this.openDifyService();
|
this.openDifyService();
|
||||||
break;
|
break;
|
||||||
case 'wxwork':
|
case 'wxwork':
|
||||||
|
console.log('【跳转企业微信客服】');
|
||||||
this.openWxworkService(false, config, options);
|
this.openWxworkService(false, config, options);
|
||||||
break;
|
break;
|
||||||
case 'third':
|
case 'third':
|
||||||
|
console.log('【跳转第三方客服】');
|
||||||
|
this.openThirdService(config);
|
||||||
|
break;
|
||||||
|
case 'miniprogram':
|
||||||
|
console.log('【跳转第三方小程序客服】');
|
||||||
this.openThirdService(config);
|
this.openThirdService(config);
|
||||||
break;
|
break;
|
||||||
case 'niushop':
|
case 'niushop':
|
||||||
|
console.log('【跳转牛商客服】');
|
||||||
this.openNiushopService(niushop);
|
this.openNiushopService(niushop);
|
||||||
break;
|
break;
|
||||||
case 'weapp':
|
case 'weapp':
|
||||||
|
console.log('【跳转微信官方客服】');
|
||||||
this.openWeappService(config, options);
|
this.openWeappService(config, options);
|
||||||
break;
|
break;
|
||||||
case 'aliapp':
|
case 'aliapp':
|
||||||
|
console.log('【跳转支付宝客服】');
|
||||||
this.openAliappService(config);
|
this.openAliappService(config);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
console.error('【未知客服类型】', config.type);
|
||||||
this.makePhoneCall();
|
this.makePhoneCall();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ================== 各类型客服实现 ==================
|
||||||
* 打开企业微信客服
|
|
||||||
* @param {boolean} useOriginalService 是否使用原有客服方式
|
|
||||||
* @param {Object} servicerConfig 客服配置
|
|
||||||
* @param {Object} options 选项参数
|
|
||||||
*/
|
|
||||||
openWxworkService(useOriginalService = false, servicerConfig = null, options = {}) {
|
openWxworkService(useOriginalService = false, servicerConfig = null, options = {}) {
|
||||||
const config = servicerConfig || this.getPlatformConfig();
|
const config = servicerConfig || this.getPlatformConfig();
|
||||||
const wxworkConfig = this.getWxworkConfig();
|
const wxworkConfig = this.getWxworkConfig();
|
||||||
const { sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options;
|
const { sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options;
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
if (wxworkConfig?.enable && wxworkConfig?.contact_url && !useOriginalService) {
|
if (!useOriginalService && wxworkConfig?.enable && wxworkConfig?.contact_url) {
|
||||||
wx.navigateToMiniProgram({
|
wx.navigateToMiniProgram({
|
||||||
appId: 'wxeb490c6f9b154ef9',
|
appId: 'wxeb490c6f9b154ef9',
|
||||||
path: `pages/contacts/externalContactDetail?url=${encodeURIComponent(wxworkConfig.contact_url)}`,
|
path: `pages/contacts/externalContactDetail?url=${encodeURIComponent(wxworkConfig.contact_url)}`,
|
||||||
@@ -225,9 +232,16 @@ export class CustomerService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// 检查是否有企业微信配置
|
||||||
|
if (!config.wxwork_url && !config.corpid) {
|
||||||
|
console.error('企业微信配置不完整,缺少 wxwork_url 或 corpid');
|
||||||
|
uni.showToast({ title: '企业微信配置不完整', icon: 'none' });
|
||||||
|
this.fallbackToPhoneCall();
|
||||||
|
return;
|
||||||
|
}
|
||||||
wx.openCustomerServiceChat({
|
wx.openCustomerServiceChat({
|
||||||
extInfo: { url: config.wxwork_url },
|
extInfo: { url: config.wxwork_url || '' },
|
||||||
corpId: config.corpid,
|
corpId: config.corpid || '',
|
||||||
showMessageCard: true,
|
showMessageCard: true,
|
||||||
sendMessageTitle,
|
sendMessageTitle,
|
||||||
sendMessagePath,
|
sendMessagePath,
|
||||||
@@ -237,120 +251,125 @@ export class CustomerService {
|
|||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifdef H5
|
// #ifdef H5
|
||||||
if (wxworkConfig?.enable && wxworkConfig?.contact_url) {
|
if (!useOriginalService && wxworkConfig?.enable && wxworkConfig?.contact_url) {
|
||||||
window.location.href = wxworkConfig.contact_url;
|
window.location.href = wxworkConfig.contact_url;
|
||||||
} else if (config.wxwork_url) {
|
} else if (config.wxwork_url) {
|
||||||
location.href = config.wxwork_url;
|
window.location.href = config.wxwork_url;
|
||||||
} else {
|
} else {
|
||||||
this.fallbackToPhoneCall();
|
this.fallbackToPhoneCall();
|
||||||
}
|
}
|
||||||
// #endif
|
// #endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 打开第三方客服
|
|
||||||
* @param {Object} config 客服配置
|
|
||||||
*/
|
|
||||||
openThirdService(config) {
|
openThirdService(config) {
|
||||||
|
console.log('【第三方客服配置】', config);
|
||||||
|
console.log('【配置字段】', Object.keys(config));
|
||||||
|
|
||||||
|
// 支持多种可能的字段名
|
||||||
|
const miniAppId = config.mini_app_id || config.miniAppId || config.appid || config.appId || config.app_id;
|
||||||
|
const miniAppPath = config.mini_app_path || config.miniAppPath || config.path || config.page_path || '';
|
||||||
|
|
||||||
|
console.log('【解析后的小程序配置】AppID:', miniAppId, 'Path:', miniAppPath);
|
||||||
|
|
||||||
|
// 优先处理第三方微信小程序客服
|
||||||
|
if (miniAppId) {
|
||||||
|
console.log('【跳转第三方小程序】AppID:', miniAppId, 'Path:', miniAppPath);
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
wx.navigateToMiniProgram({
|
||||||
|
appId: miniAppId,
|
||||||
|
path: miniAppPath,
|
||||||
|
success: () => {
|
||||||
|
console.log('【跳转第三方小程序成功】');
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('【跳转第三方小程序失败】', err);
|
||||||
|
uni.showToast({ title: '跳转失败,请稍后重试', icon: 'none' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
|
// #ifdef H5
|
||||||
|
uni.showToast({ title: '第三方小程序客服仅在微信小程序中可用', icon: 'none' });
|
||||||
|
// #endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理第三方链接客服
|
||||||
if (config.third_url) {
|
if (config.third_url) {
|
||||||
|
console.log('【跳转第三方链接】', config.third_url);
|
||||||
|
// #ifdef H5
|
||||||
window.location.href = config.third_url;
|
window.location.href = config.third_url;
|
||||||
|
// #endif
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: config.third_url,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({ title: '链接已复制,请在浏览器打开', icon: 'none' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
} else {
|
} else {
|
||||||
|
console.error('【第三方客服配置不完整】缺少 mini_app_id 或 third_url');
|
||||||
this.fallbackToPhoneCall();
|
this.fallbackToPhoneCall();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 打开牛商客服
|
|
||||||
* @param {Object} niushop 牛商参数
|
|
||||||
*/
|
|
||||||
openNiushopService(niushop) {
|
openNiushopService(niushop) {
|
||||||
if (Object.keys(niushop).length > 0) {
|
if (Object.keys(niushop).length > 0 && this.vm.$util?.redirectTo) {
|
||||||
this.vm.$util.redirectTo('/pages_tool/chat/room', niushop);
|
this.vm.$util.redirectTo('/pages_tool/chat/room', niushop);
|
||||||
} else {
|
} else {
|
||||||
this.makePhoneCall();
|
this.makePhoneCall();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 打开微信小程序客服
|
|
||||||
* @param {Object} config 客服配置
|
|
||||||
* @param {Object} options 选项参数
|
|
||||||
*/
|
|
||||||
openWeappService(config, options = {}) {
|
openWeappService(config, options = {}) {
|
||||||
if (!this.shouldUseCustomService(config)) {
|
// 如果 useOfficial 为 true 或 undefined,则使用原生系统客服(由 button open-type="contact" 触发)
|
||||||
console.log('使用官方微信小程序客服');
|
// 此方法仅用于自定义跳转(如 useOfficial: false)
|
||||||
|
if (config.useOfficial !== false) {
|
||||||
|
// 不做任何事,应由 <button open-type="contact"> 触发
|
||||||
|
console.log('使用微信官方客服,请确保按钮为 <button open-type="contact">');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('使用自定义微信小程序客服');
|
|
||||||
this.handleCustomWeappService(config, options);
|
this.handleCustomWeappService(config, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理自定义微信小程序客服
|
|
||||||
* @param {Object} config 客服配置
|
|
||||||
* @param {Object} options 选项参数
|
|
||||||
*/
|
|
||||||
handleCustomWeappService(config, options = {}) {
|
handleCustomWeappService(config, options = {}) {
|
||||||
const { sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options;
|
|
||||||
|
|
||||||
if (config.customServiceUrl) {
|
if (config.customServiceUrl) {
|
||||||
let url = config.customServiceUrl;
|
let url = config.customServiceUrl;
|
||||||
const params = [];
|
const params = [];
|
||||||
|
const { sendMessageTitle, sendMessagePath, sendMessageImg } = options;
|
||||||
if (sendMessageTitle) params.push(`title=${encodeURIComponent(sendMessageTitle)}`);
|
if (sendMessageTitle) params.push(`title=${encodeURIComponent(sendMessageTitle)}`);
|
||||||
if (sendMessagePath) params.push(`path=${encodeURIComponent(sendMessagePath)}`);
|
if (sendMessagePath) params.push(`path=${encodeURIComponent(sendMessagePath)}`);
|
||||||
if (sendMessageImg) params.push(`img=${encodeURIComponent(sendMessageImg)}`);
|
if (sendMessageImg) params.push(`img=${encodeURIComponent(sendMessageImg)}`);
|
||||||
|
|
||||||
if (params.length > 0) {
|
if (params.length > 0) {
|
||||||
url += (url.includes('?') ? '&' : '?') + params.join('&');
|
url += (url.includes('?') ? '&' : '?') + params.join('&');
|
||||||
}
|
}
|
||||||
|
uni.navigateTo({ url });
|
||||||
uni.navigateTo({
|
|
||||||
url: url,
|
|
||||||
fail: (err) => {
|
|
||||||
console.error('跳转自定义客服页面失败:', err);
|
|
||||||
this.tryThirdPartyService(config, options);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tryThirdPartyService(config, options);
|
this.tryThirdPartyService(config, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 尝试使用第三方客服
|
|
||||||
* @param {Object} config 客服配置
|
|
||||||
* @param {Object} options 选项参数
|
|
||||||
*/
|
|
||||||
tryThirdPartyService(config, options = {}) {
|
tryThirdPartyService(config, options = {}) {
|
||||||
if (config.thirdPartyServiceUrl) {
|
// 支持第三方微信小程序客服
|
||||||
// #ifdef H5
|
if (config.thirdPartyMiniAppId || config.mini_app_id) {
|
||||||
window.open(config.thirdPartyServiceUrl, '_blank');
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
if (config.thirdPartyMiniAppId) {
|
wx.navigateToMiniProgram({
|
||||||
wx.navigateToMiniProgram({
|
appId: config.thirdPartyMiniAppId || config.mini_app_id,
|
||||||
appId: config.thirdPartyMiniAppId,
|
path: config.thirdPartyMiniAppPath || config.mini_app_path || ''
|
||||||
path: config.thirdPartyMiniAppPath || '',
|
});
|
||||||
fail: (err) => {
|
// #endif
|
||||||
console.error('跳转第三方小程序失败:', err);
|
return;
|
||||||
this.fallbackToPhoneCall();
|
}
|
||||||
}
|
|
||||||
});
|
// 支持第三方链接客服
|
||||||
} else {
|
if (config.thirdPartyServiceUrl || config.third_url) {
|
||||||
uni.setClipboardData({
|
const serviceUrl = config.thirdPartyServiceUrl || config.third_url;
|
||||||
data: config.thirdPartyServiceUrl,
|
// #ifdef H5
|
||||||
success: () => {
|
window.open(serviceUrl, '_blank');
|
||||||
uni.showModal({
|
// #endif
|
||||||
title: '客服链接已复制',
|
// #ifdef MP-WEIXIN
|
||||||
content: '客服链接已复制到剪贴板,请在浏览器中粘贴访问',
|
uni.setClipboardData({ data: serviceUrl });
|
||||||
showCancel: false
|
uni.showToast({ title: '客服链接已复制', icon: 'none' });
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// #endif
|
// #endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -358,103 +377,55 @@ export class CustomerService {
|
|||||||
this.fallbackToPhoneCall();
|
this.fallbackToPhoneCall();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 降级到电话客服
|
|
||||||
*/
|
|
||||||
fallbackToPhoneCall() {
|
|
||||||
uni.showModal({
|
|
||||||
title: '联系客服',
|
|
||||||
content: '在线客服暂时不可用,是否拨打电话联系客服?',
|
|
||||||
success: (res) => {
|
|
||||||
if (res.confirm) {
|
|
||||||
this.makePhoneCall();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 打开支付宝小程序客服
|
|
||||||
* @param {Object} config 客服配置
|
|
||||||
*/
|
|
||||||
openAliappService(config) {
|
openAliappService(config) {
|
||||||
console.log('支付宝小程序客服', config);
|
if (config.type === 'aikefu') {
|
||||||
switch (config.type) {
|
this.openDifyService();
|
||||||
case 'aikefu':
|
} else if (config.type === 'third') {
|
||||||
this.openDifyService();
|
this.openThirdService(config);
|
||||||
break;
|
} else {
|
||||||
case 'third':
|
// 支付宝原生客服由 button open-type="contact" 触发,此处不处理
|
||||||
this.openThirdService(config);
|
console.log('使用支付宝官方客服');
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.log('使用支付宝官方客服');
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ================== 辅助方法 ==================
|
||||||
* 拨打电话
|
|
||||||
*/
|
|
||||||
makePhoneCall() {
|
makePhoneCall() {
|
||||||
this.vm.$api.sendRequest({
|
this.vm.$api.sendRequest({
|
||||||
url: '/api/site/shopcontact',
|
url: '/api/site/shopcontact',
|
||||||
success: res => {
|
success: res => {
|
||||||
if (res.code === 0 && res.data?.mobile) {
|
if (res.code === 0 && res.data?.mobile) {
|
||||||
uni.makePhoneCall({
|
uni.makePhoneCall({ phoneNumber: res.data.mobile });
|
||||||
phoneNumber: res.data.mobile
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({ title: '暂无客服电话', icon: 'none' });
|
||||||
title: '暂无客服电话',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: () => {
|
fail: () => {
|
||||||
uni.showToast({
|
uni.showToast({ title: '获取客服电话失败', icon: 'none' });
|
||||||
title: '获取客服电话失败',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示无客服弹窗
|
|
||||||
*/
|
|
||||||
showNoServicePopup() {
|
showNoServicePopup() {
|
||||||
const siteInfo = this.vm.$store.state.siteInfo || {};
|
const siteInfo = this.vm.$store.state.siteInfo || {};
|
||||||
const message = siteInfo?.site_tel
|
const message = siteInfo?.site_tel
|
||||||
? `请联系客服,客服电话是${siteInfo.site_tel}`
|
? `请联系客服,客服电话是 ${siteInfo.site_tel}`
|
||||||
: '抱歉,商家暂无客服,请线下联系';
|
: '抱歉,商家暂无客服,请线下联系';
|
||||||
|
uni.showModal({ title: '联系客服', content: message, showCancel: false });
|
||||||
uni.showModal({
|
|
||||||
title: '联系客服',
|
|
||||||
content: message,
|
|
||||||
showCancel: false
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示配置错误弹窗
|
|
||||||
* @param {Array} errors 错误列表
|
|
||||||
*/
|
|
||||||
showConfigErrorPopup(errors) {
|
showConfigErrorPopup(errors) {
|
||||||
const message = errors.join('\n');
|
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '配置错误',
|
title: '客服配置错误',
|
||||||
content: `客服配置有误:\n${message}`,
|
content: `配置有误:\n${errors.join('\n')}`,
|
||||||
showCancel: false
|
showCancel: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 降级处理:使用原有客服方式
|
|
||||||
*/
|
|
||||||
fallbackToOriginalService() {
|
fallbackToOriginalService() {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '无法直接添加企业微信客服,是否使用其他方式联系客服?',
|
content: '无法直接添加企业微信,是否使用其他方式联系客服?',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
this.openWxworkService(true);
|
this.openWxworkService(true);
|
||||||
@@ -463,9 +434,19 @@ export class CustomerService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fallbackToPhoneCall() {
|
||||||
|
uni.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '在线客服不可用,是否拨打电话联系客服?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) this.makePhoneCall();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取客服按钮配置
|
* 获取按钮配置(用于 template 中 v-if / open-type 判断)
|
||||||
* @returns {Object} 按钮配置
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
getButtonConfig() {
|
getButtonConfig() {
|
||||||
const config = this.getPlatformConfig();
|
const config = this.getPlatformConfig();
|
||||||
@@ -473,11 +454,10 @@ export class CustomerService {
|
|||||||
|
|
||||||
let openType = '';
|
let openType = '';
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
if (config.type === 'weapp') {
|
if (config.type === 'weapp' && config.useOfficial !== false) {
|
||||||
openType = config.useOfficial !== false ? 'contact' : '';
|
openType = 'contact';
|
||||||
}
|
}
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifdef MP-ALIPAY
|
// #ifdef MP-ALIPAY
|
||||||
if (config.type === 'aliapp') openType = 'contact';
|
if (config.type === 'aliapp') openType = 'contact';
|
||||||
// #endif
|
// #endif
|
||||||
@@ -486,25 +466,238 @@ export class CustomerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断是否应该使用自定义客服处理
|
* 获取客服按钮图标
|
||||||
* @param {Object} config 客服配置
|
* @returns {String} 图标URL
|
||||||
* @returns {boolean} 是否使用自定义客服
|
|
||||||
*/
|
*/
|
||||||
shouldUseCustomService(config) {
|
getKefuButtonIcon() {
|
||||||
// #ifdef MP-WEIXIN
|
const shopInfo = this.vm.shopInfo || {};
|
||||||
if (config?.type === 'weapp') {
|
const config = this.getPlatformConfig();
|
||||||
return config.useOfficial === false;
|
|
||||||
|
// 优先使用指定的智能客服头像
|
||||||
|
if (config?.type === 'aikefu' || config?.type === 'wxwork' || config?.type === 'qyweixin') {
|
||||||
|
return this.vm.$util.img(IMAGE_CONFIG.aiAvatar);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shopInfo.kefuimg || this.vm.$util?.getDefaultImage()?.kefu || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为微信官方客服
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
isWeappOfficialKefu() {
|
||||||
|
const config = this.getPlatformConfig();
|
||||||
|
return config?.type === 'weapp';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为智能客服
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
isAIKefu() {
|
||||||
|
const config = this.getPlatformConfig();
|
||||||
|
return config?.type === 'aikefu';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为企业微信客服
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
isWxworkKefu() {
|
||||||
|
const config = this.getPlatformConfig();
|
||||||
|
return config?.type === 'wxwork' || config?.type === 'qyweixin';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为第三方客服
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
isThirdKefu() {
|
||||||
|
const config = this.getPlatformConfig();
|
||||||
|
return config?.type === 'third';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否需要同时显示小程序系统客服
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
shouldShowExtraWeappSystemKefu() {
|
||||||
|
const config = this.getPlatformConfig();
|
||||||
|
return (config?.show_system_service === true || config?.show_system_service === '1') && config?.type !== 'weapp';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算容器高度
|
||||||
|
* @param {Boolean} isLanguageSwitchEnabled 是否启用语言切换
|
||||||
|
* @param {Boolean} showWeappSystemKefu 是否显示小程序系统客服
|
||||||
|
* @param {Boolean} fixBtnShow 是否显示按钮
|
||||||
|
* @returns {String} 高度值(px)
|
||||||
|
*/
|
||||||
|
getContainerHeight(isLanguageSwitchEnabled, showWeappSystemKefu, fixBtnShow) {
|
||||||
|
if (!fixBtnShow) return '160px';
|
||||||
|
|
||||||
|
let buttonCount = 1;
|
||||||
|
if (isLanguageSwitchEnabled) buttonCount++;
|
||||||
|
if (showWeappSystemKefu) buttonCount++;
|
||||||
|
|
||||||
|
const totalRpx = 94 * buttonCount - 14;
|
||||||
|
const pxValue = Math.round(totalRpx * 0.5);
|
||||||
|
|
||||||
|
return `${pxValue}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取未读消息数量
|
||||||
|
* @returns {Number}
|
||||||
|
*/
|
||||||
|
getUnreadCount() {
|
||||||
|
return this.vm.$store?.state?.aiUnreadCount || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拨打电话联系客服
|
||||||
|
* @param {String} phoneNumber 电话号码
|
||||||
|
*/
|
||||||
|
makePhoneCallByNumber(phoneNumber) {
|
||||||
|
if (phoneNumber) {
|
||||||
|
uni.makePhoneCall({ phoneNumber });
|
||||||
|
} else {
|
||||||
|
this.makePhoneCall();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开 AI 智能助手
|
||||||
|
*/
|
||||||
|
openAIChat() {
|
||||||
|
try {
|
||||||
|
if (typeof this.vm.setAiUnreadCount === 'function') {
|
||||||
|
this.vm.setAiUnreadCount(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const aiChatUrl = '/pages_tool/ai-chat/index';
|
||||||
|
|
||||||
|
uni.navigateTo({
|
||||||
|
url: aiChatUrl,
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('跳转 AI 客服失败:', err);
|
||||||
|
// #ifdef H5
|
||||||
|
window.location.href = aiChatUrl;
|
||||||
|
// #endif
|
||||||
|
uni.showToast({ title: '打开客服失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('跳转 AI 客服异常:', e);
|
||||||
|
uni.showToast({ title: '打开客服失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开客服选择对话框(预留方法)
|
||||||
|
*/
|
||||||
|
openCustomerSelectPopupDialog() {
|
||||||
|
uni.showToast({ title: '客服选择功能开发中', icon: 'none' });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开客服选择弹出层(ActionSheet)
|
||||||
|
* @param {Array} kefuList 客服列表
|
||||||
|
*/
|
||||||
|
openKefuSelectPopup(kefuList) {
|
||||||
|
const kefuNames = kefuList.map(item => item.name);
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: kefuNames,
|
||||||
|
success: (res) => {
|
||||||
|
const selectedKefu = kefuList[res.tapIndex];
|
||||||
|
const cs = createCustomerService(this.vm, selectedKefu);
|
||||||
|
|
||||||
|
if (selectedKefu.isOfficial) {
|
||||||
|
uni.openCustomerServiceConversation({
|
||||||
|
sessionFrom: 'weapp',
|
||||||
|
showMessageCard: true
|
||||||
|
});
|
||||||
|
} else if (selectedKefu.id === 'qyweixin-kefu') {
|
||||||
|
if (uni.getSystemInfoSync().platform === 'wechat') {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages_tool/qyweixin-kefu/index'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const qyweixinUrl = this.vm.shopInfo?.qyweixinUrl;
|
||||||
|
if (qyweixinUrl) {
|
||||||
|
window.location.href = qyweixinUrl;
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '企业微信客服未配置', icon: 'none' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cs.handleCustomerClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 核心方法:统一客服入口(带验证和配置检查)
|
||||||
|
* @param {Object} options 选项参数
|
||||||
|
*/
|
||||||
|
handleUnifiedKefuClick(options = {}) {
|
||||||
|
const validation = this.validateConfig();
|
||||||
|
|
||||||
|
console.log('【客服配置验证】', validation);
|
||||||
|
|
||||||
|
if (!validation.isValid) {
|
||||||
|
console.error('客服配置无效:', validation.errors);
|
||||||
|
uni.showToast({ title: '客服暂不可用', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validation.warnings.length > 0) {
|
||||||
|
console.warn('客服配置警告:', validation.warnings);
|
||||||
|
}
|
||||||
|
|
||||||
|
const platformConfig = this.getPlatformConfig();
|
||||||
|
console.log('【当前客服配置】', platformConfig);
|
||||||
|
|
||||||
|
// 检查企业微信配置
|
||||||
|
if (platformConfig.type === 'wxwork') {
|
||||||
|
const wxworkConfig = this.getWxworkConfig();
|
||||||
|
console.log('【企业微信配置】', wxworkConfig);
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
if (!wxworkConfig?.enable || !wxworkConfig?.contact_url) {
|
||||||
|
console.warn('企业微信配置不完整,使用原生客服');
|
||||||
|
uni.showToast({ title: '企业微信配置不完整', icon: 'none' });
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef H5
|
||||||
|
if (!wxworkConfig?.contact_url && !platformConfig.wxwork_url) {
|
||||||
|
console.error('企业微信链接未配置');
|
||||||
|
uni.showToast({ title: '企业微信链接未配置', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接调用统一处理方法,由 CustomerService 内部根据配置路由
|
||||||
|
try {
|
||||||
|
this.handleCustomerClick({
|
||||||
|
sendMessageTitle: options.sendMessageTitle || '来自悬浮按钮的咨询',
|
||||||
|
sendMessagePath: options.sendMessagePath || '/pages/index/index'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('客服处理失败:', error);
|
||||||
|
uni.showToast({ title: '打开客服失败', icon: 'none' });
|
||||||
}
|
}
|
||||||
// #endif
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建客服服务实例
|
* 创建客服服务实例
|
||||||
* @param {Object} vueInstance Vue实例
|
* @param {Object} vueInstance Vue 实例(通常是 this)
|
||||||
* @param {Object} externalConfig 外部最新配置
|
* @param {Object} externalConfig 可选:外部传入的最新配置(如从 DIY 数据中提取)
|
||||||
* @returns {CustomerService} 客服服务实例
|
* @returns {CustomerService}
|
||||||
*/
|
*/
|
||||||
export function createCustomerService(vueInstance, externalConfig = null) {
|
export function createCustomerService(vueInstance, externalConfig = null) {
|
||||||
return new CustomerService(vueInstance, externalConfig);
|
return new CustomerService(vueInstance, externalConfig);
|
||||||
|
|||||||
@@ -609,10 +609,10 @@ export default {
|
|||||||
},
|
},
|
||||||
// 分享给好友
|
// 分享给好友
|
||||||
onShareAppMessage() {
|
onShareAppMessage() {
|
||||||
return this.mpShareData.appMessage;
|
return this.mpShareData?.appMessage;
|
||||||
},
|
},
|
||||||
// 分享到朋友圈
|
// 分享到朋友圈
|
||||||
onShareTimeline() {
|
onShareTimeline() {
|
||||||
return this.mpShareData.timeLine;
|
return this.mpShareData?.timeLine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
// 是否是英文环境
|
// 是否是英文环境
|
||||||
isEnEnv() {
|
isEnEnv() {
|
||||||
return uni.getStorageSync('lang') === 'en-us';
|
return this.$langConfig.getCurrentLocale() === 'en-us';
|
||||||
},
|
},
|
||||||
themeStyle() {
|
themeStyle() {
|
||||||
return this.$store.state.themeStyle;
|
return this.$store.state.themeStyle;
|
||||||
@@ -65,10 +65,18 @@ export default {
|
|||||||
componentRefresh() {
|
componentRefresh() {
|
||||||
return this.$store.state.componentRefresh;
|
return this.$store.state.componentRefresh;
|
||||||
},
|
},
|
||||||
|
// AI客服配置
|
||||||
|
globalAIKefuConfig() {
|
||||||
|
return this.$store.state.globalAIKefuConfig;
|
||||||
|
},
|
||||||
// 客服配置
|
// 客服配置
|
||||||
servicerConfig() {
|
servicerConfig() {
|
||||||
return this.$store.state.servicerConfig;
|
return this.$store.state.servicerConfig;
|
||||||
},
|
},
|
||||||
|
// 企业微信配置
|
||||||
|
wxworkConfig() {
|
||||||
|
return this.$store.state.wxworkConfig;
|
||||||
|
},
|
||||||
diySeckillInterval() {
|
diySeckillInterval() {
|
||||||
return this.$store.state.diySeckillInterval;
|
return this.$store.state.diySeckillInterval;
|
||||||
},
|
},
|
||||||
@@ -145,7 +153,7 @@ export default {
|
|||||||
if (isJump && route != 'pages/index/index') {
|
if (isJump && route != 'pages/index/index') {
|
||||||
uni.setStorageSync('manual_change_store', true); // 手动切换门店
|
uni.setStorageSync('manual_change_store', true); // 手动切换门店
|
||||||
this.$store.dispatch('getCartNumber'); //重新获取购物车数据
|
this.$store.dispatch('getCartNumber'); //重新获取购物车数据
|
||||||
this.$util.redirectTo('/pages/index/index');
|
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ export default {
|
|||||||
},
|
},
|
||||||
goHome() {
|
goHome() {
|
||||||
if (this.preview) return; // 开启预览,禁止任何操作和跳转
|
if (this.preview) return; // 开启预览,禁止任何操作和跳转
|
||||||
this.$util.redirectTo('/pages/index/index');
|
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||||||
},
|
},
|
||||||
goCart() {
|
goCart() {
|
||||||
if (this.preview) return; // 开启预览,禁止任何操作和跳转
|
if (this.preview) return; // 开启预览,禁止任何操作和跳转
|
||||||
|
|||||||
15
common/js/image-config.js
Normal file
15
common/js/image-config.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* 图片配置文件
|
||||||
|
* 将大图片上传到服务器后,请在此处配置网络URL
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 替换为你上传后的实际图片URL
|
||||||
|
const IMAGE_CONFIG = {
|
||||||
|
// AI客服头像
|
||||||
|
aiAvatar: 'public/static/img/s-agent.jpg',
|
||||||
|
|
||||||
|
// 用户默认头像
|
||||||
|
userAvatar: 'public/static/img/s-user.jpg'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IMAGE_CONFIG;
|
||||||
@@ -54,27 +54,40 @@ function loadLangPackSync(lang, path) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCurrentLocale() {
|
||||||
|
return uni.getStorageSync('lang') || "zh-cn";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
langList: langConfig.langList,
|
langList: langConfig.langList,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前本地语言
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getCurrentLocale,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* * 解析多语言
|
* * 解析多语言
|
||||||
* @param {Object} field
|
* @param {Object} field
|
||||||
*/
|
*/
|
||||||
lang(field) {
|
lang(field) {
|
||||||
let _this = getCurrentPages()[getCurrentPages().length - 1];
|
let _page = getCurrentPages()[getCurrentPages().length - 1];
|
||||||
if (!_this) return;
|
if (!_page) return;
|
||||||
|
|
||||||
const locale = uni.getStorageSync('lang') || "zh-cn"; //设置语言
|
const locale = getCurrentLocale(); // 获得当前本地语言
|
||||||
|
|
||||||
|
let value = ''; // 存放解析后的语言值
|
||||||
|
let langPath = ''; // 存放当前页面语言包路径
|
||||||
|
|
||||||
var value = '';
|
|
||||||
try {
|
try {
|
||||||
//公共语言包(同步加载)
|
//公共语言包(同步加载)
|
||||||
var lang = loadLangPackSync(locale, 'common');
|
var lang = loadLangPackSync(locale, 'common');
|
||||||
|
|
||||||
//当前页面语言包(同步加载)
|
//当前页面语言包(同步加载)
|
||||||
let route = _this.route;
|
let route = _page.route;
|
||||||
let langPath = processRoutePath(route);
|
langPath = processRoutePath(route);
|
||||||
console.log(`当前语言: ${locale}, 当前页面语言包路径: ${langPath}`);
|
|
||||||
|
|
||||||
// 加载当前页面语言包
|
// 加载当前页面语言包
|
||||||
let currentPageLang = loadLangPackSync(locale, langPath);
|
let currentPageLang = loadLangPackSync(locale, langPath);
|
||||||
@@ -102,7 +115,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('解析语言包失败:', e);
|
console.error('解析语言包失败:', e, { langPath, field, locale });
|
||||||
value = field;
|
value = field;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +128,10 @@ export default {
|
|||||||
if (value == undefined || (value == 'title' && field == 'title')) value = ''; // field
|
if (value == undefined || (value == 'title' && field == 'title')) value = ''; // field
|
||||||
|
|
||||||
// 多语言调试,注释后可以关闭控制台输出
|
// 多语言调试,注释后可以关闭控制台输出
|
||||||
console.log(`字段: ${field}, 值: ${value}`)
|
if (field == value) {
|
||||||
|
console.warn(`警告: 字段 ${field} 在语言包 ${langPath} 中未找到对应值,使用默认值 ${field} 当前语言: ${locale}`);
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -124,11 +140,11 @@ export default {
|
|||||||
* @param {String} url 切换后跳转的页面url
|
* @param {String} url 切换后跳转的页面url
|
||||||
*/
|
*/
|
||||||
change(value, url = '/pages_tool/member/index') {
|
change(value, url = '/pages_tool/member/index') {
|
||||||
let _this = getCurrentPages()[getCurrentPages().length - 1];
|
let _page = getCurrentPages()[getCurrentPages().length - 1];
|
||||||
if (!_this) return;
|
if (!_page) return;
|
||||||
|
|
||||||
uni.setStorageSync("lang", value);
|
uni.setStorageSync("lang", value);
|
||||||
const locale = uni.getStorageSync('lang') || "zh-cn"; //设置语言
|
const locale = getCurrentLocale();
|
||||||
|
|
||||||
// 清空已加载的语言包缓存
|
// 清空已加载的语言包缓存
|
||||||
for (let key in loadedLangPacks) {
|
for (let key in loadedLangPacks) {
|
||||||
@@ -145,9 +161,10 @@ export default {
|
|||||||
},
|
},
|
||||||
//刷新标题、tabbar
|
//刷新标题、tabbar
|
||||||
refresh() {
|
refresh() {
|
||||||
let _this = getCurrentPages()[getCurrentPages().length - 1];
|
let _page = getCurrentPages()[getCurrentPages().length - 1];
|
||||||
if (!_this) return;
|
if (!_page) return;
|
||||||
const locale = uni.getStorageSync('lang') || "zh-cn"; //设置语言
|
|
||||||
|
const locale = getCurrentLocale();
|
||||||
|
|
||||||
this.title(this.lang("title"));
|
this.title(this.lang("title"));
|
||||||
|
|
||||||
|
|||||||
100
common/js/share.js
Normal file
100
common/js/share.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// 文件分享工具函数
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 生成文件预览链接
|
||||||
|
* @param {Object} file - 文件对象,包含 name 和 url 属性
|
||||||
|
* @returns {string} - 生成的文件预览链接
|
||||||
|
*/
|
||||||
|
generateFilePreviewUrl(file) {
|
||||||
|
// 推断文件类型
|
||||||
|
let fileType = '';
|
||||||
|
if (file.name) {
|
||||||
|
const ext = file.name.split('.').pop().toLowerCase();
|
||||||
|
if (['pdf'].includes(ext)) {
|
||||||
|
fileType = 'pdf';
|
||||||
|
} else if (['doc', 'docx'].includes(ext)) {
|
||||||
|
fileType = 'word';
|
||||||
|
} else if (['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv'].includes(ext)) {
|
||||||
|
fileType = 'video';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建文件预览链接
|
||||||
|
const previewUrl = `/pages_tool/file-preview/file-preview?fileName=${encodeURIComponent(file.name)}&fileUrl=${encodeURIComponent(file.url || '')}&fileType=${encodeURIComponent(fileType)}`;
|
||||||
|
return previewUrl;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分享文件
|
||||||
|
* @param {Object} file - 文件对象,包含 name 和 url 属性
|
||||||
|
* @param {string} title - 分享标题,默认使用文件名
|
||||||
|
* @param {string} desc - 分享描述,默认使用固定文案
|
||||||
|
*/
|
||||||
|
shareFile(file, title = file.name, desc = '查看企业文件:' + file.name) {
|
||||||
|
// 生成文件预览链接
|
||||||
|
const filePreviewUrl = this.generateFilePreviewUrl(file);
|
||||||
|
|
||||||
|
// #ifdef H5
|
||||||
|
// H5 平台分享
|
||||||
|
if (navigator.share) {
|
||||||
|
// 使用 Web Share API
|
||||||
|
navigator.share({
|
||||||
|
title: title,
|
||||||
|
text: desc,
|
||||||
|
url: filePreviewUrl
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('分享失败:', err);
|
||||||
|
uni.showToast({ title: '分享失败', icon: 'none' });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 不支持 Web Share API 的浏览器
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: filePreviewUrl,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({ title: '链接已复制,请粘贴分享', icon: 'success' });
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
uni.showToast({ title: '复制失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
// 微信小程序分享
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: ['发送给朋友', '分享到朋友圈'],
|
||||||
|
success: (res) => {
|
||||||
|
if (res.tapIndex === 0) {
|
||||||
|
// 发送给朋友
|
||||||
|
uni.shareAppMessage({
|
||||||
|
title: title,
|
||||||
|
path: filePreviewUrl,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({ title: '分享成功', icon: 'success' });
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
uni.showToast({ title: '分享失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (res.tapIndex === 1) {
|
||||||
|
// 分享到朋友圈
|
||||||
|
uni.shareTimeline({
|
||||||
|
title: desc,
|
||||||
|
path: filePreviewUrl,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({ title: '分享成功', icon: 'success' });
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
uni.showToast({ title: '分享失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
// 用户取消分享
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
};
|
||||||
148
common/js/uniapp.utils.js
Normal file
148
common/js/uniapp.utils.js
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
/**
|
||||||
|
* 将常用的Uniapp提供的函数,存放到这里,按需引用
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示错误信息
|
||||||
|
* @param {Exception} err
|
||||||
|
* @param {Boolean} useModal
|
||||||
|
*/
|
||||||
|
const showError = (err, useModal = false) => {
|
||||||
|
const content = err?.message || err?.errMsg || err?.toString();
|
||||||
|
if (!useModal) {
|
||||||
|
uni.showToast({
|
||||||
|
title: content,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.showModal({
|
||||||
|
title: '错误提示',
|
||||||
|
content,
|
||||||
|
showCancel: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打电话
|
||||||
|
* @param {string} mobile 电话号码
|
||||||
|
*/
|
||||||
|
export const makePhoneCall = (mobile) => {
|
||||||
|
try {
|
||||||
|
uni.makePhoneCall({
|
||||||
|
phoneNumber: `${mobile}`,
|
||||||
|
success(e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
showError(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拷贝文本(返回 Promise)
|
||||||
|
* @param {*} text
|
||||||
|
* @param {*} options
|
||||||
|
* @returns {Promise} 返回 Promise,成功时 resolve,失败时 reject
|
||||||
|
*/
|
||||||
|
export const copyTextAsync = (text, { copySuccess = '', copyFailed = '' } = {}) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// 输入验证
|
||||||
|
if (!text && text !== '') {
|
||||||
|
const error = new Error('复制文本不能为空');
|
||||||
|
showError(error);
|
||||||
|
reject(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 超时监测
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
let error = new Error('复制操作长时间无响应,请检查相关权限及配置是否正确');
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
error = new Error([
|
||||||
|
'复制操作长时间无响应!',
|
||||||
|
'原因:',
|
||||||
|
'1.微信平台->用户隐私保护指引->"剪贴板"功能未添加或审核未通过;',
|
||||||
|
'2.微信平台对剪贴板API调用频率有限制'
|
||||||
|
].join('\r\n'));
|
||||||
|
// #endif
|
||||||
|
showError(error, true);
|
||||||
|
reject(error);
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
try {
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: `${text}`,
|
||||||
|
success: (res) => {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (copySuccess) {
|
||||||
|
uni.showToast({
|
||||||
|
title: copySuccess,
|
||||||
|
icon: 'success',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(res);
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
try {
|
||||||
|
uni.showToast({
|
||||||
|
title: err.message || err.errMsg || copyFailed || '复制失败',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
showError(e);
|
||||||
|
}
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
showError(err);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拷贝文本(回调形式,兼容旧代码)
|
||||||
|
* @param {*} text
|
||||||
|
* @param {*} options
|
||||||
|
* @param {Function} callback 回调函数,接收 (success, error) 参数
|
||||||
|
*/
|
||||||
|
export const copyText = (text, options = {}, callback) => {
|
||||||
|
copyTextAsync(text, options)
|
||||||
|
.then(res => {
|
||||||
|
if (callback) callback(true, null);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
if (callback) callback(false, err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开定位
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
export const openLocation = ({ latitude, longitude, name } = {}) => {
|
||||||
|
try {
|
||||||
|
uni.openLocation({
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
showError(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,15 +7,20 @@ import {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 首页路径
|
// 关于页面路径,使用常量导出
|
||||||
export const INDEX_PAGE_URL = '/pages/index/index';
|
export const INDEX_PAGE_URL = '/pages/index/index';
|
||||||
|
export const CATEGORY_PAGE_URL = '/pages_goods/category';
|
||||||
|
export const CONTACT_PAGE_URL = '/pages_tool/contact/contact';
|
||||||
|
export const MEMBER_PAGE_URL = '/pages_tool/member/index';
|
||||||
|
export const LOGIN_PAGE_URL = '/pages_tool/login/login';
|
||||||
|
export const AI_CHAT_PAGE_URL = '/pages_tool/ai-chat/index';
|
||||||
|
|
||||||
// 当前最新的tabBar.list (参见pages.json 中的tabBar.list 配置)
|
// 当前最新的tabBar.list (参见pages.json 中的tabBar.list 配置)
|
||||||
export const systemTabBarList = [
|
export const systemTabBarList = [
|
||||||
INDEX_PAGE_URL,
|
INDEX_PAGE_URL,
|
||||||
'/pages_goods/category',
|
CATEGORY_PAGE_URL,
|
||||||
'/pages_tool/contact/contact',
|
CONTACT_PAGE_URL,
|
||||||
'/pages_tool/member/index'
|
MEMBER_PAGE_URL
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,7 +94,7 @@ export const checkTabBarActive = (linkUrl, currentPageRoute) => {
|
|||||||
const linkUrlParts = adaptSubpackageUrl(linkUrl).split('/');
|
const linkUrlParts = adaptSubpackageUrl(linkUrl).split('/');
|
||||||
const currentPageRouteParts = currentPageRoute.split('/');
|
const currentPageRouteParts = currentPageRoute.split('/');
|
||||||
|
|
||||||
console.log('diy-bottom-nav verify:', { linkUrlParts, currentPageRouteParts});
|
// console.log('diy-bottom-nav verify:', { linkUrlParts, currentPageRouteParts});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 二级页面
|
// 二级页面
|
||||||
@@ -109,17 +114,25 @@ export const checkTabBarActive = (linkUrl, currentPageRoute) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
/** 导出页面URL常量 */
|
||||||
|
MEMBER_PAGE_URL,
|
||||||
|
CATEGORY_PAGE_URL,
|
||||||
|
CONTACT_PAGE_URL,
|
||||||
|
INDEX_PAGE_URL,
|
||||||
|
LOGIN_PAGE_URL,
|
||||||
|
AI_CHAT_PAGE_URL,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 页面跳转
|
* 页面跳转
|
||||||
* @param {string} to 跳转链接 /pages/idnex/index
|
* @param {string} to 跳转链接 /pages/idnex/index
|
||||||
* @param {Object} param 参数 {key : value, ...}
|
* @param {Object} param 参数 {key : value, ...}, 默认{}
|
||||||
* @param {string} mode 模式
|
* @param {string} mode 模式 navigateTo、redirectTo、reLaunch、tabbar, 默认navigateTo
|
||||||
*/
|
*/
|
||||||
redirectTo(to, param, mode) {
|
redirectTo(to, param = {}, mode = 'navigateTo') {
|
||||||
let url = to;
|
let url = to;
|
||||||
|
|
||||||
// 替换url中的前缀
|
// 替换url中的前缀
|
||||||
console.log('redirectTo', to, param, mode);
|
console.log('页面跳转 redirectTo', to, param, mode);
|
||||||
url = adaptSubpackageUrl(url);
|
url = adaptSubpackageUrl(url);
|
||||||
console.log('adaptSubpackageUrl', url);
|
console.log('adaptSubpackageUrl', url);
|
||||||
|
|
||||||
@@ -146,26 +159,26 @@ export default {
|
|||||||
// 如果url不包含tabBarList中的路径,根据mode判断跳转方式
|
// 如果url不包含tabBarList中的路径,根据mode判断跳转方式
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'tabbar':
|
case 'tabbar':
|
||||||
// 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。
|
// 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。重置页面栈,仅保留 tabBar 页面
|
||||||
uni.switchTab({
|
uni.switchTab({
|
||||||
url
|
url
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'redirectTo':
|
case 'redirectTo':
|
||||||
// 关闭当前页面,跳转到应用内的某个页面。
|
// 关闭当前页面,跳转到应用内的某个页面。替换栈顶页面,长度不变(上限10层)
|
||||||
uni.redirectTo({
|
uni.redirectTo({
|
||||||
url
|
url
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'reLaunch':
|
case 'reLaunch':
|
||||||
// 关闭所有页面,打开到应用内的某个页面。
|
// 关闭所有页面,打开到应用内的某个页面。清空栈后打开新页面,长度=1
|
||||||
uni.reLaunch({
|
uni.reLaunch({
|
||||||
url
|
url
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// 保留当前页面,跳转到应用内的某个页面
|
// 保留当前页面,跳转到应用内的某个页面,新增页面到栈顶,长度+1(上限10层)
|
||||||
console.log('保留当前页面,跳转到应用内的某个页面', url);
|
console.log('保留当前页面,navigateTo跳转到应用内的某个页面', url);
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url
|
url
|
||||||
});
|
});
|
||||||
@@ -592,6 +605,36 @@ export default {
|
|||||||
}
|
}
|
||||||
return cloneObj
|
return cloneObj
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开微信企业客服
|
||||||
|
* @param {Function} fallbackFunc 降级处理函数
|
||||||
|
*/
|
||||||
|
openWxWorkServiceChat(fallbackFunc) {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
let wxworkConfig = store.state?.wxworkConfig
|
||||||
|
if (wxworkConfig?.enable && wxworkConfig?.contact_url) {
|
||||||
|
// 直接使用活码链接跳转
|
||||||
|
wx.navigateToMiniProgram({
|
||||||
|
appId: 'wxeb490c6f9b154ef9', // 是企业微信官方小程序的AppID(固定值, 由腾讯企业微信团队维护, 不需要修改,用于展示"添加企业微信联系人"的官方页面)
|
||||||
|
path: `pages/contacts/externalContactDetail?url=${encodeURIComponent(wxworkConfig?.contact_url)}`,
|
||||||
|
success: () => {
|
||||||
|
console.log('跳转企业微信成功');
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('跳转企业微信失败:', err);
|
||||||
|
// 降级处理:使用原有客服方式
|
||||||
|
fallbackFunc && fallbackFunc();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fallbackFunc && fallbackFunc();
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义模板的跳转链接
|
* 自定义模板的跳转链接
|
||||||
* @param {Object} link
|
* @param {Object} link
|
||||||
@@ -614,7 +657,6 @@ export default {
|
|||||||
|
|
||||||
} else if (link.appid) {
|
} else if (link.appid) {
|
||||||
// 跳转其他小程序
|
// 跳转其他小程序
|
||||||
|
|
||||||
uni.navigateToMiniProgram({
|
uni.navigateToMiniProgram({
|
||||||
appId: link.appid,
|
appId: link.appid,
|
||||||
path: link.page
|
path: link.page
|
||||||
@@ -622,7 +664,6 @@ export default {
|
|||||||
|
|
||||||
} else if (link.name == 'MOBILE' && !link.wap_url) {
|
} else if (link.name == 'MOBILE' && !link.wap_url) {
|
||||||
// 拨打电话
|
// 拨打电话
|
||||||
|
|
||||||
uni.makePhoneCall({
|
uni.makePhoneCall({
|
||||||
phoneNumber: link.mobile,
|
phoneNumber: link.mobile,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
@@ -655,16 +696,19 @@ export default {
|
|||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
wx.openCustomerServiceChat({
|
this.openWxWorkServiceChat(() => {
|
||||||
extInfo: {
|
wx.openCustomerServiceChat({
|
||||||
url: config.wxwork_url
|
extInfo: {
|
||||||
},
|
url: config.wxwork_url
|
||||||
corpId: config.corpid,
|
},
|
||||||
showMessageCard: true,
|
corpId: config.corpid,
|
||||||
sendMessageTitle: 'this.sendMessageTitle',
|
showMessageCard: true,
|
||||||
sendMessagePath: 'this.sendMessagePath',
|
sendMessageTitle: 'this.sendMessageTitle',
|
||||||
sendMessageImg: 'this.sendMessageImg'
|
sendMessagePath: 'this.sendMessagePath',
|
||||||
|
sendMessageImg: 'this.sendMessageImg'
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// #endif
|
// #endif
|
||||||
break;
|
break;
|
||||||
case 'third':
|
case 'third':
|
||||||
@@ -792,13 +836,22 @@ export default {
|
|||||||
let routes = getCurrentPages(); // 获取当前打开过的页面路由数组
|
let routes = getCurrentPages(); // 获取当前打开过的页面路由数组
|
||||||
return routes.length ? routes[routes.length - 1].route : '';
|
return routes.length ? routes[routes.length - 1].route : '';
|
||||||
},
|
},
|
||||||
goBack(backUrl = '/pages/index/index') {
|
goBack(backUrl = INDEX_PAGE_URL) {
|
||||||
if (getCurrentPages().length == 1) {
|
if (getCurrentPages().length == 1) {
|
||||||
this.redirectTo(backUrl);
|
this.redirectTo(backUrl);
|
||||||
} else {
|
} else {
|
||||||
uni.navigateBack();
|
uni.navigateBack();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 隐藏“返回首页/小房子”按钮
|
||||||
|
* 这个函数,用到页面show, onshow 的生命周期时
|
||||||
|
*/
|
||||||
|
hideHomeButton() {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
wx.hideHomeButton();
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param val 转化时间字符串 (转化时分秒)
|
* @param val 转化时间字符串 (转化时分秒)
|
||||||
@@ -1148,5 +1201,74 @@ export default {
|
|||||||
verifyMobile(mobile) {
|
verifyMobile(mobile) {
|
||||||
var parse = /^\d{11}$/.test(mobile);
|
var parse = /^\d{11}$/.test(mobile);
|
||||||
return parse;
|
return parse;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 判断是否为快应用环境
|
||||||
|
* @returns {boolean} 是否为快应用
|
||||||
|
*/
|
||||||
|
isQuickApp() {
|
||||||
|
// #ifndef QUICKAPP-WEBVIEW
|
||||||
|
let _isQuickApp = false;
|
||||||
|
try {
|
||||||
|
const ua = navigator?.userAgent?.toLowerCase();
|
||||||
|
// console.log('ua = ', ua);
|
||||||
|
_isQuickApp = ua.indexOf('quickapp') !== -1;
|
||||||
|
|
||||||
|
if (!_isQuickApp) {
|
||||||
|
// 特殊说明由于网站是嵌入到快应用的Webview组件中<web>,UA中不会包含quickapp字符串,
|
||||||
|
// 但是会包含uniacid参数,所以这里通过uniacid参数来判断是否为快应用环境
|
||||||
|
_isQuickApp = ua.indexOf('uniacid=') !== -1;
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
|
||||||
|
if (!_isQuickApp) {
|
||||||
|
const systemInfo = this.getDeviceInfo();
|
||||||
|
_isQuickApp = systemInfo?.platform === 'quickapp' || process?.env?.UNI_PLATFORM === 'quickapp';
|
||||||
|
}
|
||||||
|
return _isQuickApp;
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 判断是否为华为快应用环境
|
||||||
|
* @returns {boolean} 是否为华为快应用
|
||||||
|
*/
|
||||||
|
isHuaweiQuickApp() {
|
||||||
|
if (!this.isQuickApp()) return false;
|
||||||
|
|
||||||
|
// #ifndef QUICKAPP-WEBVIEW-HUAWEI
|
||||||
|
const systemInfo = this.getDeviceInfo();
|
||||||
|
return systemInfo.brand === 'HUAWEI' || systemInfo.manufacturer === 'HUAWEI' ||
|
||||||
|
(typeof qh !== 'undefined' && qh.platform === 'huawei');
|
||||||
|
// #endif
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 蛇形命名转驼峰命名
|
||||||
|
* @param {string} str 蛇形命名字符串
|
||||||
|
* @returns {string} 驼峰命名字符串
|
||||||
|
*/
|
||||||
|
snakeToCamel(str) {
|
||||||
|
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 蛇形命名转驼峰命名(递归处理对象中的所有属性名)
|
||||||
|
* @param {Object} obj 包含蛇形命名字符串属性的对象
|
||||||
|
* @returns {Object} 驼峰命名字符串属性的对象
|
||||||
|
*/
|
||||||
|
snakeToCamelForObj(obj) {
|
||||||
|
if (typeof obj !== 'object' || obj === null) return obj;
|
||||||
|
if (Array.isArray(obj)) return obj.map(this.snakeToCamelForObj.bind(this));
|
||||||
|
const newObj = {};
|
||||||
|
for (const key in obj) {
|
||||||
|
if (Object.hasOwnProperty.call(obj, key)) {
|
||||||
|
const newKey = this.snakeToCamel(key);
|
||||||
|
newObj[newKey] = this.snakeToCamelForObj(obj[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newObj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
143
components-diy/css/common-channel.scss
Normal file
143
components-diy/css/common-channel.scss
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
// 公共微信视频号样式
|
||||||
|
|
||||||
|
// CSS 变量
|
||||||
|
:root {
|
||||||
|
// 尺寸变量
|
||||||
|
--channel-play-btn-size: 80rpx;
|
||||||
|
--channel-play-btn-small-size: 60rpx;
|
||||||
|
--channel-play-btn-icon-size: 40rpx;
|
||||||
|
--channel-play-btn-icon-small-size: 30rpx;
|
||||||
|
--channel-border-radius: 12rpx;
|
||||||
|
--channel-stats-padding: 10rpx;
|
||||||
|
--channel-info-wrap-padding: 10rpx 0;
|
||||||
|
|
||||||
|
// 字体变量
|
||||||
|
--channel-name-font-size: 28rpx;
|
||||||
|
--video-title-font-size: 28rpx;
|
||||||
|
--video-title-small-font-size: 24rpx;
|
||||||
|
--video-stats-font-size: 24rpx;
|
||||||
|
--channel-stats-font-size: 20rpx;
|
||||||
|
|
||||||
|
// 颜色变量
|
||||||
|
--channel-name-color: #333;
|
||||||
|
--video-title-color: #333;
|
||||||
|
--video-title-small-color: #666;
|
||||||
|
--video-stats-color: #999;
|
||||||
|
--channel-stats-color: #fff;
|
||||||
|
--channel-border-color: #f0f0f0;
|
||||||
|
--channel-play-btn-bg: rgba(0, 0, 0, 0.4);
|
||||||
|
--channel-stats-bg: linear-gradient(transparent, rgba(0, 0, 0, 0.6));
|
||||||
|
|
||||||
|
// 间距变量
|
||||||
|
--channel-name-margin-bottom: 4rpx;
|
||||||
|
--video-title-margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式设计
|
||||||
|
@media (max-width: 375px) {
|
||||||
|
:root {
|
||||||
|
--channel-play-btn-size: 70rpx;
|
||||||
|
--channel-play-btn-small-size: 50rpx;
|
||||||
|
--channel-play-btn-icon-size: 35rpx;
|
||||||
|
--channel-play-btn-icon-small-size: 25rpx;
|
||||||
|
--channel-name-font-size: 24rpx;
|
||||||
|
--video-title-font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 750px) {
|
||||||
|
:root {
|
||||||
|
--channel-play-btn-size: 90rpx;
|
||||||
|
--channel-play-btn-small-size: 70rpx;
|
||||||
|
--channel-play-btn-icon-size: 45rpx;
|
||||||
|
--channel-play-btn-icon-small-size: 35rpx;
|
||||||
|
--channel-name-font-size: 32rpx;
|
||||||
|
--video-title-font-size: 32rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放按钮样式
|
||||||
|
.channel-play-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: var(--channel-play-btn-size);
|
||||||
|
height: var(--channel-play-btn-size);
|
||||||
|
background-color: var(--channel-play-btn-bg);
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
.play-icon {
|
||||||
|
width: var(--channel-play-btn-icon-size);
|
||||||
|
height: var(--channel-play-btn-icon-size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 小尺寸播放按钮(用于列表)
|
||||||
|
.channel-play-btn.small {
|
||||||
|
width: var(--channel-play-btn-small-size);
|
||||||
|
height: var(--channel-play-btn-small-size);
|
||||||
|
|
||||||
|
.play-icon {
|
||||||
|
width: var(--channel-play-btn-icon-small-size);
|
||||||
|
height: var(--channel-play-btn-icon-small-size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 视频统计信息
|
||||||
|
.channel-stats {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: var(--channel-stats-bg);
|
||||||
|
padding: var(--channel-stats-padding);
|
||||||
|
color: var(--channel-stats-color);
|
||||||
|
font-size: var(--channel-stats-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 视频信息容器
|
||||||
|
.channel-info-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
padding: var(--channel-info-wrap-padding);
|
||||||
|
|
||||||
|
.channel-name {
|
||||||
|
margin-bottom: var(--channel-name-margin-bottom);
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: var(--channel-name-font-size);
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--channel-name-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-title {
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: var(--video-title-small-font-size);
|
||||||
|
color: var(--video-title-small-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 视频标题
|
||||||
|
.video-title {
|
||||||
|
font-size: var(--video-title-font-size);
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--video-title-color);
|
||||||
|
margin-bottom: var(--video-title-margin-bottom);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 视频统计信息(非绝对定位版本)
|
||||||
|
.video-stats {
|
||||||
|
font-size: var(--video-stats-font-size);
|
||||||
|
color: var(--video-stats-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -3,11 +3,11 @@
|
|||||||
<view class="article-wrap" :style="warpCss">
|
<view class="article-wrap" :style="warpCss">
|
||||||
<view :class="['list-wrap', value.style]" :style="warpCss">
|
<view :class="['list-wrap', value.style]" :style="warpCss">
|
||||||
<!-- 自动垂直滚动 -->
|
<!-- 自动垂直滚动 -->
|
||||||
<swiper class="auto-scroll-swiper" :style="swiperStyle" :vertical="swiperConfig.vertical !== false"
|
<swiper class="auto-scroll-swiper" :style="[swiperStyle]" :vertical="swiperConfig.vertical !== false"
|
||||||
:autoplay="swiperConfig.autoplay !== false" :circular="swiperConfig.circular !== false"
|
:autoplay="swiperConfig.autoplay !== false" :circular="swiperConfig.circular !== false"
|
||||||
:interval="swiperConfig.interval || 3000" :duration="swiperConfig.duration || 500"
|
:interval="swiperConfig.interval || 3000" :duration="swiperConfig.duration || 500"
|
||||||
:display-multiple-items="safeDisplayMultipleItems">
|
:display-multiple-items="safeDisplayMultipleItems">
|
||||||
<swiper-item v-for="(item, index) in list" :key="index" @click="toDetail(item)">
|
<swiper-item v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)">
|
||||||
<view class="swiper-item-content">
|
<view class="swiper-item-content">
|
||||||
<view :class="['item', value.ornament.type]" :style="itemCss">
|
<view :class="['item', value.ornament.type]" :style="itemCss">
|
||||||
<view class="article-img">
|
<view class="article-img">
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<view class="time" :style="{ color: value.timecolor }">
|
<view class="time" :style="{ color: value.timecolor }">
|
||||||
{{ audiotime }}
|
{{ audiotime }}
|
||||||
</view>
|
</view>
|
||||||
<view @click="play()" class="start" :class="status ? 'iconj icon-07zanting' : 'iconj icon-bofang'"
|
<view @tap.stop="play()" class="start" :class="status ? 'iconj icon-07zanting' : 'iconj icon-bofang'"
|
||||||
style="padding-top: 18rpx"></view>
|
style="padding-top: 18rpx"></view>
|
||||||
</view>
|
</view>
|
||||||
<view class="fui-audio style3" :style="{ background: value.background }" v-else>
|
<view class="fui-audio style3" :style="{ background: value.background }" v-else>
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
<!-- {{audios[value.id].audiotime}} -->
|
<!-- {{audios[value.id].audiotime}} -->
|
||||||
{{ audiotime }}
|
{{ audiotime }}
|
||||||
</view>
|
</view>
|
||||||
<view @click="play()" class="start" :class="status ? 'iconj icon-07zanting' : 'iconj icon-bofang'"></view>
|
<view @tap.stop="play()" class="start" :class="status ? 'iconj icon-07zanting' : 'iconj icon-bofang'"></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
:style="{ color: value.titleStyle.textColor }">低至0元免费拿</view>
|
:style="{ color: value.titleStyle.textColor }">低至0元免费拿</view>
|
||||||
<view class="head-right"
|
<view class="head-right"
|
||||||
:style="{ fontSize: value.titleStyle.moreFontSize * 2 + 'rpx', color: value.titleStyle.moreColor }"
|
:style="{ fontSize: value.titleStyle.moreFontSize * 2 + 'rpx', color: value.titleStyle.moreColor }"
|
||||||
@click="$util.redirectTo('/pages_promotion/bargain/list')">
|
@tap.stop="$util.redirectTo('/pages_promotion/bargain/list')">
|
||||||
<text>{{ value.titleStyle.more }}</text>
|
<text>{{ value.titleStyle.more }}</text>
|
||||||
<text class="iconfont icon-right"></text>
|
<text class="iconfont icon-right"></text>
|
||||||
</view>
|
</view>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<!-- 商品列表 -->
|
<!-- 商品列表 -->
|
||||||
<template v-if="value.template == 'row1-of1'">
|
<template v-if="value.template == 'row1-of1'">
|
||||||
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
|
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
|
||||||
:class="[value.ornament.type]" :style="goodsItemCss">
|
:class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
|
|
||||||
<template v-if="value.template == 'horizontal-slide'">
|
<template v-if="value.template == 'horizontal-slide'">
|
||||||
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
|
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
|
||||||
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
|
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
|
||||||
:class="[value.ornament.type]" :style="goodsItemCss">
|
:class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
|
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
|
||||||
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
|
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
|
||||||
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
|
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
|
||||||
@click="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
|
@tap.stop="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
||||||
@@ -365,7 +365,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
/deep/.uni-scroll-view ::-webkit-scrollbar {
|
::v-deep .uni-scroll-view ::-webkit-scrollbar {
|
||||||
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
@@ -374,7 +374,7 @@ export default {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/::-webkit-scrollbar {
|
::v-deep ::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<view data-component-name="diy-bottom-nav" v-if="tabBarList && tabBarList.list">
|
<view data-component-name="diy-bottom-nav" v-if="tabBarList && tabBarList.list">
|
||||||
<view class="tab-bar" :style="{ backgroundColor: tabBarList.backgroundColor }">
|
<view class="tab-bar" :style="{ backgroundColor: tabBarList.backgroundColor }">
|
||||||
<view class="tabbar-border"></view>
|
<view class="tabbar-border"></view>
|
||||||
<view class="item" v-for="(item, index) in tabBarList.list" :key="item.id" @click="redirectTo(item.link)">
|
<view class="item" v-for="(item, index) in tabBarList.list" :key="item.id" @tap.stop="redirectTo(item.link)">
|
||||||
<view class="bd">
|
<view class="bd">
|
||||||
<block v-if="item.link.wap_url == '/pages_goods/cart'">
|
<block v-if="item.link.wap_url == '/pages_goods/cart'">
|
||||||
<view class="icon" v-if="tabBarList.type == 1 || tabBarList.type == 2"
|
<view class="icon" v-if="tabBarList.type == 1 || tabBarList.type == 2"
|
||||||
@@ -145,7 +145,7 @@ export default {
|
|||||||
let linkUrl = link.wap_url;
|
let linkUrl = link.wap_url;
|
||||||
|
|
||||||
// 首页特殊处理
|
// 首页特殊处理
|
||||||
if (linkUrl === '/pages/index/index' && this.name === 'DIY_VIEW_INDEX') {
|
if (linkUrl === this.$util.INDEX_PAGE_URL && this.name === 'DIY_VIEW_INDEX') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<view data-component-name="diy-category-item" class="item-wrap" :class="type">
|
<view data-component-name="diy-category-item" class="item-wrap" :class="type">
|
||||||
<block v-if="type == 'category' && category.child_list && category.child_list.length">
|
<block v-if="type == 'category' && category.child_list && category.child_list.length">
|
||||||
<view class="category-adv" v-if="category.image_adv" @click="diyRedirectTo(category.link_url)">
|
<view class="category-adv" v-if="category.image_adv" @tap.stop="diyRedirectTo(category.link_url)">
|
||||||
<image :src="$util.img(category.image_adv)" mode="widthFix" />
|
<image :src="$util.img(category.image_adv)" mode="widthFix" />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<view class="category-title">{{ category.category_name }}</view>
|
<view class="category-title">{{ category.category_name }}</view>
|
||||||
<view class="category-list">
|
<view class="category-list">
|
||||||
<view class="category-item" v-for="(one, oneIndex) in category.child_list" :key="oneIndex"
|
<view class="category-item" v-for="(one, oneIndex) in category.child_list" :key="oneIndex"
|
||||||
@click="$util.redirectTo('/pages_goods/list', { category_id: one.category_id })">
|
@tap.stop="$util.redirectTo('/pages_goods/list', { category_id: one.category_id })">
|
||||||
<view class="img-box">
|
<view class="img-box">
|
||||||
<image :src="$util.img(one.image)" mode="widthFix" />
|
<image :src="$util.img(one.image)" mode="widthFix" />
|
||||||
</view>
|
</view>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<view class="category-title">{{ one.category_name }}</view>
|
<view class="category-title">{{ one.category_name }}</view>
|
||||||
<view class="category-list">
|
<view class="category-list">
|
||||||
<view class="category-item" v-for="(two, twoIndex) in one.child_list" :key="twoIndex"
|
<view class="category-item" v-for="(two, twoIndex) in one.child_list" :key="twoIndex"
|
||||||
@click="$util.redirectTo('/pages_goods/list', { category_id: two.category_id })">
|
@tap.stop="$util.redirectTo('/pages_goods/list', { category_id: two.category_id })">
|
||||||
<view class="img-box">
|
<view class="img-box">
|
||||||
<image :src="$util.img(two.image)" mode="widthFix" :lazy-load="true" />
|
<image :src="$util.img(two.image)" mode="widthFix" :lazy-load="true" />
|
||||||
</view>
|
</view>
|
||||||
@@ -44,22 +44,22 @@
|
|||||||
:class="{ 'screen-category-4': value.template == 4 }" :scroll-with-animation="true"
|
:class="{ 'screen-category-4': value.template == 4 }" :scroll-with-animation="true"
|
||||||
:scroll-into-view="scrollIntoView">
|
:scroll-into-view="scrollIntoView">
|
||||||
<view class="item" id="category-2--1" :class="{ selected: categoryId == -1 }"
|
<view class="item" id="category-2--1" :class="{ selected: categoryId == -1 }"
|
||||||
@click="selectCategory(-1)">全部</view>
|
@tap.stop="selectCategory(-1)">全部</view>
|
||||||
<view class="item" :id="'category-2-' + oneIndex"
|
<view class="item" :id="'category-2-' + oneIndex"
|
||||||
:class="{ selected: categoryId == oneIndex }" @click="selectCategory(oneIndex)"
|
:class="{ selected: categoryId == oneIndex }" @tap.stop="selectCategory(oneIndex)"
|
||||||
v-for="(one, oneIndex) in category.child_list" :key="oneIndex">
|
v-for="(one, oneIndex) in category.child_list" :key="oneIndex">
|
||||||
{{ one.category_name }}
|
{{ one.category_name }}
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
<view class="iconfont icon-unfold" @click="$refs.screenCategoryPopup.open()"></view>
|
<view class="iconfont icon-unfold" @tap.stop="$refs.screenCategoryPopup.open()"></view>
|
||||||
</view>
|
</view>
|
||||||
<uni-popup type="top" ref="screenCategoryPopup">
|
<uni-popup type="top" ref="screenCategoryPopup">
|
||||||
<view class="screen-category-popup" @click="$refs.screenCategoryPopup.close()">
|
<view class="screen-category-popup" @tap.stop="$refs.screenCategoryPopup.close()">
|
||||||
<scroll-view scroll-y="true" class="screen-category"
|
<scroll-view scroll-y="true" class="screen-category"
|
||||||
:class="{ 'screen-category-4': value.template == 4 }">
|
:class="{ 'screen-category-4': value.template == 4 }">
|
||||||
<view class="title">全部</view>
|
<view class="title">全部</view>
|
||||||
<view class="item" :class="{ selected: categoryId == oneIndex }"
|
<view class="item" :class="{ selected: categoryId == oneIndex }"
|
||||||
@click="selectCategory(oneIndex)" v-for="(one, oneIndex) in category.child_list"
|
@tap.stop="selectCategory(oneIndex)" v-for="(one, oneIndex) in category.child_list"
|
||||||
:key="oneIndex">
|
:key="oneIndex">
|
||||||
{{ one.category_name }}
|
{{ one.category_name }}
|
||||||
</view>
|
</view>
|
||||||
@@ -81,13 +81,13 @@
|
|||||||
:data-template="value.template">
|
:data-template="value.template">
|
||||||
<block v-if="goodsList.length">
|
<block v-if="goodsList.length">
|
||||||
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
|
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
|
||||||
<view class="goods-img" @click="toDetail(item)">
|
<view class="goods-img" @tap.stop="toDetail(item)">
|
||||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" />
|
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" :show-menu-by-longpress="true" />
|
||||||
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}
|
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
<view class="info-wrap">
|
<view class="info-wrap">
|
||||||
<view class="name-wrap" @click="toDetail(item)">
|
<view class="name-wrap" @tap.stop="toDetail(item)">
|
||||||
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="price-wrap">
|
<view class="price-wrap">
|
||||||
@@ -99,9 +99,6 @@
|
|||||||
<text class="unit price-style small">.{{
|
<text class="unit price-style small">.{{
|
||||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||||
</block>
|
</block>
|
||||||
<block v-else>
|
|
||||||
<text class="unit price-style small">{{ $lang('Make') ? $lang('Make') : '询底价' }}</text>
|
|
||||||
</block>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="member-price-tag"
|
<view class="member-price-tag"
|
||||||
v-if="item.member_price && item.member_price == showPrice(item)">
|
v-if="item.member_price && item.member_price == showPrice(item)">
|
||||||
@@ -120,27 +117,33 @@
|
|||||||
<text>{{ showMarketPrice(item) }}</text>
|
<text>{{ showMarketPrice(item) }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="ai-chat-btn-wrap" v-if="item.isinformation == 1">
|
||||||
|
<view class="ai-chat-btn" @tap.stop="sendToAIChat(item)">
|
||||||
|
立即咨询
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- <view class="right-wrap" v-if="value.template == 2 || value.template == 4">
|
<!-- <view class="right-wrap" v-if="value.template == 2 || value.template == 4">
|
||||||
<block v-if="item.is_virtual">
|
<block v-if="item.is_virtual">
|
||||||
<view class="color-base-bg select-sku" @click="toDetail(item)">立即购买</view>
|
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
|
||||||
</block>
|
</block>
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<view v-if="item.goods_spec_format" class="color-base-bg select-sku" @click="selectSku(item)">
|
<view v-if="item.goods_spec_format" class="color-base-bg select-sku" @tap.stop="selectSku(item)">
|
||||||
<text>选规格</text>
|
<text>选规格</text>
|
||||||
<text class="num-tag" v-if="item.num">{{ item.num }}</text>
|
<text class="num-tag" v-if="item.num">{{ item.num }}</text>
|
||||||
</view>
|
</view>
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<block v-if="cartList['goods_' + item.goods_id]&&cartList['goods_' + item.goods_id]['sku_' + item.sku_id]">
|
<block v-if="cartList['goods_' + item.goods_id]&&cartList['goods_' + item.goods_id]['sku_' + item.sku_id]">
|
||||||
<view class="num-action reduce" @click="reduce(item)">
|
<view class="num-action reduce" @tap.stop="reduce(item)">
|
||||||
<text class="iconfont icon-jian"></text>
|
<text class="iconfont icon-jian"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="num">{{ cartList['goods_' + item.goods_id]['sku_' + item.sku_id].num }}</view>
|
<view class="num">{{ cartList['goods_' + item.goods_id]['sku_' + item.sku_id].num }}</view>
|
||||||
<view class="num-action" :id="'cart-num-' + index" @click="increase($event, item)">
|
<view class="num-action" :id="'cart-num-' + index" @tap.stop="increase($event, item)">
|
||||||
<text class="iconfont icon-jia"></text>
|
<text class="iconfont icon-jia"></text>
|
||||||
<view class="click-event"></view>
|
<view class="click-event"></view>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
<view class="num-action" v-else :id="'cart-num-' + index" @click="increase($event, item, 0)">
|
<view class="num-action" v-else :id="'cart-num-' + index" @tap.stop="increase($event, item, 0)">
|
||||||
<text class="iconfont icon-jia"></text>
|
<text class="iconfont icon-jia"></text>
|
||||||
<view class="click-event"></view>
|
<view class="click-event"></view>
|
||||||
</view>
|
</view>
|
||||||
@@ -148,7 +151,7 @@
|
|||||||
</block>
|
</block>
|
||||||
</view> -->
|
</view> -->
|
||||||
<!-- <view class="right-wrap" v-if="value.template == 3">
|
<!-- <view class="right-wrap" v-if="value.template == 3">
|
||||||
<view class="color-base-bg select-sku" @click="toDetail(item)">立即购买</view>
|
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
|
||||||
</view> -->
|
</view> -->
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -158,7 +161,7 @@
|
|||||||
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" />
|
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" />
|
||||||
</view>
|
</view>
|
||||||
<!-- <view class="end-tips" ref="endTips" v-if="last && (categoryId == -1 || !category.child_list || (category.child_list && categoryId == category.child_list.length - 1))">已经到底了~</view>
|
<!-- <view class="end-tips" ref="endTips" v-if="last && (categoryId == -1 || !category.child_list || (category.child_list && categoryId == category.child_list.length - 1))">已经到底了~</view>
|
||||||
<view class="end-tips" ref="endTips" v-else @click="switchCategory('next')">
|
<view class="end-tips" ref="endTips" v-else @tap.stop="switchCategory('next')">
|
||||||
<text class="iconfont icon-xiangshangzhanhang"></text>
|
<text class="iconfont icon-xiangshangzhanhang"></text>
|
||||||
上滑查看下一分类
|
上滑查看下一分类
|
||||||
</view> -->
|
</view> -->
|
||||||
@@ -173,29 +176,25 @@
|
|||||||
<view class="goods-list" :class="{ 'double-column': !isList, 'single-column': isList }"
|
<view class="goods-list" :class="{ 'double-column': !isList, 'single-column': isList }"
|
||||||
:data-template="value.template">
|
:data-template="value.template">
|
||||||
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
|
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
|
||||||
<view class="goods-img" @click="toDetail(item)">
|
<view class="goods-img" @tap.stop="toDetail(item)">
|
||||||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"
|
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"
|
||||||
:lazy-load="true" />
|
:lazy-load="true" :show-menu-by-longpress="true" />
|
||||||
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}</view>
|
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="info-wrap">
|
<view class="info-wrap">
|
||||||
<view class="name-wrap" @click="toDetail(item)">
|
<view class="name-wrap" @tap.stop="toDetail(item)">
|
||||||
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="price-wrap">
|
<view class="price-wrap">
|
||||||
<view class="discount-price">
|
<view class="discount-price">
|
||||||
<block v-if="item.isinformation == 0">
|
<block v-if="item.isinformation == 0">
|
||||||
<text class="unit price-style small">¥</text>
|
<text class="unit price-style small">¥</text>
|
||||||
<text class="price price-style large">{{
|
<text class="price price-style large">{{
|
||||||
parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
|
parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
|
||||||
<text class="unit price-style small">.{{
|
<text class="unit price-style small">.{{
|
||||||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||||||
</block>
|
</block>
|
||||||
<block v-else>
|
</view>
|
||||||
<text class="unit price-style small">{{ $lang('Make') ? $lang('Make') : '询底价' }}</text>
|
|
||||||
</block>
|
|
||||||
|
|
||||||
</view>
|
|
||||||
<view class="member-price-tag"
|
<view class="member-price-tag"
|
||||||
v-if="item.member_price && item.member_price == showPrice(item)">
|
v-if="item.member_price && item.member_price == showPrice(item)">
|
||||||
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix" />
|
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix" />
|
||||||
@@ -212,32 +211,38 @@
|
|||||||
<text>{{ showMarketPrice(item) }}</text>
|
<text>{{ showMarketPrice(item) }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="ai-chat-btn-wrap" v-if="item.isinformation == 1">
|
||||||
|
<view class="ai-chat-btn" @tap.stop="sendToAIChat(item)">
|
||||||
|
立即咨询
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="right-wrap" v-if="value.template == 2">
|
<view class="right-wrap" v-if="value.template == 2">
|
||||||
<block v-if="item.is_virtual">
|
<block v-if="item.is_virtual">
|
||||||
<view class="color-base-bg select-sku" @click="toDetail(item)">立即购买</view>
|
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
|
||||||
</block>
|
</block>
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<view v-if="item.goods_spec_format" class="color-base-bg select-sku"
|
<view v-if="item.goods_spec_format" class="color-base-bg select-sku"
|
||||||
@click="selectSku(item)">
|
@tap.stop="selectSku(item)">
|
||||||
<text>选规格</text>
|
<text>选规格</text>
|
||||||
<text class="num-tag" v-if="item.num">{{ item.num }}</text>
|
<text class="num-tag" v-if="item.num">{{ item.num }}</text>
|
||||||
</view>
|
</view>
|
||||||
<block v-else>
|
<block v-else>
|
||||||
<block
|
<block
|
||||||
v-if="cartList['goods_' + item.goods_id] && cartList['goods_' + item.goods_id]['sku_' + item.sku_id]">
|
v-if="cartList['goods_' + item.goods_id] && cartList['goods_' + item.goods_id]['sku_' + item.sku_id]">
|
||||||
<view class="num-action reduce" @click="reduce(item)">
|
<view class="num-action reduce" @tap.stop="reduce(item)">
|
||||||
<text class="iconfont icon-jian"></text>
|
<text class="iconfont icon-jian"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="num">{{ cartList['goods_' + item.goods_id]['sku_' +
|
<view class="num">{{ cartList['goods_' + item.goods_id]['sku_' +
|
||||||
item.sku_id].num }}</view>
|
item.sku_id].num }}</view>
|
||||||
<view class="num-action" :id="'cart-num-' + index"
|
<view class="num-action" :id="'cart-num-' + index"
|
||||||
@click="increase($event, item)">
|
@tap.stop="increase($event, item)">
|
||||||
<text class="iconfont icon-jia"></text>
|
<text class="iconfont icon-jia"></text>
|
||||||
<view class="click-event"></view>
|
<view class="click-event"></view>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
<view class="num-action" v-else :id="'cart-num-' + index"
|
<view class="num-action" v-else :id="'cart-num-' + index"
|
||||||
@click="increase($event, item, 0)">
|
@tap.stop="increase($event, item, 0)">
|
||||||
<text class="iconfont icon-jia"></text>
|
<text class="iconfont icon-jia"></text>
|
||||||
<view class="click-event"></view>
|
<view class="click-event"></view>
|
||||||
</view>
|
</view>
|
||||||
@@ -245,7 +250,7 @@
|
|||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
<view class="right-wrap" v-if="value.template == 3">
|
<view class="right-wrap" v-if="value.template == 3">
|
||||||
<view class="color-base-bg select-sku" @click="toDetail(item)">立即购买</view>
|
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">立即购买</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -718,6 +723,15 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 跳转到商品详情
|
||||||
|
* @param {Object} item 商品信息
|
||||||
|
*/
|
||||||
|
sendToAIChat(item) {
|
||||||
|
this.$util.redirectTo('/pages_goods/detail', {
|
||||||
|
goods_id: item.goods_id
|
||||||
|
});
|
||||||
|
},
|
||||||
diyRedirectTo(link) {
|
diyRedirectTo(link) {
|
||||||
if (!link) return;
|
if (!link) return;
|
||||||
this.$util.diyRedirectTo(JSON.parse(link));
|
this.$util.diyRedirectTo(JSON.parse(link));
|
||||||
@@ -1160,7 +1174,7 @@ export default {
|
|||||||
.right-wrap {
|
.right-wrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: end;
|
justify-content: flex-end;
|
||||||
|
|
||||||
.num {
|
.num {
|
||||||
width: auto;
|
width: auto;
|
||||||
@@ -1314,7 +1328,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .uni-popup__wrapper-box {
|
::v-deep .uni-popup__wrapper-box {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1348,7 +1362,7 @@ export default {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .loading-layer {
|
::v-deep .loading-layer {
|
||||||
background: #fff !important;
|
background: #fff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1384,4 +1398,25 @@ export default {
|
|||||||
color: var(--main-color);
|
color: var(--main-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-chat-btn-wrap {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: auto;
|
||||||
|
padding-top: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-chat-btn {
|
||||||
|
background-color: #ff0000;
|
||||||
|
color: #fff;
|
||||||
|
padding: 12rpx 30rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
@@ -3,9 +3,9 @@
|
|||||||
:style="{ height: 'calc(100vh - ' + tabBarHeight + ')' }">
|
:style="{ height: 'calc(100vh - ' + tabBarHeight + ')' }">
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
<!-- <block v-if="value.template == 4">
|
<!-- <block v-if="value.template == 4">
|
||||||
<view class="search-box" v-if="value.search" @click="$util.redirectTo('/pages_tool/goods/search')" :style="navbarInnerStyle">
|
<view class="search-box" v-if="value.search" @tap.stop="$util.redirectTo('/pages_tool/goods/search')" :style="navbarInnerStyle">
|
||||||
<view class="search-content">
|
<view class="search-content">
|
||||||
<input type="text" class="uni-input font-size-tag" maxlength="50" :placeholder="$lang('search')" confirm-type="search" @click.stop="onClickSearch()" @tap.stop="onClickSearch()" disabled="true" />
|
<input type="text" class="uni-input font-size-tag" maxlength="50" :placeholder="$lang('search')" confirm-type="search" @tap.stop="onClickSearch()" disabled="true" />
|
||||||
<text class="iconfont icon-sousuo3"></text>
|
<text class="iconfont icon-sousuo3"></text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -13,35 +13,35 @@
|
|||||||
</block> -->
|
</block> -->
|
||||||
<block v-if="value.template != 4">
|
<block v-if="value.template != 4">
|
||||||
<!-- <view :style="navbarInnerStyle">商品分类</view> -->
|
<!-- <view :style="navbarInnerStyle">商品分类</view> -->
|
||||||
<view class="search-box" v-if="value.search" @click="onClickSearch()" @tap.stop="onClickSearch()"
|
<view class="search-box" v-if="value.search" @tap.stop="onClickSearch()"
|
||||||
:style="wxSearchHeight">
|
:style="wxSearchHeight">
|
||||||
<view class="search-content">
|
<view class="search-content">
|
||||||
<input type="text" class="uni-input" maxlength="50" :placeholder="$lang('search')"
|
<input type="text" class="uni-input" maxlength="50" :placeholder="$lang('search')"
|
||||||
confirm-type="search" @click.stop="onClickSearch()" @tap.stop="onClickSearch()"
|
confirm-type="search" @tap.stop="onClickSearch()"
|
||||||
disabled="true" />
|
disabled="true" />
|
||||||
<text class="iconfont icon-sousuo3"></text>
|
<text class="iconfont icon-sousuo3"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="iconfont" :class="{ 'icon-apps': !isList, 'icon-list': isList }"
|
<view class="iconfont" :class="{ 'icon-apps': !isList, 'icon-list': isList }"
|
||||||
@click.stop.prevent="changeListStyle()"></view>
|
@tap.stop.prevent="changeListStyle()"></view>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
<!-- #ifdef H5 -->
|
<!-- #ifdef H5 -->
|
||||||
<view class="search-box" v-if="value.search" @click="onClickSearch()" @tap.stop="onClickSearch()">
|
<view class="search-box" v-if="value.search" @tap.stop="onClickSearch()">
|
||||||
<view class="search-content">
|
<view class="search-content">
|
||||||
<input type="text" class="uni-input" maxlength="50" :placeholder="$lang('search')" confirm-type="search"
|
<input type="text" class="uni-input" maxlength="50" :placeholder="$lang('search')" confirm-type="search"
|
||||||
@click.stop="onClickSearch()" @tap.stop="onClickSearch()" disabled="true" />
|
@tap.stop="onClickSearch()" disabled="true" />
|
||||||
<text class="iconfont icon-sousuo3"></text>
|
<text class="iconfont icon-sousuo3"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="iconfont" :class="{ 'icon-apps': !isList, 'icon-list': isList }"
|
<view class="iconfont" :class="{ 'icon-apps': !isList, 'icon-list': isList }"
|
||||||
@click.stop.prevent="changeListStyle()"></view>
|
@tap.stop.prevent="changeListStyle()"></view>
|
||||||
</view>
|
</view>
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
<view class="template-four wx" v-if="value.template == 4">
|
<view class="template-four wx" v-if="value.template == 4">
|
||||||
<scroll-view scroll-x="true" class="template-four-wrap" :scroll-with-animation="true"
|
<scroll-view scroll-x="true" class="template-four-wrap" :scroll-with-animation="true"
|
||||||
:scroll-into-view="'category-one-' + oneCategorySelect" enable-flex="true">
|
:scroll-into-view="'category-one-' + oneCategorySelect" enable-flex="true">
|
||||||
<view class="category-item" :id="'category-one-' + index" v-for="(item, index) in templateFourData"
|
<view class="category-item" :id="'category-one-' + index" v-for="(item, index) in templateFourData"
|
||||||
:key="index" :class="{ select: oneCategorySelect == index }" @click="templateFourOneFn(index)">
|
:key="index" :class="{ select: oneCategorySelect == index }" @tap.stop="templateFourOneFn(index)">
|
||||||
<view class="image-warp" :class="[{ 'color-base-border': oneCategorySelect == index }]">
|
<view class="image-warp" :class="[{ 'color-base-border': oneCategorySelect == index }]">
|
||||||
<image :src="$util.img(item.image)" mode="aspectFill" />
|
<image :src="$util.img(item.image)" mode="aspectFill" />
|
||||||
</view>
|
</view>
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
<view class="category-item-all" @click="$refs.templateFourPopup.open()">
|
<view class="category-item-all" @tap.stop="$refs.templateFourPopup.open()">
|
||||||
<view class="category-item-all-wrap">
|
<view class="category-item-all-wrap">
|
||||||
<text class="text">展开</text>
|
<text class="text">展开</text>
|
||||||
<image class="img" :src="$util.img('/public/uniapp/category/unfold.png')" mode="aspectFill"></image>
|
<image class="img" :src="$util.img('/public/uniapp/category/unfold.png')" mode="aspectFill"></image>
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
<view class="template-four-popup">
|
<view class="template-four-popup">
|
||||||
<scroll-view scroll-y="true" class="template-four-scroll" enable-flex="true">
|
<scroll-view scroll-y="true" class="template-four-scroll" enable-flex="true">
|
||||||
<view class="item" :class="{ selected: oneCategorySelect == index }"
|
<view class="item" :class="{ selected: oneCategorySelect == index }"
|
||||||
@click="templateFourOneFn(index)" v-for="(item, index) in templateFourData" :key="index">
|
@tap.stop="templateFourOneFn(index)" v-for="(item, index) in templateFourData" :key="index">
|
||||||
<view class="image-warp" :class="[{ 'color-base-border': oneCategorySelect == index }]">
|
<view class="image-warp" :class="[{ 'color-base-border': oneCategorySelect == index }]">
|
||||||
<image :src="$util.img(item.image)" mode="aspectFill"></image>
|
<image :src="$util.img(item.image)" mode="aspectFill"></image>
|
||||||
</view>
|
</view>
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
item.category_name }}</view>
|
item.category_name }}</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
<view class="pack-up" @click="$refs.templateFourPopup.close()">
|
<view class="pack-up" @tap.stop="$refs.templateFourPopup.close()">
|
||||||
<text>点击收起</text>
|
<text>点击收起</text>
|
||||||
<text class="iconfont icon-iconangledown-copy"></text>
|
<text class="iconfont icon-iconangledown-copy"></text>
|
||||||
</view>
|
</view>
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
{ select: select == index },
|
{ select: select == index },
|
||||||
{ 'border-bottom': value.template == 4 && select + 1 === index },
|
{ 'border-bottom': value.template == 4 && select + 1 === index },
|
||||||
{ 'border-top': value.template == 4 && select - 1 === index }
|
{ 'border-top': value.template == 4 && select - 1 === index }
|
||||||
]" @click="switchOneCategory(index)">
|
]" @tap.stop="switchOneCategory(index)">
|
||||||
<view class="">{{ item.category_name }}</view>
|
<view class="">{{ item.category_name }}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
|
|
||||||
<!-- <view class="cart-box" v-if="(value.template == 2 || value.template == 4) && value.quickBuy && storeToken && categoryTree && categoryTree.length">
|
<!-- <view class="cart-box" v-if="(value.template == 2 || value.template == 4) && value.quickBuy && storeToken && categoryTree && categoryTree.length">
|
||||||
<view class="left-wrap">
|
<view class="left-wrap">
|
||||||
<view class="cart-icon" ref="cartIcon" :animation="cartAnimation" @click="$util.redirectTo('/pages_goods/cart')">
|
<view class="cart-icon" ref="cartIcon" :animation="cartAnimation" @tap.stop="$util.redirectTo('/pages_goods/cart')">
|
||||||
<text class="iconfont icon-ziyuan1"></text>
|
<text class="iconfont icon-ziyuan1"></text>
|
||||||
<view class="num" v-if="cartNumber">{{ cartNumber < 99 ? cartNumber : '99+' }}</view>
|
<view class="num" v-if="cartNumber">{{ cartNumber < 99 ? cartNumber : '99+' }}</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
<text class="unit font-size-tag price-font">.{{ cartTotalMoney[1] ? cartTotalMoney[1] : '00' }}</text>
|
<text class="unit font-size-tag price-font">.{{ cartTotalMoney[1] ? cartTotalMoney[1] : '00' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="right-wrap"><button type="primary" class="settlement-btn" @click="settlement">去结算</button>
|
<view class="right-wrap"><button type="primary" class="settlement-btn" @tap.stop="settlement">去结算</button>
|
||||||
</view>
|
</view>
|
||||||
</view> -->
|
</view> -->
|
||||||
|
|
||||||
@@ -548,7 +548,7 @@ export default {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss" scoped>
|
||||||
.category-page-wrap {
|
.category-page-wrap {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
// height: calc(100vh - var(--tab-bar-height, 0));
|
// height: calc(100vh - var(--tab-bar-height, 0));
|
||||||
@@ -818,7 +818,7 @@ export default {
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .template-four {
|
::v-deep .template-four {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
|
|||||||
645
components-diy/diy-channel-list.vue
Normal file
645
components-diy/diy-channel-list.vue
Normal file
@@ -0,0 +1,645 @@
|
|||||||
|
<template>
|
||||||
|
<view class="channel-list-container" :style="[componentStyle, { '--row-count': value.rowCount }]">
|
||||||
|
<!-- 轮播模式 -->
|
||||||
|
<swiper v-if="value.showStyle == 'carousel' && carouselConfig.type != 'hide'" :indicator-dots="isIndicatorDots"
|
||||||
|
:autoplay="carouselConfig.autoplay || false" :interval="carouselConfig.interval || 3000"
|
||||||
|
:duration="carouselConfig.duration || 500" :circular="carouselConfig.circular || false"
|
||||||
|
:style="swiperHeight" class="channel-swiper">
|
||||||
|
<swiper-item v-for="(slide, slideIndex) in swiperSlides" :key="slideIndex">
|
||||||
|
<view :class="['swiper-slide-content', 'row1-of' + value.rowCount]">
|
||||||
|
<view v-for="(item, index) in slide" :key="index" :class="['channel-item', value.mode]">
|
||||||
|
<!-- 视频号视频卡片,轮播模式 -->
|
||||||
|
<diy-channel-video :value="item" @video-play="playVideo" :list-mode="true"
|
||||||
|
:title-line-clamp="value.titleLineClamp" :show-play-btn="value.showPlayBtn"
|
||||||
|
:show-view-count="value.showViewCount" :cover-style="computedCoverStyle"
|
||||||
|
:play-btn-style="value.playBtnStyle" :aspect-ratio="value.aspectRatio" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
|
|
||||||
|
<!-- 固定布局模式 -->
|
||||||
|
<view v-else-if="value.showStyle == 'fixed'" :class="['channel-list', 'row1-of' + value.rowCount]">
|
||||||
|
<view v-for="(item, index) in value.list" :key="index" class="channel-item">
|
||||||
|
<!-- 视频号视频卡片,列表模式 -->
|
||||||
|
<diy-channel-video :value="item" @video-play="playVideo" :list-mode="true"
|
||||||
|
:title-line-clamp="value.titleLineClamp" :show-play-btn="value.showPlayBtn"
|
||||||
|
:show-view-count="value.showViewCount" :cover-style="computedCoverStyle"
|
||||||
|
:play-btn-style="value.playBtnStyle" :aspect-ratio="value.aspectRatio" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 其他布局模式(如滚动布局) -->
|
||||||
|
<scroll-view v-else
|
||||||
|
:class="['channel-nav', value.showStyle == 'fixed' ? 'fixed-layout' : 'singleSlide', 'row1-of' + value.rowCount]"
|
||||||
|
:scroll-x="true"
|
||||||
|
:scroll-y="false"
|
||||||
|
:enhanced="true"
|
||||||
|
:bounces="false">
|
||||||
|
<view class="uni-scroll-view-content">
|
||||||
|
<view v-for="(item, index) in value.list" :key="index"
|
||||||
|
:class="['channel-nav-item', value.mode, 'row1-of' + value.rowCount]">
|
||||||
|
<!-- 视频号视频卡片,滚动模式 -->
|
||||||
|
<diy-channel-video :value="item" @video-play="playVideo" :list-mode="true"
|
||||||
|
:title-line-clamp="value.titleLineClamp" :show-play-btn="value.showPlayBtn"
|
||||||
|
:show-view-count="value.showViewCount" :cover-style="computedCoverStyle"
|
||||||
|
:play-btn-style="value.playBtnStyle" :aspect-ratio="value.aspectRatio" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 微信视频号列表组件
|
||||||
|
* 支持多种布局模式,包括固定布局和滚动布局
|
||||||
|
* 可配置列数、视频高度、标题行数等属性
|
||||||
|
* 用于在页面中展示多个微信视频号视频
|
||||||
|
*/
|
||||||
|
import DiyMinx from './minx.js'
|
||||||
|
import { wechatChannelUtil, wechatChannelConfig } from './js/wechat-channel.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'diy-channel-list',
|
||||||
|
props: {
|
||||||
|
/**
|
||||||
|
* 组件配置数据
|
||||||
|
* @type {Object}
|
||||||
|
* @default () => ({})
|
||||||
|
* @property {string} showStyle - 显示样式,可选值:carousel, fixed, singleSlide
|
||||||
|
* @property {number} rowCount - 每行显示的视频数量
|
||||||
|
* @property {Array} list - 视频列表数据
|
||||||
|
* @property {string} componentBgColor - 组件背景色
|
||||||
|
* @property {string} componentAngle - 组件圆角类型,可选值:round
|
||||||
|
* @property {number} topAroundRadius - 顶部圆角半径
|
||||||
|
* @property {number} bottomAroundRadius - 底部圆角半径
|
||||||
|
* @property {Object} ornament - 装饰效果配置
|
||||||
|
* @property {boolean} showViewCount - 是否显示播放量,可选值:true, false
|
||||||
|
* @property {number} titleLineClamp - 标题显示行数
|
||||||
|
* @property {string} aspectRatio - 视频比例,可选值:16:9, 3:4
|
||||||
|
* @property {boolean} showPlayBtn - 是否显示播放按钮
|
||||||
|
* @property {Object} coverStyle - 视频封面图样式
|
||||||
|
* @property {Object} playBtnStyle - 播放按钮样式
|
||||||
|
* @property {string} mode - 显示模式, 可选值:video, text, image, graphic
|
||||||
|
* @property {number} imageSize - 图片尺寸(仅在特定模式下使用)
|
||||||
|
* @property {number} pageCount - 每页显示的视频数量
|
||||||
|
* @property {Object} carousel - 轮播配置
|
||||||
|
* @property {string} carousel.type - 轮播类型,可选值:default, hide
|
||||||
|
* @property {boolean} carousel.autoplay - 是否自动播放,默认false
|
||||||
|
* @property {number} carousel.interval - 自动播放间隔,默认3000ms
|
||||||
|
* @property {number} carousel.duration - 切换动画时长,默认500ms
|
||||||
|
* @property {boolean} carousel.circular - 是否循环播放,默认false
|
||||||
|
*/
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mixins: [DiyMinx],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
pageWidth: '', // 页面宽度
|
||||||
|
indicatorDots: false, // 是否显示轮播指示器
|
||||||
|
swiperCurrent: 0 // 当前轮播索引
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// 组件创建时的逻辑
|
||||||
|
// 可以在这里进行初始化操作,如获取页面宽度等
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// 组件挂载后添加鼠标拖拽滚动功能
|
||||||
|
if (!['fixed', 'carousel'].includes(this.value?.showStyle)) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.addMouseDragScroll();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
/**
|
||||||
|
* 组件刷新监听
|
||||||
|
* 当组件需要刷新时触发
|
||||||
|
* @param {*} newValue - 新值
|
||||||
|
*/
|
||||||
|
componentRefresh(newValue) {
|
||||||
|
// 监听组件刷新
|
||||||
|
// 可以在这里处理组件刷新时的逻辑
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
carouselConfig() {
|
||||||
|
return this.value?.carousel || {
|
||||||
|
type: 'default',
|
||||||
|
autoplay: false,
|
||||||
|
interval: 3000,
|
||||||
|
duration: 500,
|
||||||
|
circular: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 组件样式
|
||||||
|
* 根据配置动态生成样式字符串,包括背景色、圆角、阴影和边框
|
||||||
|
* @returns {string} 样式字符串
|
||||||
|
*/
|
||||||
|
componentStyle() {
|
||||||
|
const style = {};
|
||||||
|
// 背景色
|
||||||
|
if (this.value?.componentBgColor) {
|
||||||
|
style.backgroundColor = this.value?.componentBgColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 圆角样式
|
||||||
|
if (this.value?.componentAngle == 'round') {
|
||||||
|
style.borderTopLeftRadius = (2 * this.value?.topAroundRadius) + 'rpx';
|
||||||
|
style.borderTopRightRadius = (2 * this.value?.topAroundRadius) + 'rpx';
|
||||||
|
style.borderBottomLeftRadius = (2 * this.value?.bottomAroundRadius) + 'rpx';
|
||||||
|
style.borderBottomRightRadius = (2 * this.value?.bottomAroundRadius) + 'rpx';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 装饰效果:阴影
|
||||||
|
style.boxShadow = this.value?.ornament?.type == 'shadow' ? '0 0 10rpx ' + this.value?.ornament?.color : '';
|
||||||
|
// 装饰效果:边框
|
||||||
|
style.border = this.value?.ornament?.type == 'stroke' ? '2rpx solid ' + this.value?.ornament?.color : '';
|
||||||
|
|
||||||
|
return style;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轮播高度
|
||||||
|
* 根据模式和配置计算轮播高度
|
||||||
|
* @returns {string} 轮播高度样式
|
||||||
|
*/
|
||||||
|
swiperHeight() {
|
||||||
|
let height = 0;
|
||||||
|
|
||||||
|
const pageCount = this.value?.pageCount || 1;
|
||||||
|
|
||||||
|
// 根据不同模式计算高度
|
||||||
|
if (this.value?.mode == 'graphic') {
|
||||||
|
height = (49 + this.value?.imageSize) * pageCount;
|
||||||
|
} else if (this.value?.mode == 'img') {
|
||||||
|
height = (22 + this.value?.imageSize) * pageCount;
|
||||||
|
} else if (this.value?.mode == 'text') {
|
||||||
|
height = 43 * pageCount;
|
||||||
|
} else { // 默认视频模式
|
||||||
|
if (this.value?.aspectRatio == '16:9') {
|
||||||
|
height = [250, 160, 140, 120][this.value.rowCount - 1];
|
||||||
|
} else {
|
||||||
|
height = [510, 280, 220, 180][this.value.rowCount - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'height:' + (2 * height) + 'rpx';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否显示指示器
|
||||||
|
* 根据轮播配置和列表长度判断是否显示指示器
|
||||||
|
* @returns {boolean} 是否显示指示器
|
||||||
|
*/
|
||||||
|
isIndicatorDots() {
|
||||||
|
// 当轮播类型不是隐藏,且视频数量超过一页时显示指示器
|
||||||
|
const pageCount = this.value?.pageCount || 1;
|
||||||
|
return this.value?.carousel?.type != 'hide' && Math.ceil(this.value?.list?.length / (pageCount * this.value?.rowCount)) > 1
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轮播幻灯片数据
|
||||||
|
* 将视频列表分割成轮播所需的幻灯片数据
|
||||||
|
* @returns {Array} 轮播幻灯片数据
|
||||||
|
*/
|
||||||
|
swiperSlides() {
|
||||||
|
const slides = [];
|
||||||
|
const list = this.value?.list || [];
|
||||||
|
const pageSize = (this.value?.pageCount || 1) * (this.value?.rowCount || 1);
|
||||||
|
|
||||||
|
// 将列表数据分割成每页显示的数量
|
||||||
|
for (let i = 0; i < list.length; i += pageSize) {
|
||||||
|
slides.push(list.slice(i, i + pageSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
return slides;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 视频封面样式
|
||||||
|
* 根据aspectRatio属性动态计算封面样式
|
||||||
|
* @returns {Object} 封面样式对象
|
||||||
|
*/
|
||||||
|
computedCoverStyle() {
|
||||||
|
const aspectRatio = this.value?.aspectRatio || '16:9';
|
||||||
|
let paddingTop = '56.25%'; // 默认 16:9 比例
|
||||||
|
|
||||||
|
if (aspectRatio === '3:4') {
|
||||||
|
paddingTop = '133.33%'; // 3:4 比例
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
width: '100%',
|
||||||
|
height: '0',
|
||||||
|
paddingTop: paddingTop
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 播放视频
|
||||||
|
* 触发 video-play 事件,并在微信小程序中调用视频播放接口
|
||||||
|
* @param {Object} item - 视频数据对象
|
||||||
|
*/
|
||||||
|
async playVideo(item) {
|
||||||
|
await this.__$emitEvent({
|
||||||
|
eventName: 'video-play',
|
||||||
|
data: item,
|
||||||
|
promiseCallback: async (event, handler, awaitedResult) => {
|
||||||
|
if (!awaitedResult) return;
|
||||||
|
try {
|
||||||
|
// 发送视频被点击播放事件
|
||||||
|
this.$emit('channel-video-click-play', item);
|
||||||
|
console.log('播放视频:', item);
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
// 在微信小程序环境中调用视频播放接口
|
||||||
|
await wechatChannelUtil.playVideo(item);
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
if (item?.channelType === 'wechat') {
|
||||||
|
uni.showToast({
|
||||||
|
title: '视频号仅支持在微信小程序环境中播放',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 在非微信小程序环境中,直接触发事件
|
||||||
|
this.$emit('video-play', item);
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
} catch (err) {
|
||||||
|
console.error('打开视频号失败', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片加载错误处理
|
||||||
|
* 当图片加载失败时,设置默认图片
|
||||||
|
* @param {number} index - 图片索引
|
||||||
|
*/
|
||||||
|
imgError(index) {
|
||||||
|
// 图片加载失败的处理逻辑
|
||||||
|
console.log('图片加载失败:', index);
|
||||||
|
// 为失败的图片设置默认图片
|
||||||
|
const item = this.value.list[index];
|
||||||
|
if (item) {
|
||||||
|
// 使用默认图片替代加载失败的图片
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
item.coverUrl = wechatChannelConfig.video.defaultCoverUrl;
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 添加鼠标拖拽滚动功能
|
||||||
|
* 在Web环境中实现与微信小程序相同的拖拽滚动效果
|
||||||
|
*/
|
||||||
|
addMouseDragScroll() {
|
||||||
|
// 只在Web环境中添加
|
||||||
|
// #ifndef MP
|
||||||
|
console.log('addMouseDragScroll called');
|
||||||
|
|
||||||
|
let isDragging = false;
|
||||||
|
let startX = 0;
|
||||||
|
let startScrollLeft = 0;
|
||||||
|
let currentScrollElement = null;
|
||||||
|
|
||||||
|
// 查找最近的可滚动祖先元素
|
||||||
|
const findScrollableParent = (element) => {
|
||||||
|
while (element && element !== document) {
|
||||||
|
const style = window.getComputedStyle(element);
|
||||||
|
if (style.overflowX === 'auto' || style.overflowX === 'scroll') {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
element = element.parentElement;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 鼠标按下事件
|
||||||
|
const handleMouseDown = (e) => {
|
||||||
|
// 检查是否点击在组件内
|
||||||
|
if (this.$el.contains(e.target)) {
|
||||||
|
console.log('mousedown event in component:', e);
|
||||||
|
// 查找可滚动元素
|
||||||
|
currentScrollElement = findScrollableParent(e.target);
|
||||||
|
if (currentScrollElement) {
|
||||||
|
console.log('Found scrollable element:', currentScrollElement);
|
||||||
|
isDragging = true;
|
||||||
|
startX = e.pageX;
|
||||||
|
startScrollLeft = currentScrollElement.scrollLeft;
|
||||||
|
currentScrollElement.style.cursor = 'grabbing';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 鼠标移动事件
|
||||||
|
const handleMouseMove = (e) => {
|
||||||
|
if (!isDragging || !currentScrollElement) return;
|
||||||
|
console.log('mousemove event:', e);
|
||||||
|
e.preventDefault();
|
||||||
|
const dx = e.pageX - startX;
|
||||||
|
currentScrollElement.scrollLeft = startScrollLeft - dx;
|
||||||
|
console.log('scrollLeft:', currentScrollElement.scrollLeft);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 鼠标释放事件
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
if (isDragging && currentScrollElement) {
|
||||||
|
console.log('mouseup event');
|
||||||
|
currentScrollElement.style.cursor = 'grab';
|
||||||
|
}
|
||||||
|
isDragging = false;
|
||||||
|
currentScrollElement = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加全局事件监听器
|
||||||
|
document.addEventListener('mousedown', handleMouseDown);
|
||||||
|
document.addEventListener('mousemove', handleMouseMove);
|
||||||
|
document.addEventListener('mouseup', handleMouseUp);
|
||||||
|
document.addEventListener('mouseleave', handleMouseUp);
|
||||||
|
|
||||||
|
console.log('Global mouse event listeners added');
|
||||||
|
|
||||||
|
// 组件销毁时移除事件监听器
|
||||||
|
this.$once('hook:beforeDestroy', () => {
|
||||||
|
document.removeEventListener('mousedown', handleMouseDown);
|
||||||
|
document.removeEventListener('mousemove', handleMouseMove);
|
||||||
|
document.removeEventListener('mouseup', handleMouseUp);
|
||||||
|
document.removeEventListener('mouseleave', handleMouseUp);
|
||||||
|
console.log('Global mouse event listeners removed');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 为所有.channel-nav元素添加必要的样式
|
||||||
|
setTimeout(() => {
|
||||||
|
const channelNavs = document.querySelectorAll('.channel-nav');
|
||||||
|
console.log('Found channel-nav elements:', channelNavs.length);
|
||||||
|
channelNavs.forEach(element => {
|
||||||
|
element.style.overflowX = 'auto';
|
||||||
|
element.style.overflowY = 'hidden';
|
||||||
|
element.style.whiteSpace = 'nowrap';
|
||||||
|
element.style.width = '100%';
|
||||||
|
element.style.maxWidth = '100%';
|
||||||
|
element.style.cursor = 'grab';
|
||||||
|
element.style.userSelect = 'none'; // 防止文本选择
|
||||||
|
console.log('Added styles to channel-nav:', element);
|
||||||
|
});
|
||||||
|
}, 100); // 延迟执行,确保DOM已完全渲染
|
||||||
|
|
||||||
|
console.log('Mouse drag scroll setup completed');
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import './css/common-channel.scss';
|
||||||
|
|
||||||
|
.channel-list-container {
|
||||||
|
padding: 16px 16px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表布局样式
|
||||||
|
*/
|
||||||
|
.channel-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(var(--row-count, 2), 1fr);
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导航布局样式
|
||||||
|
* 支持固定布局和滚动布局
|
||||||
|
*/
|
||||||
|
.channel-nav {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 16rpx;
|
||||||
|
|
||||||
|
// 确保在H5环境中可以水平滚动
|
||||||
|
overflow-x: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
// 隐藏滚动条但保留滚动功能
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollbar-width: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
|
||||||
|
// 启用触摸滚动和鼠标拖拽滚动
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
|
||||||
|
.uni-scroll-view-content {
|
||||||
|
/* #ifdef MP-WEIXIN */
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
gap: 16rpx;
|
||||||
|
/* #endif */
|
||||||
|
/* #ifndef MP-WEIXIN */
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(var(--row-count, 2), 1fr);
|
||||||
|
gap: 16rpx;
|
||||||
|
/* #endif */
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单滑动模式
|
||||||
|
&.singleSlide {
|
||||||
|
.uni-scroll-view-content {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
gap: 16rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-nav-item {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 280rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-nav-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
/* #ifdef MP-WEIXIN */
|
||||||
|
&.row1-of1 {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
&.row1-of2 {
|
||||||
|
width: calc(50% - 8rpx);
|
||||||
|
}
|
||||||
|
&.row1-of3 {
|
||||||
|
width: calc(33.333% - 10.67rpx);
|
||||||
|
}
|
||||||
|
&.row1-of4 {
|
||||||
|
width: calc(25% - 12rpx);
|
||||||
|
}
|
||||||
|
/* #endif */
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 列布局
|
||||||
|
&.row1-of1 {
|
||||||
|
.uni-scroll-view-content {
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 列布局
|
||||||
|
&.row1-of2 {
|
||||||
|
.uni-scroll-view-content {
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3 列布局
|
||||||
|
&.row1-of3 {
|
||||||
|
.uni-scroll-view-content {
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4 列布局
|
||||||
|
&.row1-of4 {
|
||||||
|
.uni-scroll-view-content {
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确保所有视频卡片高度一致
|
||||||
|
*/
|
||||||
|
.channel-item,
|
||||||
|
.channel-nav-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轮播样式
|
||||||
|
* 支持轮播模式的布局和样式
|
||||||
|
*/
|
||||||
|
.channel-swiper {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.swiper-slide-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(var(--row-count, 2), 1fr);
|
||||||
|
gap: 16rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.channel-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 轮播模式下的 1 列布局
|
||||||
|
&.row1-of1 {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 轮播模式下的 2 列布局
|
||||||
|
&.row1-of2 {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 轮播模式下的 3 列布局
|
||||||
|
&.row1-of3 {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 轮播模式下的 4 列布局
|
||||||
|
&.row1-of4 {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应式调整
|
||||||
|
* 在小屏幕设备上调整布局和间距
|
||||||
|
*/
|
||||||
|
@media (max-width: 375px) {
|
||||||
|
|
||||||
|
.channel-list,
|
||||||
|
.channel-nav,
|
||||||
|
.channel-swiper .swiper-slide-content {
|
||||||
|
gap: 12rpx;
|
||||||
|
padding: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 小屏幕上的单滑动模式
|
||||||
|
.channel-nav {
|
||||||
|
&.singleSlide {
|
||||||
|
.uni-scroll-view-content {
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-nav-item {
|
||||||
|
width: 240rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频比例样式调整
|
||||||
|
* 根据不同的视频比例调整布局
|
||||||
|
*/
|
||||||
|
|
||||||
|
// // 3:4 比例的视频卡片样式
|
||||||
|
// .channel-video.ratio-3-4 {
|
||||||
|
// // 调整视频卡片的整体高度
|
||||||
|
// ::v-deep .video-cover-wrap {
|
||||||
|
// padding-top: 133.33%; // 3:4 比例
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 列表模式下的3:4比例调整
|
||||||
|
// &.list-mode {
|
||||||
|
// ::v-deep .video-cover-wrap {
|
||||||
|
// padding-top: 133.33%; // 3:4 比例
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 16:9 比例的视频卡片样式(默认)
|
||||||
|
// .channel-video.ratio-16-9 {
|
||||||
|
// // 保持默认的16:9比例
|
||||||
|
// ::v-deep .video-cover-wrap {
|
||||||
|
// padding-top: 56.25%; // 16:9 比例
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 列表模式下的16:9比例保持默认
|
||||||
|
// &.list-mode {
|
||||||
|
// ::v-deep .video-cover-wrap {
|
||||||
|
// padding-top: 56.25%; // 16:9 比例
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }</style>
|
||||||
489
components-diy/diy-channel-video.vue
Normal file
489
components-diy/diy-channel-video.vue
Normal file
@@ -0,0 +1,489 @@
|
|||||||
|
<template>
|
||||||
|
<view class="channel-video" :class="{ 'list-mode': listMode }">
|
||||||
|
<!-- 嵌入式视频播放 -->
|
||||||
|
<view v-if="canUseEmbedMode" class="embed-video-container">
|
||||||
|
<view class="video-cover-wrap" :style="[coverStyle]">
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<native-component>
|
||||||
|
<!-- 嵌入式视频播放组件 -->
|
||||||
|
<channel-video :feed-id="value.feedId" :finder-user-name="value.finderUserName"
|
||||||
|
:feed-token="value.feedToken" :auto-play="autoPlay">
|
||||||
|
</channel-video>
|
||||||
|
</native-component>
|
||||||
|
<!-- #endif -->
|
||||||
|
</view>
|
||||||
|
<view class="video-info">
|
||||||
|
<!-- 视频标题,支持多行显示控制 -->
|
||||||
|
<view class="video-title" :style="[{ '--title-line-clamp': titleLineClamp }]">{{ value.videoTitle }}
|
||||||
|
</view>
|
||||||
|
<!-- 视频统计信息 -->
|
||||||
|
<view class="video-stats" v-if="showViewCount && value.showViewCount">{{ value.viewCount }}次观看</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 跳转式视频播放 -->
|
||||||
|
<view v-else @tap.stop="playVideo" class="video-container">
|
||||||
|
<view class="video-cover-wrap" :style="[coverStyle]">
|
||||||
|
<image class="video-cover" :src="$util.img(value.coverUrl)" mode="aspectFill"></image>
|
||||||
|
<view class="channel-play-btn" v-if="showPlayBtn" :style="[playBtnStyle]">
|
||||||
|
<view class="play-icon-css"></view>
|
||||||
|
</view>
|
||||||
|
<view class="video-stats-overlay" v-if="showViewCount && value.showViewCount">
|
||||||
|
{{ value.viewCount }}次观看
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="video-info">
|
||||||
|
<!-- 视频标题,支持多行显示控制 -->
|
||||||
|
<view class="video-title" :style="[{ '--title-line-clamp': titleLineClamp }]">{{ value.videoTitle }}
|
||||||
|
</view>
|
||||||
|
<!-- 视频统计信息,非列表模式下显示 -->
|
||||||
|
<view class="video-stats" v-if="showViewCount && value.showViewCount && !listMode">{{ value.viewCount }}次观看</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import { wechatChannelUtil, wechatChannelConfig } from './js/wechat-channel.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信视频号视频卡片组件
|
||||||
|
* 支持嵌入式播放和跳转式播放两种模式
|
||||||
|
* 可配置列表模式、视频高度、标题行数等属性
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: 'diy-channel-video',
|
||||||
|
props: {
|
||||||
|
/**
|
||||||
|
* 视频数据对象
|
||||||
|
* @type {Object}
|
||||||
|
* @required
|
||||||
|
* @property {string} feedId - 视频 feedId
|
||||||
|
* @property {string} feedToken - 视频 token
|
||||||
|
* @property {string} coverUrl - 视频封面图
|
||||||
|
* @property {string} videoTitle - 视频标题
|
||||||
|
* @property {number} viewCount - 观看次数
|
||||||
|
* @property {boolean} showViewCount - 是否显示观看次数
|
||||||
|
* @property {boolean} embedMode - 是否启用嵌入式播放
|
||||||
|
*/
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 是否为列表模式
|
||||||
|
* @type {boolean}
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
listMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
/** 是否显示观看次数,由父组件可以整体配置 */
|
||||||
|
showViewCount: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 标题显示行数
|
||||||
|
* @type {number}
|
||||||
|
* @default 1
|
||||||
|
*/
|
||||||
|
titleLineClamp: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
/** 是否显示播放按钮 */
|
||||||
|
showPlayBtn: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 视频封面图样式
|
||||||
|
* 采用 16:9 比例的响应式高度
|
||||||
|
*/
|
||||||
|
coverStyle: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
width: '100%',
|
||||||
|
height: '0',
|
||||||
|
paddingTop: '56.25%' // 16:9 比例
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 播放按钮样式
|
||||||
|
*/
|
||||||
|
playBtnStyle: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
width: '60rpx',
|
||||||
|
height: '60rpx',
|
||||||
|
borderRadius: '30rpx',
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.6)'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 是否自动播放 */
|
||||||
|
autoPlay: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* 是否支持嵌入式播放
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
canUseEmbedMode() {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
const enableEmbedMode = Boolean(this.value?.embedMode)
|
||||||
|
&& Boolean(this.value?.feedToken)
|
||||||
|
&& wechatChannelUtil.isEmbedModeSupported();
|
||||||
|
console.log('enableEmbedMode', enableEmbedMode);
|
||||||
|
return enableEmbedMode;
|
||||||
|
// #endif
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 播放视频
|
||||||
|
* 触发 video-play 事件,由父组件处理具体播放逻辑
|
||||||
|
*/
|
||||||
|
async playVideo() {
|
||||||
|
this.$emit('video-play', this.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import './css/common-channel.scss';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频卡片容器样式
|
||||||
|
* 包含卡片基础样式、悬停效果和列表模式样式
|
||||||
|
*/
|
||||||
|
.channel-video {
|
||||||
|
position: relative;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
// 悬停效果
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-4rpx);
|
||||||
|
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表模式样式调整
|
||||||
|
&.list-mode {
|
||||||
|
border-radius: 10rpx;
|
||||||
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.08);
|
||||||
|
|
||||||
|
.video-info {
|
||||||
|
padding: 14rpx;
|
||||||
|
|
||||||
|
.video-title {
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin-bottom: 6rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频容器样式
|
||||||
|
* 用于跳转式播放模式
|
||||||
|
*/
|
||||||
|
.video-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 嵌入式视频容器样式
|
||||||
|
* 与视频容器样式保持一致
|
||||||
|
*/
|
||||||
|
.embed-video-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频封面容器
|
||||||
|
*/
|
||||||
|
.video-cover-wrap {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 56.25%;
|
||||||
|
/* 16:9 比例 */
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 12rpx 12rpx 0 0;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
|
||||||
|
// 悬停时缩放效果
|
||||||
|
.channel-video:hover & {
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表模式下的边框圆角调整
|
||||||
|
.channel-video.list-mode & {
|
||||||
|
border-radius: 10rpx 10rpx 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频封面图片
|
||||||
|
* 绝对定位填充整个容器
|
||||||
|
*/
|
||||||
|
.video-cover {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频统计信息遮罩
|
||||||
|
* 显示在视频封面底部
|
||||||
|
*/
|
||||||
|
.video-stats-overlay {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
|
||||||
|
padding: 12rpx 16rpx;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 20rpx;
|
||||||
|
text-align: right;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
|
||||||
|
// 悬停时的透明度变化
|
||||||
|
.channel-video:hover & {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-video:not(:hover) & {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频信息区域
|
||||||
|
* 包含标题和统计信息
|
||||||
|
*/
|
||||||
|
.video-info {
|
||||||
|
padding: 16rpx;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: #fff;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
|
||||||
|
// 悬停时的背景色变化
|
||||||
|
.channel-video:hover & {
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频标题
|
||||||
|
* 支持多行显示控制和渐变遮罩效果
|
||||||
|
*/
|
||||||
|
.video-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #222;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
line-height: 1.4;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: var(--title-line-clamp, 2);
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
word-break: break-all;
|
||||||
|
word-wrap: break-word;
|
||||||
|
white-space: normal;
|
||||||
|
|
||||||
|
// 悬停时的颜色变化
|
||||||
|
.channel-video:hover & {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表模式下的样式调整
|
||||||
|
.channel-video.list-mode & {
|
||||||
|
-webkit-line-clamp: var(--title-line-clamp, 1);
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin-bottom: 6rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加渐变遮罩效果,让过长的文字有柔和的结束
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 40rpx;
|
||||||
|
height: 1.4em;
|
||||||
|
background: linear-gradient(to right, transparent, #fff 90%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表模式下的遮罩高度调整
|
||||||
|
.channel-video.list-mode &::after {
|
||||||
|
height: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据行数调整遮罩高度
|
||||||
|
&[style*="--title-line-clamp: 1"]::after {
|
||||||
|
height: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[style*="--title-line-clamp: 3"]::after {
|
||||||
|
height: 4.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[style*="--title-line-clamp: 4"]::after {
|
||||||
|
height: 5.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表模式下的多行遮罩高度调整
|
||||||
|
.channel-video.list-mode &[style*="--title-line-clamp: 1"]::after {
|
||||||
|
height: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-video.list-mode &[style*="--title-line-clamp: 3"]::after {
|
||||||
|
height: 3.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-video.list-mode &[style*="--title-line-clamp: 4"]::after {
|
||||||
|
height: 5.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频统计信息
|
||||||
|
* 显示观看次数等数据
|
||||||
|
*/
|
||||||
|
.video-stats {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 6rpx;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
|
||||||
|
// 悬停时的颜色变化
|
||||||
|
.channel-video:hover & {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表模式下的播放按钮样式
|
||||||
|
* 更小的尺寸和悬停效果
|
||||||
|
*/
|
||||||
|
.channel-video.list-mode .channel-play-btn {
|
||||||
|
width: 50rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
// 悬停效果
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放图标尺寸
|
||||||
|
.play-icon {
|
||||||
|
width: 25rpx;
|
||||||
|
height: 25rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用播放按钮样式优化
|
||||||
|
* 添加悬停效果
|
||||||
|
*/
|
||||||
|
.channel-play-btn {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
// 悬停效果
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 纯 CSS 播放按钮图标
|
||||||
|
*/
|
||||||
|
.play-icon-css {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top: 12rpx solid transparent;
|
||||||
|
border-bottom: 12rpx solid transparent;
|
||||||
|
border-left: 18rpx solid #fff;
|
||||||
|
margin-left: 4rpx;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
|
||||||
|
// 悬停时的缩放效果
|
||||||
|
.channel-play-btn:hover & {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表模式下的尺寸调整
|
||||||
|
.channel-video.list-mode & {
|
||||||
|
border-top: 10rpx solid transparent;
|
||||||
|
border-bottom: 10rpx solid transparent;
|
||||||
|
border-left: 15rpx solid #fff;
|
||||||
|
margin-left: 3rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原生组件样式控制
|
||||||
|
* 确保 native-component 和 channel-video 正确填充容器
|
||||||
|
*/
|
||||||
|
native-component {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* channel-video 组件样式控制
|
||||||
|
* 避免受到 wx-channel-video 全局样式的影响
|
||||||
|
*/
|
||||||
|
channel-video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/style1-bg.png') + ')',
|
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/style1-bg.png') + ')',
|
||||||
marginRight: couponItemHeight + 'px',
|
marginRight: couponItemHeight + 'px',
|
||||||
marginLeft: couponItemHeight + 'px'
|
marginLeft: couponItemHeight + 'px'
|
||||||
}" @click="couponAction(item, index)">
|
}" @tap.stop="couponAction(item, index)">
|
||||||
|
|
||||||
<view class="coupon-info">
|
<view class="coupon-info">
|
||||||
<view class="coupon-num" :style="{ color: value.moneyColor }"
|
<view class="coupon-num" :style="{ color: value.moneyColor }"
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/coupon_bg1.png') + ')',
|
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/coupon_bg1.png') + ')',
|
||||||
marginRight: couponItemHeight + 'px',
|
marginRight: couponItemHeight + 'px',
|
||||||
marginLeft: couponItemHeight + 'px'
|
marginLeft: couponItemHeight + 'px'
|
||||||
}" @click="couponAction(item, index)">
|
}" @tap.stop="couponAction(item, index)">
|
||||||
<view class="coupon-info">
|
<view class="coupon-info">
|
||||||
<view class="coupon-num" :style="{ color: value.moneyColor }"
|
<view class="coupon-num" :style="{ color: value.moneyColor }"
|
||||||
v-if="!parseInt(item.discount)">
|
v-if="!parseInt(item.discount)">
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/coupon_shu.png') + ')',
|
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/coupon_shu.png') + ')',
|
||||||
marginRight: couponItemHeight + 'px',
|
marginRight: couponItemHeight + 'px',
|
||||||
marginLeft: couponItemHeight + 'px'
|
marginLeft: couponItemHeight + 'px'
|
||||||
}" @click="couponAction(item, index)">
|
}" @tap.stop="couponAction(item, index)">
|
||||||
<view class="coupon-num" :style="{ color: value.moneyColor }"
|
<view class="coupon-num" :style="{ color: value.moneyColor }"
|
||||||
v-if="!parseInt(item.discount)">
|
v-if="!parseInt(item.discount)">
|
||||||
<text class="font-size-tag coupon-sign">¥</text>
|
<text class="font-size-tag coupon-sign">¥</text>
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/style4_bg.png') + ')',
|
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/style4_bg.png') + ')',
|
||||||
marginRight: couponItemHeight + 'px',
|
marginRight: couponItemHeight + 'px',
|
||||||
marginLeft: couponItemHeight + 'px'
|
marginLeft: couponItemHeight + 'px'
|
||||||
}" @click="couponAction(item, index)">
|
}" @tap.stop="couponAction(item, index)">
|
||||||
<view class="coupon-info">
|
<view class="coupon-info">
|
||||||
<view class="coupon-num" :style="{ color: value.moneyColor }"
|
<view class="coupon-num" :style="{ color: value.moneyColor }"
|
||||||
v-if="!parseInt(item.discount)">
|
v-if="!parseInt(item.discount)">
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
<view class="coupon-all">
|
<view class="coupon-all">
|
||||||
<view class="coupon-box">
|
<view class="coupon-box">
|
||||||
<view class="coupon-list" v-for="(item, index) in computedCouponList" :key="index"
|
<view class="coupon-list" v-for="(item, index) in computedCouponList" :key="index"
|
||||||
@click="couponAction(item, index)">
|
@tap.stop="couponAction(item, index)">
|
||||||
<image :src="$util.img('public/uniapp/coupon/style5_bg.png')"></image>
|
<image :src="$util.img('public/uniapp/coupon/style5_bg.png')"></image>
|
||||||
<view class="coupon">
|
<view class="coupon">
|
||||||
<view class="coupon-info">
|
<view class="coupon-info">
|
||||||
@@ -199,7 +199,7 @@
|
|||||||
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/style6-bg-1.png') + ')',
|
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/style6-bg-1.png') + ')',
|
||||||
marginRight: couponItemHeight + 'px',
|
marginRight: couponItemHeight + 'px',
|
||||||
marginLeft: couponItemHeight + 'px'
|
marginLeft: couponItemHeight + 'px'
|
||||||
}" @click="couponAction(item, index)">
|
}" @tap.stop="couponAction(item, index)">
|
||||||
<view class="coupon-content">
|
<view class="coupon-content">
|
||||||
<view class="price-wrap">
|
<view class="price-wrap">
|
||||||
<text class="price" :style="{ color: value.moneyColor }">{{ (item.discount == '0.00'
|
<text class="price" :style="{ color: value.moneyColor }">{{ (item.discount == '0.00'
|
||||||
@@ -229,7 +229,7 @@
|
|||||||
<text class="limit" :style="{ color: value.limitColor }" v-else>无门槛使用</text>
|
<text class="limit" :style="{ color: value.limitColor }" v-else>无门槛使用</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<div v-if="computedCouponList.length <= 2" @click="$util.redirectTo('/pages_goods/category')"
|
<div v-if="computedCouponList.length <= 2" @tap.stop="$util.redirectTo('/pages_goods/category')"
|
||||||
class="coupon coupon-null" :style="{
|
class="coupon coupon-null" :style="{
|
||||||
color: value.moneyColor,
|
color: value.moneyColor,
|
||||||
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/style6-bg-2.png') + ')',
|
backgroundImage: 'url(' + $util.img('public/uniapp/coupon/style6-bg-2.png') + ')',
|
||||||
@@ -250,7 +250,7 @@
|
|||||||
<scroll-view class="coupon-style-seven" scroll-x="true">
|
<scroll-view class="coupon-style-seven" scroll-x="true">
|
||||||
<view class="wrap">
|
<view class="wrap">
|
||||||
<view class="coupon-list" v-for="(item, index) in computedCouponList" :key="index"
|
<view class="coupon-list" v-for="(item, index) in computedCouponList" :key="index"
|
||||||
@click="couponAction(item, index)">
|
@tap.stop="couponAction(item, index)">
|
||||||
<image :src="$util.img('public/uniapp/coupon/style7_bg.png')"></image>
|
<image :src="$util.img('public/uniapp/coupon/style7_bg.png')"></image>
|
||||||
<view class="coupon">
|
<view class="coupon">
|
||||||
<view class="coupon-info">
|
<view class="coupon-info">
|
||||||
@@ -454,7 +454,7 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.$refs.login.open('/pages/index/index');
|
this.$refs.login.open(this.$util.INDEX_PAGE_URL);
|
||||||
this.couponBtnSwitch = false;
|
this.couponBtnSwitch = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -488,7 +488,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 风格一
|
// 风格一
|
||||||
/deep/.coupon-style-one {
|
::v-deep .coupon-style-one {
|
||||||
height: 110rpx;
|
height: 110rpx;
|
||||||
|
|
||||||
.coupon-item-box {
|
.coupon-item-box {
|
||||||
@@ -534,7 +534,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 风格二
|
// 风格二
|
||||||
/deep/.coupon-style-two {
|
::v-deep .coupon-style-two {
|
||||||
height: 96rpx;
|
height: 96rpx;
|
||||||
|
|
||||||
.coupon-item-box {
|
.coupon-item-box {
|
||||||
@@ -591,7 +591,7 @@ export default {
|
|||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/.coupon-style-three {
|
::v-deep .coupon-style-three {
|
||||||
height: 284rpx;
|
height: 284rpx;
|
||||||
padding: 20rpx;
|
padding: 20rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -638,7 +638,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 风格四
|
// 风格四
|
||||||
/deep/.coupon-style-four {
|
::v-deep .coupon-style-four {
|
||||||
height: 108rpx;
|
height: 108rpx;
|
||||||
|
|
||||||
.coupon-item-box {
|
.coupon-item-box {
|
||||||
@@ -997,7 +997,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//风格七
|
//风格七
|
||||||
/deep/ .coupon-style-seven {
|
::v-deep .coupon-style-seven {
|
||||||
.wrap {
|
.wrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@@ -1075,7 +1075,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/.uni-scroll-view ::-webkit-scrollbar {
|
::v-deep .uni-scroll-view ::-webkit-scrollbar {
|
||||||
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
@@ -1084,7 +1084,7 @@ export default {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/::-webkit-scrollbar {
|
::v-deep ::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export default {
|
|||||||
|
|
||||||
// 页面跳转
|
// 页面跳转
|
||||||
redirectTo(item) {
|
redirectTo(item) {
|
||||||
if (!item.wap_url || this.$util.getCurrRoute() != 'pages/member/index' || this.storeToken) {
|
if (!item.wap_url || this.$util.getCurrRoute() != this.$util.MEMBER_PAGE_URL || this.storeToken) {
|
||||||
console.log(item);
|
console.log(item);
|
||||||
this.$util.diyRedirectTo(item);
|
this.$util.diyRedirectTo(item);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<view data-component-name="diy-fenxiao-goods-list" class="diy-fenxiao" v-if="list.length"
|
<view data-component-name="diy-fenxiao-goods-list" class="diy-fenxiao" v-if="list.length"
|
||||||
:class="['goods-list', value.template, value.style]" :style="goodsListWarpCss">
|
:class="['goods-list', value.template, value.style]" :style="goodsListWarpCss">
|
||||||
<view class="goods-item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
|
<view class="goods-item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
|
||||||
:class="[value.ornament.type]" :style="goodsItemCss">
|
:class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
@@ -32,14 +32,14 @@
|
|||||||
background: value.btnStyle.theme == 'diy' ? 'linear-gradient(to right,' + value.btnStyle.bgColorStart + ',' + value.btnStyle.bgColorEnd + ')' : '',
|
background: value.btnStyle.theme == 'diy' ? 'linear-gradient(to right,' + value.btnStyle.bgColorStart + ',' + value.btnStyle.bgColorEnd + ')' : '',
|
||||||
color: value.btnStyle.theme == 'diy' ? value.btnStyle.textColor : '',
|
color: value.btnStyle.theme == 'diy' ? value.btnStyle.textColor : '',
|
||||||
borderRadius: value.btnStyle.aroundRadius * 2 + 'rpx'
|
borderRadius: value.btnStyle.aroundRadius * 2 + 'rpx'
|
||||||
}" @click.stop="followGoods(item, index)">
|
}" @tap.stop="followGoods(item, index)">
|
||||||
关注
|
关注
|
||||||
</view>
|
</view>
|
||||||
<view class="sale-btn" v-if="value.btnStyle.control && item.is_collect == 1" :style="{
|
<view class="sale-btn" v-if="value.btnStyle.control && item.is_collect == 1" :style="{
|
||||||
background: value.btnStyle.theme == 'diy' ? 'linear-gradient(to right,' + value.btnStyle.bgColorStart + ',' + value.btnStyle.bgColorEnd + ')' : '',
|
background: value.btnStyle.theme == 'diy' ? 'linear-gradient(to right,' + value.btnStyle.bgColorStart + ',' + value.btnStyle.bgColorEnd + ')' : '',
|
||||||
color: value.btnStyle.theme == 'diy' ? value.btnStyle.textColor : '',
|
color: value.btnStyle.theme == 'diy' ? value.btnStyle.textColor : '',
|
||||||
borderRadius: value.btnStyle.aroundRadius * 2 + 'rpx'
|
borderRadius: value.btnStyle.aroundRadius * 2 + 'rpx'
|
||||||
}" @click.stop="delFollowTip(item, index)">
|
}" @tap.stop="delFollowTip(item, index)">
|
||||||
取消关注
|
取消关注
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -75,7 +75,7 @@ export default {
|
|||||||
this.currentRoute = '/' + currentPage.route;
|
this.currentRoute = '/' + currentPage.route;
|
||||||
if (!this.storeToken) {
|
if (!this.storeToken) {
|
||||||
this.$util.redirectTo(
|
this.$util.redirectTo(
|
||||||
'/pages_tool/login/login',
|
this.$util.LOGIN_PAGE_URL,
|
||||||
{
|
{
|
||||||
back: this.currentRoute
|
back: this.currentRoute
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
:class="{ left_top: value.bottomPosition == 1, right_top: value.bottomPosition == 2, left_bottom: value.bottomPosition == 3, right_bottom: value.bottomPosition == 4 }"
|
:class="{ left_top: value.bottomPosition == 1, right_top: value.bottomPosition == 2, left_bottom: value.bottomPosition == 3, right_bottom: value.bottomPosition == 4 }"
|
||||||
:style="style">
|
:style="style">
|
||||||
<block v-for="(item, index) in value.list" :key="index">
|
<block v-for="(item, index) in value.list" :key="index">
|
||||||
<view class="button-box" @click="$util.diyRedirectTo(item.link)"
|
<view class="button-box" @tap.stop="$util.diyRedirectTo(item.link)"
|
||||||
:style="{ width: value.imageSize + 'px', height: value.imageSize + 'px', fontSize: value.imageSize + 'px' }">
|
:style="{ width: value.imageSize + 'px', height: value.imageSize + 'px', fontSize: value.imageSize + 'px' }">
|
||||||
<image v-if="!item.iconType || item.iconType == 'img'" :src="$util.img(item.imageUrl)" mode="aspectFit"
|
<image v-if="!item.iconType || item.iconType == 'img'" :src="$util.img(item.imageUrl)" mode="aspectFit"
|
||||||
:show-menu-by-longpress="true" />
|
:show-menu-by-longpress="true" />
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view @click="submitform" class="fui-btn btn-danger block mtop">提交信息</view>
|
<view @tap.stop="submitform" class="fui-btn btn-danger block mtop">提交信息</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<view class="ul-wrap">
|
<view class="ul-wrap">
|
||||||
<view class="li-item" v-for="(item, index) in list" :key="index">
|
<view class="li-item" v-for="(item, index) in list" :key="index">
|
||||||
<image class="brand-pic" :src="$util.img(item.image_url)" mode="aspectFit"
|
<image class="brand-pic" :src="$util.img(item.image_url)" mode="aspectFit"
|
||||||
@click="handlerClick(item)" @tap="handlerClick(item)" @error="imgError(index)"
|
@tap.stop="handlerClick(item)" @error="imgError(index)"
|
||||||
:style="itemCss" />
|
:style="itemCss" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
<x-skeleton data-component-name="diy-goods-list" :type="skeletonType" :loading="loading" :configs="skeletonConfig">
|
<x-skeleton data-component-name="diy-goods-list" :type="skeletonType" :loading="loading" :configs="skeletonConfig">
|
||||||
<view :class="['goods-list', goodsValue.template, goodsValue.style]" :style="goodsListWarpCss">
|
<view :class="['goods-list', goodsValue.template, goodsValue.style]" :style="goodsListWarpCss">
|
||||||
<template v-if="goodsValue.template != 'horizontal-slide'">
|
<template v-if="goodsValue.template != 'horizontal-slide'">
|
||||||
<view class="goods-item" v-for="(item, index) in list" :key="index" @click="handlerClick(item)"
|
<view class="goods-item" v-for="(item, index) in list" :key="index" @tap.stop="handlerClick(item)"
|
||||||
@tap="handlerClick(item)" :class="[goodsValue.ornament.type]" :style="goodsItemCss">
|
:class="[goodsValue.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="goods-img-wrap">
|
<view class="goods-img-wrap">
|
||||||
<image class="goods-img"
|
<image class="goods-img"
|
||||||
:src="$util.img(item.goods_image, { size: goodsValue.template == 'large-mode' ? 'big' : 'mid' })"
|
:src="$util.img(item.goods_image, { size: goodsValue.template == 'large-mode' ? 'big' : 'mid' })"
|
||||||
mode="widthFix" @error="imgError(index)"
|
mode="widthFix" @error="imgError(index)"
|
||||||
:style="{ borderRadius: goodsValue.imgAroundRadius * 2 + 'rpx' }" />
|
:style="{ borderRadius: goodsValue.imgAroundRadius * 2 + 'rpx' }"
|
||||||
|
:show-menu-by-longpress="true" />
|
||||||
<view class="sell-out" v-if="item.stock <= 0">
|
<view class="sell-out" v-if="item.stock <= 0">
|
||||||
<text class="iconfont icon-shuqing"></text>
|
<text class="iconfont icon-shuqing"></text>
|
||||||
</view>
|
</view>
|
||||||
@@ -70,7 +71,7 @@
|
|||||||
color: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : '',
|
color: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : '',
|
||||||
borderColor: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : ''
|
borderColor: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : ''
|
||||||
}" class="cart shopping-cart-btn iconfont icon-gouwuche click-wrap" :id="'goods-' + item.id"
|
}" class="cart shopping-cart-btn iconfont icon-gouwuche click-wrap" :id="'goods-' + item.id"
|
||||||
@click.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
|
@tap.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
|
||||||
<view class="click-event"></view>
|
<view class="click-event"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -79,7 +80,7 @@
|
|||||||
color: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : '',
|
color: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : '',
|
||||||
borderColor: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : ''
|
borderColor: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : ''
|
||||||
}" class="cart plus-sign-btn iconfont icon-add1 click-wrap" :id="'goods-' + item.id"
|
}" class="cart plus-sign-btn iconfont icon-add1 click-wrap" :id="'goods-' + item.id"
|
||||||
@click.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
|
@tap.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
|
||||||
<view class="click-event"></view>
|
<view class="click-event"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@
|
|||||||
fontWeight: goodsValue.btnStyle.theme == 'diy' ? (goodsValue.btnStyle.fontWeight ? 'bold' : 'normal') : '',
|
fontWeight: goodsValue.btnStyle.theme == 'diy' ? (goodsValue.btnStyle.fontWeight ? 'bold' : 'normal') : '',
|
||||||
padding: goodsValue.btnStyle.theme == 'diy' ? '0 ' + goodsValue.btnStyle.padding * 2 + 'rpx' : ''
|
padding: goodsValue.btnStyle.theme == 'diy' ? '0 ' + goodsValue.btnStyle.padding * 2 + 'rpx' : ''
|
||||||
}" class="cart buy-btn click-wrap" :id="'goods-' + item.id"
|
}" class="cart buy-btn click-wrap" :id="'goods-' + item.id"
|
||||||
@click.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
|
@tap.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
|
||||||
{{ goodsValue.btnStyle.text }}
|
{{ goodsValue.btnStyle.text }}
|
||||||
<view class="click-event"></view>
|
<view class="click-event"></view>
|
||||||
<!-- <text class="cart-num" v-if="cartList['goods_' + item.goods_id]">{{ cartList['goods_' + item.goods_id].num }}</text> -->
|
<!-- <text class="cart-num" v-if="cartList['goods_' + item.goods_id]">{{ cartList['goods_' + item.goods_id].num }}</text> -->
|
||||||
@@ -100,7 +101,7 @@
|
|||||||
<view v-else-if="goodsValue.btnStyle.style == 'icon-diy'" :style="{
|
<view v-else-if="goodsValue.btnStyle.style == 'icon-diy'" :style="{
|
||||||
color: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : ''
|
color: goodsValue.btnStyle.theme == 'diy' ? goodsValue.btnStyle.textColor : ''
|
||||||
}" class="icon-diy click-wrap" :id="'goods-' + item.id"
|
}" class="icon-diy click-wrap" :id="'goods-' + item.id"
|
||||||
@click.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
|
@tap.stop="$refs.goodsSkuIndex.addCart(goodsValue.btnStyle.cartEvent, item, $event)">
|
||||||
<view class="click-event"></view>
|
<view class="click-event"></view>
|
||||||
<diy-icon :icon="goodsValue.btnStyle.iconDiy.icon"
|
<diy-icon :icon="goodsValue.btnStyle.iconDiy.icon"
|
||||||
:value="goodsValue.btnStyle.iconDiy.style ? goodsValue.btnStyle.iconDiy.style : null"></diy-icon>
|
:value="goodsValue.btnStyle.iconDiy.style ? goodsValue.btnStyle.iconDiy.style : null"></diy-icon>
|
||||||
@@ -112,12 +113,12 @@
|
|||||||
</template>
|
</template>
|
||||||
<scroll-view v-if="goodsValue.template == 'horizontal-slide' && goodsValue.slideMode == 'scroll'"
|
<scroll-view v-if="goodsValue.template == 'horizontal-slide' && goodsValue.slideMode == 'scroll'"
|
||||||
class="scroll" :scroll-x="true">
|
class="scroll" :scroll-x="true">
|
||||||
<view class="goods-item" v-for="(item, index) in list" :key="index" @click="handlerClick(item)"
|
<view class="goods-item" v-for="(item, index) in list" :key="index" @tap.stop="handlerClick(item)"
|
||||||
@tap="handlerClick(item)" :class="[goodsValue.ornament.type]" :style="goodsItemCss">
|
:class="[goodsValue.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="goods-img-wrap">
|
<view class="goods-img-wrap">
|
||||||
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix" @error="imgError(index)"
|
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix" @error="imgError(index)"
|
||||||
:lazy-load="true" />
|
:lazy-load="true" :show-menu-by-longpress="true" />
|
||||||
<view class="sell-out" v-if="item.stock <= 0">
|
<view class="sell-out" v-if="item.stock <= 0">
|
||||||
<text class="iconfont icon-shuqing"></text>
|
<text class="iconfont icon-shuqing"></text>
|
||||||
</view>
|
</view>
|
||||||
@@ -179,12 +180,12 @@
|
|||||||
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
|
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
|
||||||
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
|
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
|
||||||
<view class="goods-item" v-for="(dataItem, dataIndex) in list[pageIndex]" :key="dataIndex"
|
<view class="goods-item" v-for="(dataItem, dataIndex) in list[pageIndex]" :key="dataIndex"
|
||||||
@click="handlerClick(dataItem)" @tap="handlerClick(dataItem)"
|
@tap.stop="handlerClick(dataItem)"
|
||||||
:class="[goodsValue.ornament.type]" :style="goodsItemCss">
|
:class="[goodsValue.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="goods-img-wrap">
|
<view class="goods-img-wrap">
|
||||||
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
:src="$util.img(dataItem.goods_image, { size: 'mid' })" mode="widthFix"
|
:src="$util.img(dataItem.goods_image, { size: 'mid' })" mode="widthFix"
|
||||||
@error="imgError(dataIndex)" :lazy-load="true" />
|
@error="imgError(dataIndex)" :lazy-load="true" :show-menu-by-longpress="true" />
|
||||||
<view class="sell-out" v-if="dataItem.stock <= 0">
|
<view class="sell-out" v-if="dataItem.stock <= 0">
|
||||||
<text class="iconfont icon-shuqing"></text>
|
<text class="iconfont icon-shuqing"></text>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -13,11 +13,11 @@
|
|||||||
<swiper-item v-for="(item, index) in page" :key="index"
|
<swiper-item v-for="(item, index) in page" :key="index"
|
||||||
:class="['swiper-item', [list[index].length / 3] >= 1 && 'flex-between']">
|
:class="['swiper-item', [list[index].length / 3] >= 1 && 'flex-between']">
|
||||||
<view class="goods-item" v-for="(dataItem, dataIndex) in list[index]" :key="dataIndex"
|
<view class="goods-item" v-for="(dataItem, dataIndex) in list[index]" :key="dataIndex"
|
||||||
@click="toDetail(dataItem)" :class="[goodsValue.ornament.type]" :style="goodsItemCss">
|
@tap.stop="toDetail(dataItem)" :class="[goodsValue.ornament.type]" :style="goodsItemCss">
|
||||||
<div class="goods-img-wrap">
|
<div class="goods-img-wrap">
|
||||||
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image class="goods-img" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
:src="$util.img(dataItem.goods_image, { size: 'mid' })" mode="widthFix"
|
:src="$util.img(dataItem.goods_image, { size: 'mid' })" mode="widthFix"
|
||||||
@error="imgError(index, dataIndex)" :lazy-load="true" />
|
@error="imgError(index, dataIndex)" :lazy-load="true" :show-menu-by-longpress="true" />
|
||||||
<view class="sell-out" v-if="dataItem.stock <= 0">
|
<view class="sell-out" v-if="dataItem.stock <= 0">
|
||||||
<text class="iconfont icon-shuqing"></text>
|
<text class="iconfont icon-shuqing"></text>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
<view class="graphic-nav-item" :class="[value.mode]" v-for="(item, index) in value.list"
|
<view class="graphic-nav-item" :class="[value.mode]" v-for="(item, index) in value.list"
|
||||||
:key="index"
|
:key="index"
|
||||||
v-if="index >= [(numItem) * (value.pageCount * value.rowCount)] && index < [(numItem + 1) * (value.pageCount * value.rowCount)]"
|
v-if="index >= [(numItem) * (value.pageCount * value.rowCount)] && index < [(numItem + 1) * (value.pageCount * value.rowCount)]"
|
||||||
:style="{ width: 100 / value.rowCount + '%' }" @click="redirectTo(item.link)">
|
:style="{ width: 100 / value.rowCount + '%' }" @tap.stop="redirectTo(item.link)">
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
<!-- #ifdef H5 -->
|
<!-- #ifdef H5 -->
|
||||||
<view class="graphic-nav-item" :class="[value.mode]" v-for="(item, index) in value.list"
|
<view class="graphic-nav-item" :class="[value.mode]" v-for="(item, index) in value.list"
|
||||||
:key="index"
|
:key="index"
|
||||||
v-if="index >= [(numItem - 1) * (value.pageCount * value.rowCount)] && index < [numItem * (value.pageCount * value.rowCount)]"
|
v-if="index >= [(numItem - 1) * (value.pageCount * value.rowCount)] && index < [numItem * (value.pageCount * value.rowCount)]"
|
||||||
:style="{ width: 100 / value.rowCount + '%' }" @click="redirectTo(item.link)">
|
:style="{ width: 100 / value.rowCount + '%' }" @tap.stop="redirectTo(item.link)">
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
<view class="graphic-img" v-if="value.mode != 'text'"
|
<view class="graphic-img" v-if="value.mode != 'text'"
|
||||||
:style="{ fontSize: value.imageSize * 2 + 'rpx', width: value.imageSize * 2 + 'rpx', height: value.imageSize * 2 + 'rpx' }">
|
:style="{ fontSize: value.imageSize * 2 + 'rpx', width: value.imageSize * 2 + 'rpx', height: value.imageSize * 2 + 'rpx' }">
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
|
|
||||||
<view class="graphic-nav-item" :class="[value.mode]" v-for="(item, index) in value.list" :key="index"
|
<view class="graphic-nav-item" :class="[value.mode]" v-for="(item, index) in value.list" :key="index"
|
||||||
:style="{ width: 100 / value.rowCount + '%' }" @click="redirectTo(item.link)">
|
:style="{ width: 100 / value.rowCount + '%' }" @tap.stop="redirectTo(item.link)">
|
||||||
<view class="graphic-img" v-if="value.mode != 'text'"
|
<view class="graphic-img" v-if="value.mode != 'text'"
|
||||||
:style="{ fontSize: value.imageSize * 2 + 'rpx', width: value.imageSize * 2 + 'rpx', height: value.imageSize * 2 + 'rpx' }">
|
:style="{ fontSize: value.imageSize * 2 + 'rpx', width: value.imageSize * 2 + 'rpx', height: value.imageSize * 2 + 'rpx' }">
|
||||||
<image v-if="item.iconType == 'img'"
|
<image v-if="item.iconType == 'img'"
|
||||||
@@ -159,7 +159,7 @@ export default {
|
|||||||
redirectTo(link) {
|
redirectTo(link) {
|
||||||
|
|
||||||
if (link.wap_url) {
|
if (link.wap_url) {
|
||||||
if (this.$util.getCurrRoute() == 'pages/member/index' && !this.storeToken) {
|
if (this.$util.getCurrRoute() == this.$util.MEMBER_PAGE_URL && !this.storeToken) {
|
||||||
this.$refs.login.open(link.wap_url);
|
this.$refs.login.open(link.wap_url);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -251,6 +251,16 @@
|
|||||||
<diy-icon :value="item"></diy-icon>
|
<diy-icon :value="item"></diy-icon>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template v-if="item.componentName == 'Tab'">
|
||||||
|
<!-- Tab 组件 -->
|
||||||
|
<diy-tab :value="item" :diyGlobal="diyGlobalData"></diy-tab>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-if="['ChannelList', 'WechatChannel'].includes(item.componentName)">
|
||||||
|
<!-- 视频号列表 -->
|
||||||
|
<diy-channel-list :value="item"></diy-channel-list>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 自定义扩展组件 -->
|
<!-- 自定义扩展组件 -->
|
||||||
<diy-comp-extend :value="item"></diy-comp-extend>
|
<diy-comp-extend :value="item"></diy-comp-extend>
|
||||||
</view>
|
</view>
|
||||||
@@ -260,8 +270,9 @@
|
|||||||
<script>
|
<script>
|
||||||
// 组件组展示
|
// 组件组展示
|
||||||
import DiyMinx from './minx.js'
|
import DiyMinx from './minx.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {},
|
name: 'diy-group',
|
||||||
props: {
|
props: {
|
||||||
diyData: {
|
diyData: {
|
||||||
type: Object
|
type: Object
|
||||||
@@ -306,7 +317,12 @@ export default {
|
|||||||
this.diyGlobalData.value.forEach((item, index) => {
|
this.diyGlobalData.value.forEach((item, index) => {
|
||||||
item.pageStyle = '';
|
item.pageStyle = '';
|
||||||
// 给每个组件增加位置属性,用于定位,搜索、分类导航等定位
|
// 给每个组件增加位置属性,用于定位,搜索、分类导航等定位
|
||||||
item.moduleIndex = index + 1;
|
// 如果是Tab组件内部的子组件,添加tabIndex前缀避免moduleIndex冲突
|
||||||
|
if (this.diyData.tabIndex !== undefined) {
|
||||||
|
item.moduleIndex = `${this.diyData.tabIndex}-${index + 1}`;
|
||||||
|
} else {
|
||||||
|
item.moduleIndex = index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
// 特殊处理搜索框 当显示位置为滚动至顶部固定时,只设置背景颜色
|
// 特殊处理搜索框 当显示位置为滚动至顶部固定时,只设置背景颜色
|
||||||
if (item.componentName == 'Search' && item.positionWay == 'fixed') {
|
if (item.componentName == 'Search' && item.positionWay == 'fixed') {
|
||||||
@@ -341,6 +357,12 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else data = this.setPagestyle;
|
} else data = this.setPagestyle;
|
||||||
|
|
||||||
|
console.log(`diy-group ['diyDataArray'] = `, {
|
||||||
|
data: data,
|
||||||
|
diyData: this.diyData,
|
||||||
|
diyGlobalData: this.diyGlobalData,
|
||||||
|
})
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<x-skeleton data-component-name="diy-groupbuy" :type="skeletonType" :loading="loading" :configs="skeletonConfig">
|
<x-skeleton data-component-name="diy-groupbuy" :type="skeletonType" :loading="loading" :configs="skeletonConfig">
|
||||||
<view class="diy-groupbuy" :class="[value.template, value.style]" :style="warpCss">
|
<view class="diy-groupbuy" :class="[value.template, value.style]" :style="warpCss">
|
||||||
<template v-if="value.template == 'row1-of1'">
|
<template v-if="value.template == 'row1-of1'">
|
||||||
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
|
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
|
||||||
:class="[value.ornament.type]" :style="goodsItemCss">
|
:class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-if="value.template == 'horizontal-slide'">
|
<template v-if="value.template == 'horizontal-slide'">
|
||||||
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
|
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
|
||||||
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
|
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
|
||||||
:class="[value.ornament.type]" :style="goodsItemCss">
|
:class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
|
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
|
||||||
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
|
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
|
||||||
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
|
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
|
||||||
@click="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
|
@tap.stop="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
||||||
@@ -274,7 +274,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
/deep/.uni-scroll-view ::-webkit-scrollbar {
|
::v-deep .uni-scroll-view ::-webkit-scrollbar {
|
||||||
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
@@ -283,7 +283,7 @@ export default {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/::-webkit-scrollbar {
|
::v-deep ::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
height: mapItem.height + '%',
|
height: mapItem.height + '%',
|
||||||
left: mapItem.left + '%',
|
left: mapItem.left + '%',
|
||||||
top: mapItem.top + '%'
|
top: mapItem.top + '%'
|
||||||
}" @click.stop="$util.diyRedirectTo(mapItem.link)"></view>
|
}" @tap.stop="$util.diyRedirectTo(mapItem.link)"></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -15,12 +15,12 @@
|
|||||||
'width': (item.imgWidth / 2 + 'rpx') + ';',
|
'width': (item.imgWidth / 2 + 'rpx') + ';',
|
||||||
'height': (item.imgHeight / 2 + 'rpx') + ';'
|
'height': (item.imgHeight / 2 + 'rpx') + ';'
|
||||||
}" :src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')"
|
}" :src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')"
|
||||||
:show-menu-by-longpress="true" @tap="redirectTo(item.link)"></image>
|
:show-menu-by-longpress="true" @tap.stop="redirectTo(item.link)"></image>
|
||||||
<image v-else :style="{
|
<image v-else :style="{
|
||||||
'width': (item.imgWidth / 2 + 'rpx') + ';',
|
'width': (item.imgWidth / 2 + 'rpx') + ';',
|
||||||
'height': (item.imgHeight / 2 + 'rpx') + ';'
|
'height': (item.imgHeight / 2 + 'rpx') + ';'
|
||||||
}" :src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')"
|
}" :src="$util.img(item.imageUrl) || $util.img('public/uniapp/default_img/goods.png')"
|
||||||
:show-menu-by-longpress="true" @tap="previewImg(item.imageUrl)"></image>
|
:show-menu-by-longpress="true" @tap.stop="previewImg(item.imageUrl)"></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 文字部分 -->
|
<!-- 文字部分 -->
|
||||||
@@ -103,20 +103,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 预览图片
|
|
||||||
previewImg(imageUrl) {
|
|
||||||
uni.previewImage({
|
|
||||||
current: 0,
|
|
||||||
urls: [this.$util.img(imageUrl)],
|
|
||||||
success: (res) => { },
|
|
||||||
fail: (res) => { },
|
|
||||||
complete: (res) => { }
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 页面跳转
|
// 页面跳转
|
||||||
redirectTo(link) {
|
redirectTo(link) {
|
||||||
if (!link.wap_url || this.$util.getCurrRoute() != 'pages/member/index' || this.storeToken) {
|
if (!link.wap_url || this.$util.getCurrRoute() != this.$util.MEMBER_PAGE_URL || this.storeToken) {
|
||||||
this.$util.diyRedirectTo(link);
|
this.$util.diyRedirectTo(link);
|
||||||
} else {
|
} else {
|
||||||
this.$refs.login.open(link.wap_url);
|
this.$refs.login.open(link.wap_url);
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
<view data-component-name="diy-img-ads" class="single-graph">
|
<view data-component-name="diy-img-ads" class="single-graph">
|
||||||
<view :style="imgAdsMarginWarp" class="swiper-box">
|
<view :style="imgAdsMarginWarp" class="swiper-box">
|
||||||
<block v-if="imgAdsValue.list.length == 1">
|
<block v-if="imgAdsValue.list.length == 1">
|
||||||
<view class="simple-graph-wrap" :style="imgAdsSwiper" @click="handlerClick(imgAdsValue.list[0].link)"
|
<view class="simple-graph-wrap" :style="imgAdsSwiper" @tap.stop="handlerClick(imgAdsValue.list[0].link)">
|
||||||
@tap="handlerClick(imgAdsValue.list[0].link)">
|
|
||||||
<image :style="{ height: imgAdsValue.list[0].imgHeight }"
|
<image :style="{ height: imgAdsValue.list[0].imgHeight }"
|
||||||
:src="$util.img(imgAdsValue.list[0].imageUrl)" mode="widthFix" :show-menu-by-longpress="true" />
|
:src="$util.img(imgAdsValue.list[0].imageUrl)" mode="widthFix" :show-menu-by-longpress="true" />
|
||||||
</view>
|
</view>
|
||||||
@@ -16,7 +15,7 @@
|
|||||||
indicator-color="rgba(130, 130, 130, .5)" :indicator-active-color="imgAdsValue.indicatorColor"
|
indicator-color="rgba(130, 130, 130, .5)" :indicator-active-color="imgAdsValue.indicatorColor"
|
||||||
@change="swiperChange">
|
@change="swiperChange">
|
||||||
<swiper-item class="swiper-item" :style="imgAdsSwiper" v-for="(item, index) in imgAdsValue.list"
|
<swiper-item class="swiper-item" :style="imgAdsSwiper" v-for="(item, index) in imgAdsValue.list"
|
||||||
:key="index" v-if="item.imageUrl" @click="handlerClick(item.link)" @tap="handlerClick(item.link)">
|
:key="index" v-if="item.imageUrl" @tap.stop="handlerClick(item.link)">
|
||||||
<view class="item" :style="imgAdsSwiper + 'height: ' + item.imgHeight">
|
<view class="item" :style="imgAdsSwiper + 'height: ' + item.imgHeight">
|
||||||
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
|
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
|
||||||
:show-menu-by-longpress="true" />
|
:show-menu-by-longpress="true" />
|
||||||
@@ -280,39 +279,39 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
||||||
/deep/.uni-scroll-view::-webkit-scrollbar {
|
::v-deep .uni-scroll-view::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper /deep/ .uni-swiper-dots-horizontal {
|
.swiper ::v-deep .uni-swiper-dots-horizontal {
|
||||||
bottom: 25rpx;
|
bottom: 25rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper-left /deep/ .uni-swiper-dots-horizontal {
|
.swiper-left ::v-deep .uni-swiper-dots-horizontal {
|
||||||
left: 40rpx;
|
left: 40rpx;
|
||||||
transform: translate(0);
|
transform: translate(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper-right /deep/ .uni-swiper-dots-horizontal {
|
.swiper-right ::v-deep .uni-swiper-dots-horizontal {
|
||||||
right: 40rpx;
|
right: 40rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
transform: translate(0);
|
transform: translate(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.carousel-angle /deep/ .uni-swiper-dots-horizontal .uni-swiper-dot {
|
.carousel-angle ::v-deep .uni-swiper-dots-horizontal .uni-swiper-dot {
|
||||||
width: 24rpx;
|
width: 24rpx;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
height: 8rpx;
|
height: 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper.ns-indicator-dots /deep/ .uni-swiper-dot {
|
.swiper.ns-indicator-dots ::v-deep .uni-swiper-dot {
|
||||||
width: 18rpx;
|
width: 18rpx;
|
||||||
height: 6rpx;
|
height: 6rpx;
|
||||||
border-radius: 4rpx;
|
border-radius: 4rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper.ns-indicator-dots /deep/ .uni-swiper-dot-active {
|
.swiper.ns-indicator-dots ::v-deep .uni-swiper-dot-active {
|
||||||
width: 36rpx;
|
width: 36rpx;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
:style="{ background: value.backgroundColor ? value.backgroundColor : '', width: 'calc(100% - 48rpx)' }"
|
:style="{ background: value.backgroundColor ? value.backgroundColor : '', width: 'calc(100% - 48rpx)' }"
|
||||||
@touchmove.stop>
|
@touchmove.stop>
|
||||||
<view class="item" :id="'a' + index" v-for="(item, index) in cateList" :key="index"
|
<view class="item" :id="'a' + index" v-for="(item, index) in cateList" :key="index"
|
||||||
@click="changePageIndex(index)" :class="{ fill: value.styleType == 'fill' }"
|
@tap.stop="changePageIndex(index)" :class="{ fill: value.styleType == 'fill' }"
|
||||||
:style="{ background: index == pageIndex && value.styleType == 'fill' ? value.selectColor : '' }">
|
:style="{ background: index == pageIndex && value.styleType == 'fill' ? value.selectColor : '' }">
|
||||||
<view class="text-con" :class="index == pageIndex ? 'active' : ''" :style="{
|
<view class="text-con" :class="index == pageIndex ? 'active' : ''" :style="{
|
||||||
color: index == pageIndex ? '' : value.noColor
|
color: index == pageIndex ? '' : value.noColor
|
||||||
@@ -25,13 +25,13 @@
|
|||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
<text class="iconfont icon-unfold unfold-arrows" :style="{ color: value.moreColor }"
|
<text class="iconfont icon-unfold unfold-arrows" :style="{ color: value.moreColor }"
|
||||||
@click="unfoldMenu"></text>
|
@tap.stop="unfoldMenu"></text>
|
||||||
</view>
|
</view>
|
||||||
<uni-popup ref="navTopCategoryPop" type="top" :top="uniPopTop">
|
<uni-popup ref="navTopCategoryPop" type="top" :top="uniPopTop">
|
||||||
<view class="nav-topcategory-pop">
|
<view class="nav-topcategory-pop">
|
||||||
<text v-for="(item, index) in cateList" :key="index"
|
<text v-for="(item, index) in cateList" :key="index"
|
||||||
:class="['category-item', { 'color-base-text color-base-border active': pageIndex == index }]"
|
:class="['category-item', { 'color-base-text color-base-border active': pageIndex == index }]"
|
||||||
@click="changePageIndex(index)">
|
@tap.stop="changePageIndex(index)">
|
||||||
{{ item.short_name ? item.short_name : item.category_name }}
|
{{ item.short_name ? item.short_name : item.category_name }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
<view class="twoCategory min" v-if="twoCategorylist.length <= 5">
|
<view class="twoCategory min" v-if="twoCategorylist.length <= 5">
|
||||||
<view class="twoCategory-page">
|
<view class="twoCategory-page">
|
||||||
<view class="swiper-item" v-for="(item, index) in twoCategorylist"
|
<view class="swiper-item" v-for="(item, index) in twoCategorylist"
|
||||||
:key="index" @click="toCateGoodsList(item.category_id_2, 2)">
|
:key="index" @tap.stop="toCateGoodsList(item.category_id_2, 2)">
|
||||||
<view class="item-box">
|
<view class="item-box">
|
||||||
<image :src="$util.img(item.image)" v-if="item.image"
|
<image :src="$util.img(item.image)" v-if="item.image"
|
||||||
mode="aspectFill" />
|
mode="aspectFill" />
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
v-if="twoCategorylist.length > 5 && twoCategorylist.length <= 10">
|
v-if="twoCategorylist.length > 5 && twoCategorylist.length <= 10">
|
||||||
<view class="twoCategory-page">
|
<view class="twoCategory-page">
|
||||||
<view class="swiper-item" v-for="(item, index) in twoCategorylist"
|
<view class="swiper-item" v-for="(item, index) in twoCategorylist"
|
||||||
:key="index" @click="toCateGoodsList(item.category_id_2, 2)">
|
:key="index" @tap.stop="toCateGoodsList(item.category_id_2, 2)">
|
||||||
<view class="item-box">
|
<view class="item-box">
|
||||||
<image :src="$util.img(item.image)" v-if="item.image"
|
<image :src="$util.img(item.image)" v-if="item.image"
|
||||||
mode="aspectFill" />
|
mode="aspectFill" />
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
<swiper-item class="twoCategory-page" v-for="page in maxPage" :key="page">
|
<swiper-item class="twoCategory-page" v-for="page in maxPage" :key="page">
|
||||||
<view class="swiper-item" v-for="(item, index) in twoCategorylist"
|
<view class="swiper-item" v-for="(item, index) in twoCategorylist"
|
||||||
:key="index" v-if="index >= (page - 1) * 10 && index < page * 10"
|
:key="index" v-if="index >= (page - 1) * 10 && index < page * 10"
|
||||||
@click="toCateGoodsList(item.category_id_2, 2)">
|
@tap.stop="toCateGoodsList(item.category_id_2, 2)">
|
||||||
<view class="item-box">
|
<view class="item-box">
|
||||||
<image :src="item.image" mode="aspectFill" />
|
<image :src="item.image" mode="aspectFill" />
|
||||||
<view>{{ item.category_name }}</view>
|
<view>{{ item.category_name }}</view>
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
|
|
||||||
<view class="goods-list double-column" v-if="goodsList[pageIndex].list.length">
|
<view class="goods-list double-column" v-if="goodsList[pageIndex].list.length">
|
||||||
<view class="goods-item" v-for="(item, index) in goodsList[pageIndex].list"
|
<view class="goods-item" v-for="(item, index) in goodsList[pageIndex].list"
|
||||||
:key="index" @click="toDetail(item)">
|
:key="index" @tap.stop="toDetail(item)">
|
||||||
<view class="goods-img">
|
<view class="goods-img">
|
||||||
<image :src="goodsImg(item.goods_image)" mode="widthFix"
|
<image :src="goodsImg(item.goods_image)" mode="widthFix"
|
||||||
@error="imgError(index)" />
|
@error="imgError(index)" />
|
||||||
@@ -732,7 +732,7 @@ export default {
|
|||||||
margin-top: 100rpx;
|
margin-top: 100rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/.uni-scroll-view::-webkit-scrollbar {
|
::v-deep .uni-scroll-view::-webkit-scrollbar {
|
||||||
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view data-component-name="diy-kefu" class="diy-kefu" :style="style">
|
<view data-component-name="diy-kefu" class="diy-kefu" :style="style">
|
||||||
<view class="fui-list-group merchgroup" v-for="(item, index) in value.list">
|
<view class="fui-list-group merchgroup" v-for="(item, index) in value.list" :key="index">
|
||||||
<view class="fui-list jump" v-if="index == 0">
|
<view class="fui-list jump" v-if="index == 0">
|
||||||
<view class="fui-list-media">
|
<view class="fui-list-media">
|
||||||
<image class="round" :src="$util.img(item.imageUrl)" style="border-radius:6rpx"></image>
|
<image class="round" :src="$util.img(item.imageUrl)" style="border-radius:6rpx"></image>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="fui-remark jump" style="padding-right: 20rpx; text-align: center; line-height: 140rpx;">
|
<view class="fui-remark jump" style="padding-right: 20rpx; text-align: center; line-height: 140rpx;">
|
||||||
<span style="font-size:24rpx;padding: 14rpx 18rpx;border-radius:8rpx"
|
<span style="font-size:24rpx;padding: 14rpx 18rpx;border-radius:8rpx"
|
||||||
:style="{ background: item.BtBgColor, color: item.BtColor }" @click="previewSqs()">立即添加</span>
|
:style="{ background: item.BtBgColor, color: item.BtColor }" @tap.stop="previewSqs()">立即添加</span>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<view class="fui-cell-group">
|
<view class="fui-cell-group">
|
||||||
<!-- <image mode="widthFix" style="width: 100%;" :src="$util.img(item.imageUrl)"></image> -->
|
<!-- <image mode="widthFix" style="width: 100%;" :src="$util.img(item.imageUrl)"></image> -->
|
||||||
|
|
||||||
<view v-for="(item, index) in value.list" @click="redirectTo(item.link)" class="fui-cell"
|
<view v-for="(item, index) in value.list" @tap.stop="redirectTo(item.link)" class="fui-cell"
|
||||||
:class="item.iconType == 'img' ? 'img-cell' : ''">
|
:class="item.iconType == 'img' ? 'img-cell' : ''">
|
||||||
<view class="fui-cell-icon" :style="{ 'color': item.style ? item.style.iconColor : '#333' }">
|
<view class="fui-cell-icon" :style="{ 'color': item.style ? item.style.iconColor : '#333' }">
|
||||||
<diy-icon v-if="item.iconType == 'icon'" :icon="item.icon" :value="item.style ? item.style : null"
|
<diy-icon v-if="item.iconType == 'icon'" :icon="item.icon" :value="item.style ? item.style : null"
|
||||||
@@ -62,8 +62,8 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
redirectTo(link) {
|
redirectTo(link) {
|
||||||
if (link.wap_url) {
|
if (link.wap_url) {
|
||||||
if (this.$util.getCurrRoute() == 'pages/member/index' && !this.storeToken) {
|
if (this.$util.getCurrRoute() == this.$util.MEMBER_PAGE_URL && !this.storeToken) {
|
||||||
this.$refs.login.open(link.wap_url);
|
this.$refs?.login?.open(link.wap_url);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<x-skeleton data-component-name="diy-live" type="banner" :loading="loading" :configs="skeletonConfig">
|
<x-skeleton data-component-name="diy-live" type="banner" :loading="loading" :configs="skeletonConfig">
|
||||||
<view class="live-wrap" @click="handlerClick(liveInfo.roomid)" @tap="handlerClick(liveInfo.roomid)"
|
<view class="live-wrap" @tap.stop="handlerClick(liveInfo.roomid)"
|
||||||
v-if="liveInfo">
|
v-if="liveInfo">
|
||||||
<view class="banner-wrap">
|
<view class="banner-wrap">
|
||||||
<image
|
<image
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<scroll-view scroll-x="true" class="many-goods-list-head" :scroll-into-view="'a' + cateIndex"
|
<scroll-view scroll-x="true" class="many-goods-list-head" :scroll-into-view="'a' + cateIndex"
|
||||||
:style="manyWrapCss">
|
:style="manyWrapCss">
|
||||||
<view v-for="(item, index) in value.list" class="scroll-item" :class="{ active: index == cateIndex }"
|
<view v-for="(item, index) in value.list" class="scroll-item" :class="{ active: index == cateIndex }"
|
||||||
:id="'a' + index" :key="index" @click="handlerClick({ item, index })" @tap="handlerClick({ item, index })">
|
:id="'a' + index" :key="index" @tap.stop="handlerClick({ item, index })">
|
||||||
<view class="split-line" v-if="index > 0"></view>
|
<view class="split-line" v-if="index > 0"></view>
|
||||||
<view class="cate">
|
<view class="cate">
|
||||||
<view class="name" :style="{ color: value.headStyle.titleColor }">{{ item.title }}</view>
|
<view class="name" :style="{ color: value.headStyle.titleColor }">{{ item.title }}</view>
|
||||||
|
|||||||
@@ -3,11 +3,18 @@
|
|||||||
<view class="fui-list-group merchgroup" style="margin-top:0" v-for="(item, index) in value.list">
|
<view class="fui-list-group merchgroup" style="margin-top:0" v-for="(item, index) in value.list">
|
||||||
<map id="map" style="width: 100%; height:600rpx" scale="12" :markers="markerst" bindupdated="bindupdated"
|
<map id="map" style="width: 100%; height:600rpx" scale="12" :markers="markerst" bindupdated="bindupdated"
|
||||||
:longitude="item.lng" :latitude="item.lat" show-location>
|
:longitude="item.lng" :latitude="item.lat" show-location>
|
||||||
<cover-view
|
<!-- <cover-view
|
||||||
style="position:absolute;right:10px;bottom:30rpx;z-index:99999;background:#4390FF;padding:5px 10px;wxcs_style_padding:10rpx 20rpx;border-radius:8rpx;color: #fff;"
|
style="position:absolute;right:10px;bottom:30rpx;z-index:99999;background:#4390FF;padding:5px 10px;wxcs_style_padding:10rpx 20rpx;border-radius:8rpx;color: #fff;"
|
||||||
@click="handlerClick(item)" @tap="handlerClick(item)">
|
@tap.stop="handlerClick(item)" @tap="handlerClick(item)">
|
||||||
<cover-view style="font-size:24rpx">一键导航</cover-view>
|
<cover-view style="font-size:24rpx">一键导航</cover-view>
|
||||||
</cover-view>
|
</cover-view> -->
|
||||||
|
|
||||||
|
<!-- 使用非原生cover-view, 解决原生cover-view组件渲染机制z-index失效的问题 -->
|
||||||
|
<div
|
||||||
|
style="position:absolute;right:12rpx;bottom:48rpx;z-index:1;background:#4390FF;padding:0rpx 20rpx;border-radius:8rpx;color: #fff;"
|
||||||
|
@tap.stop="handlerClick(item)">
|
||||||
|
<span style="font-size:24rpx;color: #fff;">一键导航</span>
|
||||||
|
</div>
|
||||||
</map>
|
</map>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<view class="common-wrap info-wrap" :class="[`data-style-${value.style}`]">
|
<view class="common-wrap info-wrap" :class="[`data-style-${value.style}`]">
|
||||||
<view class="member-info" :style="memberInfoStyle">
|
<view class="member-info" :style="memberInfoStyle">
|
||||||
<view class="info-wrap" :style="infoStyle" v-if="memberInfo">
|
<view class="info-wrap" :style="infoStyle" v-if="memberInfo">
|
||||||
<view class="headimg" @click="getWxAuth">
|
<view class="headimg" @tap.stop="getWxAuth">
|
||||||
<image :src="memberInfo.headimg ? $util.img(memberInfo.headimg) : $util.getDefaultImage().head"
|
<image :src="memberInfo.headimg ? $util.img(memberInfo.headimg) : $util.getDefaultImage().head"
|
||||||
mode="widthFix" @error="memberInfo.headimg = $util.getDefaultImage().head" />
|
mode="widthFix" @error="memberInfo.headimg = $util.getDefaultImage().head" />
|
||||||
</view>
|
</view>
|
||||||
@@ -12,10 +12,10 @@
|
|||||||
<!-- #ifdef MP -->
|
<!-- #ifdef MP -->
|
||||||
<block
|
<block
|
||||||
v-if="(memberInfo.nickname.indexOf('u_') != -1 && memberInfo.nickname == memberInfo.username) || memberInfo.nickname == memberInfo.mobile">
|
v-if="(memberInfo.nickname.indexOf('u_') != -1 && memberInfo.nickname == memberInfo.username) || memberInfo.nickname == memberInfo.mobile">
|
||||||
<view class="nickname"><text class="name" @click="getWxAuth">点击授权头像昵称</text></view>
|
<view class="nickname"><text class="name" @tap.stop="getWxAuth">点击授权头像昵称</text></view>
|
||||||
</block>
|
</block>
|
||||||
<view class="nickname" v-else>
|
<view class="nickname" v-else>
|
||||||
<text class="name" @click="getWxAuth">{{ memberInfo.nickname }}</text>
|
<text class="name" @tap.stop="getWxAuth">{{ memberInfo.nickname }}</text>
|
||||||
<view class="member-level"
|
<view class="member-level"
|
||||||
v-if="(value.style == 1 || value.style == 2) && memberInfo.member_level">
|
v-if="(value.style == 1 || value.style == 2) && memberInfo.member_level">
|
||||||
<!-- <text class="icondiy icon-system-huangguan"></text> -->
|
<!-- <text class="icondiy icon-system-huangguan"></text> -->
|
||||||
@@ -36,10 +36,10 @@
|
|||||||
<!-- #ifdef H5 -->
|
<!-- #ifdef H5 -->
|
||||||
<block
|
<block
|
||||||
v-if="$util.isWeiXin() && ((memberInfo.nickname.indexOf('u_') != -1 && memberInfo.nickname == memberInfo.username) || memberInfo.nickname == memberInfo.mobile)">
|
v-if="$util.isWeiXin() && ((memberInfo.nickname.indexOf('u_') != -1 && memberInfo.nickname == memberInfo.username) || memberInfo.nickname == memberInfo.mobile)">
|
||||||
<view class="nickname"><text class="name" @click="getWxAuth">点击获取微信头像</text></view>
|
<view class="nickname"><text class="name" @tap.stop="getWxAuth">点击获取微信头像</text></view>
|
||||||
</block>
|
</block>
|
||||||
<view class="nickname" v-else>
|
<view class="nickname" v-else>
|
||||||
<text class="name" @click="redirect('/pages_tool/member/info')">{{ memberInfo.nickname
|
<text class="name" @tap.stop="redirect('/pages_tool/member/info')">{{ memberInfo.nickname
|
||||||
}}</text>
|
}}</text>
|
||||||
<view class="member-level"
|
<view class="member-level"
|
||||||
v-if="(value.style == 1 || value.style == 2) && memberInfo.member_level">
|
v-if="(value.style == 1 || value.style == 2) && memberInfo.member_level">
|
||||||
@@ -61,10 +61,10 @@
|
|||||||
</view>
|
</view>
|
||||||
<view v-if="ischina == 1"
|
<view v-if="ischina == 1"
|
||||||
style="background: #fff;height: 60rpx;width: 60rpx;border-radius: 50rpx;line-height:65rpx;text-align: center;color:#000"
|
style="background: #fff;height: 60rpx;width: 60rpx;border-radius: 50rpx;line-height:65rpx;text-align: center;color:#000"
|
||||||
@click.stop="modifyInfo()">{{ langIndex == 0 ? 'CN' : 'EN' }}</view>
|
@tap.stop="modifyInfo()">{{ langIndex == 0 ? 'CN' : 'EN' }}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="info-wrap" v-else :style="infoStyle" @click="redirect('/pages/member/index')">
|
<view class="info-wrap" v-else :style="infoStyle" @tap.stop="redirect($util.MEMBER_PAGE_URL)">
|
||||||
<view class="headimg">
|
<view class="headimg">
|
||||||
<image :src="$util.getDefaultImage().head" mode="widthFix"></image>
|
<image :src="$util.getDefaultImage().head" mode="widthFix"></image>
|
||||||
</view>
|
</view>
|
||||||
@@ -75,12 +75,12 @@
|
|||||||
|
|
||||||
<view v-if="ischina == 1"
|
<view v-if="ischina == 1"
|
||||||
style="background: #fff;height: 60rpx;width: 60rpx;border-radius: 50rpx;line-height:65rpx;text-align: center;color:#000"
|
style="background: #fff;height: 60rpx;width: 60rpx;border-radius: 50rpx;line-height:65rpx;text-align: center;color:#000"
|
||||||
@click.stop="modifyInfo()">{{ langIndex == 0 ? 'CN' : 'EN' }}</view>
|
@tap.stop="modifyInfo()">{{ langIndex == 0 ? 'CN' : 'EN' }}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="account-info" v-show="value.style == 1"
|
<view class="account-info" v-show="value.style == 1"
|
||||||
:style="{ 'margin-left': parseInt(value.infoMargin) * 2 + 'rpx', 'margin-right': parseInt(value.infoMargin) * 2 + 'rpx' }">
|
:style="{ 'margin-left': parseInt(value.infoMargin) * 2 + 'rpx', 'margin-right': parseInt(value.infoMargin) * 2 + 'rpx' }">
|
||||||
<view class="account-item" @click="redirect('/pages_tool/member/balance')">
|
<view class="account-item" @tap.stop="redirect('/pages_tool/member/balance')">
|
||||||
<view class="value price-font">
|
<view class="value price-font">
|
||||||
{{ memberInfo ? (parseFloat(memberInfo.balance) +
|
{{ memberInfo ? (parseFloat(memberInfo.balance) +
|
||||||
parseFloat(memberInfo.balance_money)).toFixed(2) : '--' }}
|
parseFloat(memberInfo.balance_money)).toFixed(2) : '--' }}
|
||||||
@@ -88,12 +88,12 @@
|
|||||||
<view class="title">{{ $lang('balance') }}</view>
|
<view class="title">{{ $lang('balance') }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="solid"></view>
|
<view class="solid"></view>
|
||||||
<view class="account-item" @click="redirect('/pages_tool/member/point_detail')">
|
<view class="account-item" @tap.stop="redirect('/pages_tool/member/point_detail')">
|
||||||
<view class="value price-font">{{ memberInfo ? parseFloat(memberInfo.point) : '--' }}</view>
|
<view class="value price-font">{{ memberInfo ? parseFloat(memberInfo.point) : '--' }}</view>
|
||||||
<view class="title">{{ $lang('point') }}</view>
|
<view class="title">{{ $lang('point') }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="solid"></view>
|
<view class="solid"></view>
|
||||||
<view class="account-item" @click="redirect('/pages_tool/member/coupon')">
|
<view class="account-item" @tap.stop="redirect('/pages_tool/member/coupon')">
|
||||||
<view class="value price-font">
|
<view class="value price-font">
|
||||||
{{ memberInfo && memberInfo.coupon_num != undefined ? memberInfo.coupon_num : '--' }}
|
{{ memberInfo && memberInfo.coupon_num != undefined ? memberInfo.coupon_num : '--' }}
|
||||||
</view>
|
</view>
|
||||||
@@ -110,8 +110,8 @@
|
|||||||
<text>超级会员</text>
|
<text>超级会员</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="super-text">
|
<view class="super-text">
|
||||||
<text class="see" v-if="memberInfo && memberInfo.member_level_type" @click="redirectBeforeAuth('/pages_tool/member/card')">查看特权</text>
|
<text class="see" v-if="memberInfo && memberInfo.member_level_type" @tap.stop="redirectBeforeAuth('/pages_tool/member/card')">查看特权</text>
|
||||||
<text class="see" v-else @click="redirectBeforeAuth('/pages_tool/member/card_buy')">会员可享更多权益</text>
|
<text class="see" v-else @tap.stop="redirectBeforeAuth('/pages_tool/member/card_buy')">会员可享更多权益</text>
|
||||||
<text class="iconfont icon-right"></text>
|
<text class="iconfont icon-right"></text>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
@@ -121,8 +121,8 @@
|
|||||||
<view class="desc">开通可享更多权益</view>
|
<view class="desc">开通可享更多权益</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="super-text">
|
<view class="super-text">
|
||||||
<text class="see" v-if="memberInfo && memberInfo.member_level_type" @click="redirectBeforeAuth('/pages_tool/member/card')">查看特权</text>
|
<text class="see" v-if="memberInfo && memberInfo.member_level_type" @tap.stop="redirectBeforeAuth('/pages_tool/member/card')">查看特权</text>
|
||||||
<text class="see" v-else @click="redirectBeforeAuth('/pages_tool/member/card_buy')">立即开通</text>
|
<text class="see" v-else @tap.stop="redirectBeforeAuth('/pages_tool/member/card_buy')">立即开通</text>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
@@ -134,25 +134,25 @@
|
|||||||
<view class="desc">开通可享更多权益</view>
|
<view class="desc">开通可享更多权益</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="super-text" :class="{ 'more' : memberInfo && memberInfo.member_level_type }">
|
<view class="super-text" :class="{ 'more' : memberInfo && memberInfo.member_level_type }">
|
||||||
<text class="see" v-if="memberInfo && memberInfo.member_level_type" @click="redirectBeforeAuth('/pages_tool/member/card')">查看更多权益</text>
|
<text class="see" v-if="memberInfo && memberInfo.member_level_type" @tap.stop="redirectBeforeAuth('/pages_tool/member/card')">查看更多权益</text>
|
||||||
<text class="see" v-else @click="redirectBeforeAuth('/pages_tool/member/card_buy')">立即开通</text>
|
<text class="see" v-else @tap.stop="redirectBeforeAuth('/pages_tool/member/card_buy')">立即开通</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="account-info" :style="{ 'margin-left': parseInt(value.infoMargin) * 2 + 'rpx', 'margin-right': parseInt(value.infoMargin) * 2 + 'rpx' }">
|
<view class="account-info" :style="{ 'margin-left': parseInt(value.infoMargin) * 2 + 'rpx', 'margin-right': parseInt(value.infoMargin) * 2 + 'rpx' }">
|
||||||
<view class="account-item" @click="redirect('/pages_tool/member/balance_detail')">
|
<view class="account-item" @tap.stop="redirect('/pages_tool/member/balance_detail')">
|
||||||
<view class="value price-font">
|
<view class="value price-font">
|
||||||
{{ memberInfo ? (parseFloat(memberInfo.balance) + parseFloat(memberInfo.balance_money)).toFixed(2) : '--' }}
|
{{ memberInfo ? (parseFloat(memberInfo.balance) + parseFloat(memberInfo.balance_money)).toFixed(2) : '--' }}
|
||||||
</view>
|
</view>
|
||||||
<view class="title">余额</view>
|
<view class="title">余额</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="solid"></view>
|
<view class="solid"></view>
|
||||||
<view class="account-item" @click="redirect('/pages_tool/member/point_detail')">
|
<view class="account-item" @tap.stop="redirect('/pages_tool/member/point_detail')">
|
||||||
<view class="value price-font">{{ memberInfo ? parseFloat(memberInfo.point) : '--' }}
|
<view class="value price-font">{{ memberInfo ? parseFloat(memberInfo.point) : '--' }}
|
||||||
</view>
|
</view>
|
||||||
<view class="title">积分</view>
|
<view class="title">积分</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="solid"></view>
|
<view class="solid"></view>
|
||||||
<view class="account-item" @click="redirect('/pages_tool/member/coupon')">
|
<view class="account-item" @tap.stop="redirect('/pages_tool/member/coupon')">
|
||||||
<view class="value price-font">
|
<view class="value price-font">
|
||||||
{{ memberInfo && memberInfo.coupon_num != undefined ? memberInfo.coupon_num : '--' }}
|
{{ memberInfo && memberInfo.coupon_num != undefined ? memberInfo.coupon_num : '--' }}
|
||||||
</view>
|
</view>
|
||||||
@@ -161,7 +161,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="style4-other">
|
<view class="style4-other">
|
||||||
<view class="style4-btn-wrap">
|
<view class="style4-btn-wrap">
|
||||||
<view @click="redirect('/pages_tool/recharge/list')" class="recharge-btn">余额充值</view>
|
<view @tap.stop="redirect('/pages_tool/recharge/list')" class="recharge-btn">余额充值</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view> -->
|
</view> -->
|
||||||
@@ -169,18 +169,18 @@
|
|||||||
|
|
||||||
<view class="account-info" v-show="value.style == 2"
|
<view class="account-info" v-show="value.style == 2"
|
||||||
:style="{ 'margin-left': parseInt(value.infoMargin) * 2 + 'rpx', 'margin-right': parseInt(value.infoMargin) * 2 + 'rpx' }">
|
:style="{ 'margin-left': parseInt(value.infoMargin) * 2 + 'rpx', 'margin-right': parseInt(value.infoMargin) * 2 + 'rpx' }">
|
||||||
<view class="account-item" @click="redirect('/pages_tool/member/balance')">
|
<view class="account-item" @tap.stop="redirect('/pages_tool/member/balance')">
|
||||||
<view class="value price-font">{{ memberInfo ? (parseFloat(memberInfo.balance) +
|
<view class="value price-font">{{ memberInfo ? (parseFloat(memberInfo.balance) +
|
||||||
parseFloat(memberInfo.balance_money)).toFixed(2) : '--' }}</view>
|
parseFloat(memberInfo.balance_money)).toFixed(2) : '--' }}</view>
|
||||||
<view class="title">{{ $lang('balance') }}</view>
|
<view class="title">{{ $lang('balance') }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="solid"></view>
|
<view class="solid"></view>
|
||||||
<view class="account-item" @click="redirect('/pages_tool/member/point_detail')">
|
<view class="account-item" @tap.stop="redirect('/pages_tool/member/point_detail')">
|
||||||
<view class="value price-font">{{ memberInfo ? parseFloat(memberInfo.point) : '--' }}</view>
|
<view class="value price-font">{{ memberInfo ? parseFloat(memberInfo.point) : '--' }}</view>
|
||||||
<view class="title">{{ $lang('point') }}</view>
|
<view class="title">{{ $lang('point') }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="solid"></view>
|
<view class="solid"></view>
|
||||||
<view class="account-item" @click="redirect('/pages_tool/member/coupon')">
|
<view class="account-item" @tap.stop="redirect('/pages_tool/member/coupon')">
|
||||||
<view class="value price-font">
|
<view class="value price-font">
|
||||||
{{ memberInfo && memberInfo.coupon_num != undefined ? memberInfo.coupon_num : '--' }}
|
{{ memberInfo && memberInfo.coupon_num != undefined ? memberInfo.coupon_num : '--' }}
|
||||||
</view>
|
</view>
|
||||||
@@ -196,7 +196,7 @@
|
|||||||
<view class="head">
|
<view class="head">
|
||||||
<text class="title">获取您的昵称、头像</text>
|
<text class="title">获取您的昵称、头像</text>
|
||||||
<text class="color-tip tips">获取用户头像、昵称完善个人资料,主要用于向用户提供具有辨识度的用户中心界面</text>
|
<text class="color-tip tips">获取用户头像、昵称完善个人资料,主要用于向用户提供具有辨识度的用户中心界面</text>
|
||||||
<text class="iconfont icon-close color-tip" @click="cancelCompleteInfo"></text>
|
<text class="iconfont icon-close color-tip" @tap.stop="cancelCompleteInfo"></text>
|
||||||
</view>
|
</view>
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
<view class="item-wrap">
|
<view class="item-wrap">
|
||||||
@@ -227,7 +227,7 @@
|
|||||||
<input type="nickname" placeholder="请输入昵称" v-model="nickName" @blur="blurNickName" />
|
<input type="nickname" placeholder="请输入昵称" v-model="nickName" @blur="blurNickName" />
|
||||||
</view>
|
</view>
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
<button type="default" class="save-btn" @click="saveCompleteInfo" :disabled="isDisabled">保存</button>
|
<button type="default" class="save-btn" @tap.stop="saveCompleteInfo" :disabled="isDisabled">保存</button>
|
||||||
</view>
|
</view>
|
||||||
</uni-popup>
|
</uni-popup>
|
||||||
</view>
|
</view>
|
||||||
@@ -545,7 +545,7 @@ export default {
|
|||||||
*/
|
*/
|
||||||
redirectBeforeAuth(url) {
|
redirectBeforeAuth(url) {
|
||||||
if (!this.storeToken) {
|
if (!this.storeToken) {
|
||||||
this.$refs.login.open('/pages/member/index');
|
this.$refs.login.open(this.$util.MEMBER_PAGE_URL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -583,7 +583,7 @@ export default {
|
|||||||
url: '/wechat/api/wechat/authcode',
|
url: '/wechat/api/wechat/authcode',
|
||||||
data: {
|
data: {
|
||||||
scopes: 'snsapi_userinfo',
|
scopes: 'snsapi_userinfo',
|
||||||
redirect_url: this.$config.h5Domain + '/pages/member/index'
|
redirect_url: this.$config.h5Domain + this.$util.MEMBER_PAGE_URL
|
||||||
},
|
},
|
||||||
success: res => {
|
success: res => {
|
||||||
if (res.code >= 0) {
|
if (res.code >= 0) {
|
||||||
@@ -1380,11 +1380,11 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
::v-deep .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
||||||
background: none !important;
|
background: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .member-info-style4 .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
::v-deep .member-info-style4 .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
||||||
background: #fff !important;
|
background: #fff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1484,8 +1484,8 @@ export default {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.member-complete-info-popup /deep/ .uni-popup__wrapper.bottom,
|
.member-complete-info-popup ::v-deep .uni-popup__wrapper.bottom,
|
||||||
.member-complete-info-popup /deep/ .uni-popup__wrapper.bottom .uni-popup__wrapper-box {
|
.member-complete-info-popup ::v-deep .uni-popup__wrapper.bottom .uni-popup__wrapper-box {
|
||||||
border-top-left-radius: 30rpx !important;
|
border-top-left-radius: 30rpx !important;
|
||||||
border-top-right-radius: 30rpx !important;
|
border-top-right-radius: 30rpx !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<view data-component-name="diy-member-my-order" class="common-wrap" :style="warpCss">
|
<view data-component-name="diy-member-my-order" class="common-wrap" :style="warpCss">
|
||||||
<view class="order-wrap">
|
<view class="order-wrap">
|
||||||
<view class="status-wrap">
|
<view class="status-wrap">
|
||||||
<view class="item-wrap" @click="redirect('/pages_order/list?status=waitpay')"
|
<view class="item-wrap" @tap.stop="redirect('/pages_order/list?status=waitpay')"
|
||||||
style="margin-right: 10rpx;">
|
style="margin-right: 10rpx;">
|
||||||
<view class="icon-block">
|
<view class="icon-block">
|
||||||
<template v-if="value.style == 3">
|
<template v-if="value.style == 3">
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="title">{{ $lang('waitpay') }}</view>
|
<view class="title">{{ $lang('waitpay') }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="item-wrap" @click="redirect('/pages_order/list?status=waitsend')"
|
<view class="item-wrap" @tap.stop="redirect('/pages_order/list?status=waitsend')"
|
||||||
style="margin-right: 10rpx;">
|
style="margin-right: 10rpx;">
|
||||||
<view class="icon-block">
|
<view class="icon-block">
|
||||||
<template v-if="value.style == 3">
|
<template v-if="value.style == 3">
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="title">{{ $lang('waitsend') }}</view>
|
<view class="title">{{ $lang('waitsend') }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="item-wrap" @click="redirect('/pages_order/list?status=waitconfirm')"
|
<view class="item-wrap" @tap.stop="redirect('/pages_order/list?status=waitconfirm')"
|
||||||
style="margin-right: 10rpx;">
|
style="margin-right: 10rpx;">
|
||||||
<view class="icon-block">
|
<view class="icon-block">
|
||||||
<template v-if="value.style == 3">
|
<template v-if="value.style == 3">
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="title">{{ $lang('waitconfirm') }}</view>
|
<view class="title">{{ $lang('waitconfirm') }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="item-wrap" @click="redirect('/pages_order/list?status=waitrate')"
|
<view class="item-wrap" @tap.stop="redirect('/pages_order/list?status=waitrate')"
|
||||||
style="margin-right: 10rpx;">
|
style="margin-right: 10rpx;">
|
||||||
<view class="icon-block">
|
<view class="icon-block">
|
||||||
<template v-if="value.style == 3">
|
<template v-if="value.style == 3">
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="title">{{ $lang('completed') }}</view>
|
<view class="title">{{ $lang('completed') }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="item-wrap" @click="redirect('/pages_tool/order/activist')">
|
<view class="item-wrap" @tap.stop="redirect('/pages_tool/order/activist')">
|
||||||
<view class="icon-block">
|
<view class="icon-block">
|
||||||
<template v-if="value.style == 3">
|
<template v-if="value.style == 3">
|
||||||
<image :src="$util.img('public/uniapp/member/order/refunding.png')" mode="widthFix" />
|
<image :src="$util.img('public/uniapp/member/order/refunding.png')" mode="widthFix" />
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<view class="merch-wrap" :style="warpCss">
|
<view class="merch-wrap" :style="warpCss">
|
||||||
<view :class="['list-wrap', value.style]" :style="warpCss">
|
<view :class="['list-wrap', value.style]" :style="warpCss">
|
||||||
<view :class="['item', value.ornament.type]" v-for="(item, index) in list" :key="index"
|
<view :class="['item', value.ornament.type]" v-for="(item, index) in list" :key="index"
|
||||||
:style="itemCss" @click="handlerClick(item)" @tap="handlerClick(item)">
|
:style="itemCss" @tap.stop="handlerClick(item)">
|
||||||
<view class="merch-img">
|
<view class="merch-img">
|
||||||
<image class="cover-img" :src="$util.img(item.merch_image)" mode="widthFix"
|
<image class="cover-img" :src="$util.img(item.merch_image)" mode="widthFix"
|
||||||
@error="imgError(index)" />
|
@error="imgError(index)" />
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
|
|
||||||
<view class="merch-nav-item graphic" v-for="(item, index) in list" :key="index"
|
<view class="merch-nav-item graphic" v-for="(item, index) in list" :key="index"
|
||||||
:style="{ width: 100 / 4 + '%' }" @click="handlerClick(item)" @tap="handlerClick(item)">
|
:style="{ width: 100 / 4 + '%' }" @tap.stop="handlerClick(item)">
|
||||||
<view class="graphic-img" v-if="value.mode != 'text'"
|
<view class="graphic-img" v-if="value.mode != 'text'"
|
||||||
:style="{ fontSize: value.imageSize * 2 + 'rpx', width: value.imageSize * 2 + 'rpx', height: value.imageSize * 2 + 'rpx' }">
|
:style="{ fontSize: value.imageSize * 2 + 'rpx', width: value.imageSize * 2 + 'rpx', height: value.imageSize * 2 + 'rpx' }">
|
||||||
<image
|
<image
|
||||||
|
|||||||
@@ -3,13 +3,12 @@
|
|||||||
<view class="diy-notes" :style="{ backgroundColor: value.componentBgColor }">
|
<view class="diy-notes" :style="{ backgroundColor: value.componentBgColor }">
|
||||||
<view class="diy-notes-top">
|
<view class="diy-notes-top">
|
||||||
<view class="notes-title" :style="{ color: value.titleTextColor }">{{ value.title }}</view>
|
<view class="notes-title" :style="{ color: value.titleTextColor }">{{ value.title }}</view>
|
||||||
<view class="notes-more" @click="toMore()" :style="{ color: value.moreTextColor }">{{ value.more }}
|
<view class="notes-more" @tap.stop="toMore()" :style="{ color: value.moreTextColor }">{{ value.more }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<scroll-view class="diy-notes-box" scroll-x="true" show-scrollbar="true">
|
<scroll-view class="diy-notes-box" scroll-x="true" show-scrollbar="true">
|
||||||
<view class="notes-box-item" v-for="(item, i) in dataList" :key="i" @click="handlerClick(item)"
|
<view class="notes-box-item" v-for="(item, i) in dataList" :key="i" @tap.stop="handlerClick(item)" :style="notesItemStyle">
|
||||||
@tap="handlerClick(item)" :style="notesItemStyle">
|
|
||||||
<view class="notes-item" v-if="item.status == 1">
|
<view class="notes-item" v-if="item.status == 1">
|
||||||
<view class="notes-item-con">
|
<view class="notes-item-con">
|
||||||
<view class="notes-title">{{ item.note_title }}</view>
|
<view class="notes-title">{{ item.note_title }}</view>
|
||||||
@@ -157,7 +156,7 @@ export default {
|
|||||||
/* 点赞 */
|
/* 点赞 */
|
||||||
giveLike(noteId, index) {
|
giveLike(noteId, index) {
|
||||||
if (!this.storeToken) {
|
if (!this.storeToken) {
|
||||||
this.$refs.login.open('/pages/index/index');
|
this.$refs.login.open(this.$util.INDEX_PAGE_URL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,12 +41,12 @@
|
|||||||
<view @touchmove.prevent.stop>
|
<view @touchmove.prevent.stop>
|
||||||
<uni-popup ref="noticePopup" type="center">
|
<uni-popup ref="noticePopup" type="center">
|
||||||
<view class="notice-popup">
|
<view class="notice-popup">
|
||||||
<view class="head-wrap" @click="closeNoticePopup">
|
<view class="head-wrap" @tap.stop="closeNoticePopup">
|
||||||
<text>公告</text>
|
<text>公告</text>
|
||||||
<text class="iconfont icon-close"></text>
|
<text class="iconfont icon-close"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="content-wrap">{{ notice }}</view>
|
<view class="content-wrap">{{ notice }}</view>
|
||||||
<button type="primary" @click="closeNoticePopup">我知道了</button>
|
<button type="primary" @tap.stop="closeNoticePopup">我知道了</button>
|
||||||
</view>
|
</view>
|
||||||
</uni-popup>
|
</uni-popup>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
<!-- <text class="iconfont icon-shuaxin"></text> -->
|
<!-- <text class="iconfont icon-shuaxin"></text> -->
|
||||||
</view>
|
</view>
|
||||||
<view class="qrocde-action">
|
<view class="qrocde-action">
|
||||||
<button type="primary" @click="toLink">
|
<button type="primary" @tap.stop="toLink">
|
||||||
<text class="iconfont icon-fukuanma"></text>
|
<text class="iconfont icon-fukuanma"></text>
|
||||||
<text class="action-name">付款码</text>
|
<text class="action-name">付款码</text>
|
||||||
</button>
|
</button>
|
||||||
<button type="primary" @click="openPaymentPopup">
|
<button type="primary" @tap.stop="openPaymentPopup">
|
||||||
<text class="iconfont icon-saomafu"></text>
|
<text class="iconfont icon-saomafu"></text>
|
||||||
<text class="action-name">扫码付</text>
|
<text class="action-name">扫码付</text>
|
||||||
</button>
|
</button>
|
||||||
@@ -26,12 +26,12 @@
|
|||||||
<view @touchmove.prevent.stop>
|
<view @touchmove.prevent.stop>
|
||||||
<uni-popup ref="paymentPopup" type="center">
|
<uni-popup ref="paymentPopup" type="center">
|
||||||
<view class="payment-popup">
|
<view class="payment-popup">
|
||||||
<view class="head-wrap" @click="closePaymentPopup">
|
<view class="head-wrap" @tap.stop="closePaymentPopup">
|
||||||
<text>提示</text>
|
<text>提示</text>
|
||||||
<text class="iconfont icon-close"></text>
|
<text class="iconfont icon-close"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="content-wrap">扫码付请退出程序后直接使用微信扫一扫或返回上一页使用付款码进行支付</view>
|
<view class="content-wrap">扫码付请退出程序后直接使用微信扫一扫或返回上一页使用付款码进行支付</view>
|
||||||
<button type="primary" @click="closePaymentPopup">我知道了</button>
|
<button type="primary" @tap.stop="closePaymentPopup">我知道了</button>
|
||||||
</view>
|
</view>
|
||||||
</uni-popup>
|
</uni-popup>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
<view class="fui-picture">
|
<view class="fui-picture">
|
||||||
<view v-for="(item, index) in value.list" style="line-height: 0;">
|
<view v-for="(item, index) in value.list" style="line-height: 0;">
|
||||||
<image mode="widthFix" style="width: 100%;height:auto" :src="$util.img(item.imageUrl)"
|
<image mode="widthFix" style="width: 100%;height:auto" :src="$util.img(item.imageUrl)"
|
||||||
v-if="item.link.wap_url" @click="handlerClick(item)" @tap="handlerClick(item)"></image>
|
v-if="item.link.wap_url" @tap.stop="handlerClick(item)"></image>
|
||||||
<image mode="widthFix" style="width: 100%;height:auto" :src="$util.img(item.imageUrl)" v-else
|
<image mode="widthFix" style="width: 100%;height:auto" :src="$util.img(item.imageUrl)" v-else
|
||||||
@click="handlerClick(item)" @tap="handlerClick(item)"></image>
|
@tap.stop="handlerClick(item)"></image>
|
||||||
</view>
|
</view>
|
||||||
<!-- <view wx:if="{{!childitem.linkurl}}" bindtap="previewImg" data-src="{{childitem.imgurl}}" style="padding:{{diyitem.style.paddingtop==0?0:diyitem.style.paddingtop+'rpx'}} {{diyitem.style.paddingleft==0?0:diyitem.style.paddingleft+'rpx'}}" wx:for="{{diyitem.data}}" wx:for-index="childid" wx:for-item="childitem" wx:key="{{childid}}">
|
<!-- <view wx:if="{{!childitem.linkurl}}" bindtap="previewImg" data-src="{{childitem.imgurl}}" style="padding:{{diyitem.style.paddingtop==0?0:diyitem.style.paddingtop+'rpx'}} {{diyitem.style.paddingleft==0?0:diyitem.style.paddingleft+'rpx'}}" wx:for="{{diyitem.data}}" wx:for-index="childid" wx:for-item="childitem" wx:key="{{childid}}">
|
||||||
<image mode="widthFix" src="{{childitem.imgurl}}" style="{{bannerheight?'height:'+bannerheight+'px':'height:auto'}}"></image>
|
<image mode="widthFix" src="{{childitem.imgurl}}" style="{{bannerheight?'height:'+bannerheight+'px':'height:auto'}}"></image>
|
||||||
@@ -52,20 +52,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
previewImg(img) {
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
uni.previewImage({
|
|
||||||
current: 0,
|
|
||||||
urls: [this.$util.img(img)],
|
|
||||||
success: function (res) { },
|
|
||||||
fail: function (res) { },
|
|
||||||
complete: function (res) { },
|
|
||||||
})
|
|
||||||
// #endif
|
|
||||||
},
|
|
||||||
redirectTo(link) {
|
redirectTo(link) {
|
||||||
if (link.wap_url) {
|
if (link.wap_url) {
|
||||||
if (this.$util.getCurrRoute() == 'pages/member/index' && !this.storeToken) {
|
if (this.$util.getCurrRoute() == this.$util.MEMBER_PAGE_URL && !this.storeToken) {
|
||||||
this.$refs.login.open(link.wap_url);
|
this.$refs.login.open(link.wap_url);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -77,7 +66,9 @@ export default {
|
|||||||
await this.__$emitEvent({
|
await this.__$emitEvent({
|
||||||
eventName: 'picture-tap', data: item, promiseCallback: (event, handler, awaitedResult) => {
|
eventName: 'picture-tap', data: item, promiseCallback: (event, handler, awaitedResult) => {
|
||||||
if (!awaitedResult) return;
|
if (!awaitedResult) return;
|
||||||
if (item.link.wap_url) {
|
|
||||||
|
const link = item.link;
|
||||||
|
if (link?.name || link?.wap_url || link?.appid) {
|
||||||
this.redirectTo(item.link);
|
this.redirectTo(item.link);
|
||||||
} else {
|
} else {
|
||||||
this.previewImg(item.imageUrl);
|
this.previewImg(item.imageUrl);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<x-skeleton data-component-name="diy-pinfan" :type="skeletonType" :loading="loading" :configs="skeletonConfig">
|
<x-skeleton data-component-name="diy-pinfan" :type="skeletonType" :loading="loading" :configs="skeletonConfig">
|
||||||
<view class="diy-pinfan" :class="[value.template, value.style]" :style="warpCss">
|
<view class="diy-pinfan" :class="[value.template, value.style]" :style="warpCss">
|
||||||
<template v-if="value.template == 'row1-of1'">
|
<template v-if="value.template == 'row1-of1'">
|
||||||
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
|
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
|
||||||
:class="[value.ornament.type]" :style="goodsItemCss">
|
:class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
|
|
||||||
<template v-if="value.template == 'horizontal-slide'">
|
<template v-if="value.template == 'horizontal-slide'">
|
||||||
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
|
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
|
||||||
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
|
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
|
||||||
:class="[value.ornament.type]" :style="goodsItemCss">
|
:class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
|
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
|
||||||
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
|
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
|
||||||
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
|
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
|
||||||
@click="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
|
@tap.stop="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
||||||
@@ -319,7 +319,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
/deep/.uni-scroll-view ::-webkit-scrollbar {
|
::v-deep .uni-scroll-view ::-webkit-scrollbar {
|
||||||
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
@@ -328,7 +328,7 @@ export default {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/::-webkit-scrollbar {
|
::v-deep ::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|||||||
@@ -31,14 +31,14 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="head-right"
|
<view class="head-right"
|
||||||
:style="{ fontSize: value.titleStyle.moreFontSize * 2 + 'rpx', color: value.titleStyle.moreColor }"
|
:style="{ fontSize: value.titleStyle.moreFontSize * 2 + 'rpx', color: value.titleStyle.moreColor }"
|
||||||
@click="$util.redirectTo('/pages_promotion/pintuan/list')">
|
@tap.stop="$util.redirectTo('/pages_promotion/pintuan/list')">
|
||||||
<text>{{ value.titleStyle.more }}</text>
|
<text>{{ value.titleStyle.more }}</text>
|
||||||
<text class="iconfont icon-right"></text>
|
<text class="iconfont icon-right"></text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<template v-if="value.template == 'row1-of1'">
|
<template v-if="value.template == 'row1-of1'">
|
||||||
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
|
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
|
||||||
:class="[value.ornament.type]" :style="goodsItemCss">
|
:class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
|
|
||||||
<template v-if="value.template == 'horizontal-slide'">
|
<template v-if="value.template == 'horizontal-slide'">
|
||||||
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
|
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
|
||||||
<view class="item" v-for="(item, index) in list" :key="index" @click="toDetail(item)"
|
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="toDetail(item)"
|
||||||
:class="[value.ornament.type]" :style="goodsItemCss">
|
:class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
@@ -161,7 +161,7 @@
|
|||||||
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
|
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
|
||||||
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
|
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
|
||||||
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
|
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
|
||||||
@click="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
|
@tap.stop="toDetail(item)" :class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
||||||
@@ -391,7 +391,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
/deep/.uni-scroll-view ::-webkit-scrollbar {
|
::v-deep .uni-scroll-view ::-webkit-scrollbar {
|
||||||
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
@@ -400,7 +400,7 @@ export default {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/::-webkit-scrollbar {
|
::v-deep ::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
<x-skeleton data-component-name="diy-presale" :type="skeletonType" :loading="loading" :configs="skeletonConfig">
|
<x-skeleton data-component-name="diy-presale" :type="skeletonType" :loading="loading" :configs="skeletonConfig">
|
||||||
<view class="diy-presale" v-if="list.length" :class="[value.template, value.style]" :style="warpCss">
|
<view class="diy-presale" v-if="list.length" :class="[value.template, value.style]" :style="warpCss">
|
||||||
<template v-if="value.template == 'row1-of1'">
|
<template v-if="value.template == 'row1-of1'">
|
||||||
<view class="item" v-for="(item, index) in list" :key="index" @click="handlerClick(item)"
|
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="handlerClick(item)"
|
||||||
@tap="handlerClick(item)" :class="[value.ornament.type]" :style="goodsItemCss">
|
:class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
||||||
@@ -40,8 +40,8 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-if="value.template == 'horizontal-slide'">
|
<template v-if="value.template == 'horizontal-slide'">
|
||||||
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
|
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true" :show-scrollbar="false">
|
||||||
<view class="item" v-for="(item, index) in list" :key="index" @click="handlerClick(item)"
|
<view class="item" v-for="(item, index) in list" :key="index" @tap.stop="handlerClick(item)"
|
||||||
@tap="handlerClick(item)" :class="[value.ornament.type]" :style="goodsItemCss">
|
:class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
||||||
@@ -75,8 +75,7 @@
|
|||||||
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
|
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
|
||||||
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
|
:class="['swiper-item', (list.length && [list[pageIndex].length / 3] >= 1) && 'flex-between']">
|
||||||
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
|
<view class="item" v-for="(item, dataIndex) in list[pageIndex]" :key="dataIndex"
|
||||||
@click="handlerClick(item)" @tap="handlerClick(item)" :class="[value.ornament.type]"
|
@tap.stop="handlerClick(item)" :class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
:style="goodsItemCss">
|
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
||||||
@@ -276,7 +275,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
/deep/.uni-scroll-view ::-webkit-scrollbar {
|
::v-deep .uni-scroll-view ::-webkit-scrollbar {
|
||||||
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
@@ -285,7 +284,7 @@ export default {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/::-webkit-scrollbar {
|
::v-deep ::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<view class="uni-scroll-view-content">
|
<view class="uni-scroll-view-content">
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
<view class="quick-nav-item" v-for="(item, index) in value.list" :key="index"
|
<view class="quick-nav-item" v-for="(item, index) in value.list" :key="index"
|
||||||
@click="handlerClick(item)" @tap="handlerClick(item)"
|
@tap.stop="handlerClick(item)"
|
||||||
:style="{ background: 'linear-gradient(to right,' + item.bgColorStart ? item.bgColorStart : '' + ',' + item.bgColorEnd ? item.bgColorEnd : '' + ')' }">
|
:style="{ background: 'linear-gradient(to right,' + item.bgColorStart ? item.bgColorStart : '' + ',' + item.bgColorEnd ? item.bgColorEnd : '' + ')' }">
|
||||||
<view class="quick-img" v-if="item.imageUrl || item.icon">
|
<view class="quick-img" v-if="item.imageUrl || item.icon">
|
||||||
<image v-if="item.iconType == 'img'"
|
<image v-if="item.iconType == 'img'"
|
||||||
@@ -61,7 +61,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
redirectTo(link) {
|
redirectTo(link) {
|
||||||
if (link.wap_url) {
|
if (link.wap_url) {
|
||||||
if (this.$util.getCurrRoute() == 'pages/member/index' && !this.storeToken) {
|
if (this.$util.getCurrRoute() == this.$util.MEMBER_PAGE_URL && !this.storeToken) {
|
||||||
this.$refs.login.open(link.wap_url);
|
this.$refs.login.open(link.wap_url);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view data-component-name="diy-rich-text" class="rich-text-box" :style="richTextWarpCss">
|
<view data-component-name="diy-rich-text" class="rich-text-box" :style="richTextWarpCss">
|
||||||
<rich-text :nodes="html" @click="handlerClick" @tap="handlerClick"></rich-text>
|
<rich-text :nodes="html" @tap.stop="handlerClick"></rich-text>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,8 @@
|
|||||||
<!-- 1左2右 -->
|
<!-- 1左2右 -->
|
||||||
<template v-if="value.mode == 'row1-lt-of2-rt'">
|
<template v-if="value.mode == 'row1-lt-of2-rt'">
|
||||||
<view class="template-left">
|
<view class="template-left">
|
||||||
<view :class="['item', value.mode]" @click="handlerClick(value.list[0].link)"
|
<view :class="['item', value.mode]" @tap.stop="handlerClick(value.list[0].link, value.list[0].imageUrl)"
|
||||||
@tap="handlerClick(value.list[0].link)"
|
:style="{ marginRight: value.imageGap * 2 + 'rpx', width: list[0].imgWidth, height: list[0].imgHeight + 'px' }">
|
||||||
:style="{ marginRight: value.imageGap * 2 + 'rpx', width: list[0].imgWidth, height: list[0].imgHeight + 'px' }">
|
|
||||||
<image :src="$util.img(value.list[0].imageUrl)" :mode="list[0].imageMode || 'scaleToFill'"
|
<image :src="$util.img(value.list[0].imageUrl)" :mode="list[0].imageMode || 'scaleToFill'"
|
||||||
:style="list[0].pageItemStyle" :show-menu-by-longpress="true" />
|
:style="list[0].pageItemStyle" :show-menu-by-longpress="true" />
|
||||||
</view>
|
</view>
|
||||||
@@ -19,9 +18,8 @@
|
|||||||
<view class="template-right">
|
<view class="template-right">
|
||||||
<template v-for="(item, index) in list">
|
<template v-for="(item, index) in list">
|
||||||
<template v-if="index > 0">
|
<template v-if="index > 0">
|
||||||
<view :key="index" :class="['item', value.mode]" @click="handlerClick(item.link)"
|
<view :key="index" :class="['item', value.mode]" @tap.stop="handlerClick(item.link, item.imageUrl)"
|
||||||
@tap="handlerClick(item.link)"
|
:style="{ marginBottom: value.imageGap * 2 + 'rpx', width: item.imgWidth, height: item.imgHeight + 'px' }">
|
||||||
:style="{ marginBottom: value.imageGap * 2 + 'rpx', width: item.imgWidth, height: item.imgHeight + 'px' }">
|
|
||||||
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
|
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
|
||||||
:style="item.pageItemStyle" :show-menu-by-longpress="true" />
|
:style="item.pageItemStyle" :show-menu-by-longpress="true" />
|
||||||
</view>
|
</view>
|
||||||
@@ -34,8 +32,8 @@
|
|||||||
<template v-else-if="value.mode == 'row1-lt-of1-tp-of2-bm'">
|
<template v-else-if="value.mode == 'row1-lt-of1-tp-of2-bm'">
|
||||||
<view class="template-left">
|
<view class="template-left">
|
||||||
<view :class="['item', value.mode]"
|
<view :class="['item', value.mode]"
|
||||||
:style="{ marginRight: value.imageGap * 2 + 'rpx', width: list[0].imgWidth, height: list[0].imgHeight + 'px' }"
|
:style="{ marginRight: value.imageGap * 2 + 'rpx', width: list[0].imgWidth, height: list[0].imgHeight + 'px' }"
|
||||||
@click="handlerClick(value.list[0].link)" @tap="handlerClick(value.list[0].link)">
|
@tap.stop="handlerClick(value.list[0].link, value.list[0].imageUrl)">
|
||||||
<image :src="$util.img(value.list[0].imageUrl)" :mode="list[0].imageMode || 'scaleToFill'"
|
<image :src="$util.img(value.list[0].imageUrl)" :mode="list[0].imageMode || 'scaleToFill'"
|
||||||
:style="list[0].pageItemStyle" :show-menu-by-longpress="true" />
|
:style="list[0].pageItemStyle" :show-menu-by-longpress="true" />
|
||||||
</view>
|
</view>
|
||||||
@@ -43,20 +41,19 @@
|
|||||||
|
|
||||||
<view class="template-right">
|
<view class="template-right">
|
||||||
<view :class="['item', value.mode]"
|
<view :class="['item', value.mode]"
|
||||||
:style="{ marginBottom: value.imageGap * 2 + 'rpx', width: list[1].imgWidth, height: list[1].imgHeight + 'px' }"
|
:style="{ marginBottom: value.imageGap * 2 + 'rpx', width: list[1].imgWidth, height: list[1].imgHeight + 'px' }"
|
||||||
@click="handlerClick(value.list[1].link)" @tap="handlerClick(value.list[1].link)">
|
@tap.stop="handlerClick(value.list[1].link, value.list[1].imageUrl)">
|
||||||
<image :src="$util.img(value.list[1].imageUrl)" :mode="list[1].imageMode || 'scaleToFill'"
|
<image :src="$util.img(value.list[1].imageUrl)" :mode="list[1].imageMode || 'scaleToFill'"
|
||||||
:style="list[1].pageItemStyle" :show-menu-by-longpress="true" />
|
:style="list[1].pageItemStyle" :show-menu-by-longpress="true" />
|
||||||
</view>
|
</view>
|
||||||
<view class="template-bottom">
|
<view class="template-bottom">
|
||||||
<template v-for="(item, index) in list">
|
<template v-for="(item, index) in list">
|
||||||
<template v-if="index > 1">
|
<template v-if="index > 1">
|
||||||
<view :key="index" :class="['item', value.mode]" @click="handlerClick(item.link)"
|
<view :key="index" :class="['item', value.mode]" @tap.stop="handlerClick(item.link, item.imageUrl)" :style="{
|
||||||
@tap="handlerClick(item.link)" :style="{
|
marginRight: value.imageGap * 2 + 'rpx',
|
||||||
marginRight: value.imageGap * 2 + 'rpx',
|
width: item.imgWidth,
|
||||||
width: item.imgWidth,
|
height: item.imgHeight + 'px'
|
||||||
height: item.imgHeight + 'px'
|
}">
|
||||||
}">
|
|
||||||
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
|
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
|
||||||
:style="item.pageItemStyle" :show-menu-by-longpress="true" />
|
:style="item.pageItemStyle" :show-menu-by-longpress="true" />
|
||||||
</view>
|
</view>
|
||||||
@@ -68,8 +65,8 @@
|
|||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<view :class="['item', value.mode]" v-for="(item, index) in list" :key="index"
|
<view :class="['item', value.mode]" v-for="(item, index) in list" :key="index"
|
||||||
@click="handlerClick(item.link)" @tap="handlerClick(item.link)"
|
@tap.stop="handlerClick(item.link, item.imageUrl)"
|
||||||
:style="{ marginRight: value.imageGap * 2 + 'rpx', marginBottom: value.imageGap * 2 + 'rpx', width: item.widthStyle, height: item.imgHeight + 'px' }">
|
:style="{ marginRight: value.imageGap * 2 + 'rpx', marginBottom: value.imageGap * 2 + 'rpx', width: item.widthStyle, height: item.imgHeight + 'px' }">
|
||||||
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
|
<image :src="$util.img(item.imageUrl)" :mode="item.imageMode || 'scaleToFill'"
|
||||||
:style="item.pageItemStyle" :show-menu-by-longpress="true" />
|
:style="item.pageItemStyle" :show-menu-by-longpress="true" />
|
||||||
</view>
|
</view>
|
||||||
@@ -374,11 +371,16 @@ export default {
|
|||||||
return obj;
|
return obj;
|
||||||
},
|
},
|
||||||
|
|
||||||
async handlerClick(link) {
|
async handlerClick(link, imageUrl) {
|
||||||
await this.__$emitEvent({
|
await this.__$emitEvent({
|
||||||
eventName: 'rubik-cube-tap', data: link, promiseCallback: (event, handler, awaitedResult) => {
|
eventName: 'rubik-cube-tap', data: link, promiseCallback: (event, handler, awaitedResult) => {
|
||||||
if (!awaitedResult) return;
|
if (!awaitedResult) return;
|
||||||
this.$util.diyRedirectTo(link);
|
|
||||||
|
if (link?.name || link?.wap_url || link?.appid) {
|
||||||
|
this.$util.diyRedirectTo(link);
|
||||||
|
} else if (imageUrl){
|
||||||
|
this.previewImg(imageUrl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<view data-component-name="diy-search" class="diy-search">
|
<view data-component-name="diy-search" class="diy-search">
|
||||||
<view class="diy-search-wrap" :class="value.positionWay" :style="fixedCss">
|
<view class="diy-search-wrap" :class="value.positionWay" :style="fixedCss">
|
||||||
<view :class="['search-box', 'search-box-' + value.searchStyle]" :style="searchWrapCss"
|
<view :class="['search-box', 'search-box-' + value.searchStyle]" :style="searchWrapCss"
|
||||||
@click="handlerSearchClick" @tap="handlerSearchClick">
|
@tap.stop="handlerSearchClick">
|
||||||
<block v-if="[1, 2].includes(value.searchStyle)">
|
<block v-if="[1, 2].includes(value.searchStyle)">
|
||||||
<view class="img" v-if="value.searchStyle == 2 && value.iconType == 'img'">
|
<view class="img" v-if="value.searchStyle == 2 && value.iconType == 'img'">
|
||||||
<image :src="$util.img(value.imageUrl)" mode="heightFix" />
|
<image :src="$util.img(value.imageUrl)" mode="heightFix" />
|
||||||
@@ -14,32 +14,30 @@
|
|||||||
<input type="text" class="uni-input ns-font-size-base" maxlength="50" :placeholder="value.title"
|
<input type="text" class="uni-input ns-font-size-base" maxlength="50" :placeholder="value.title"
|
||||||
v-model="searchText" @confirm="handlerSearchClick" disabled="true"
|
v-model="searchText" @confirm="handlerSearchClick" disabled="true"
|
||||||
:placeholderStyle="placeholderStyle" />
|
:placeholderStyle="placeholderStyle" />
|
||||||
<text class="iconfont icon-sousuo3" @click.stop="handlerSearchClick" @tap="handlerSearchClick"
|
<text class="iconfont icon-sousuo3" @tap.stop="handlerSearchClick"
|
||||||
:style="{ color: value.textColor ? value.textColor : 'rgba(0,0,0,0)' }"></text>
|
:style="{ color: value.textColor ? value.textColor : 'rgba(0,0,0,0)' }"></text>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
<block v-if="value.searchStyle == 3">
|
<block v-if="value.searchStyle == 3">
|
||||||
<view class="search-content" :style="inputStyle" @click.stop="handlerSearchClick"
|
<view class="search-content" :style="inputStyle"
|
||||||
@tap="handlerSearchClick">
|
@tap.stop="handlerSearchClick">
|
||||||
<text class="iconfont icon-sousuo3"
|
<text class="iconfont icon-sousuo3"
|
||||||
:style="{ color: value.textColor ? value.textColor : 'rgba(0,0,0,0)' }"></text>
|
:style="{ color: value.textColor ? value.textColor : 'rgba(0,0,0,0)' }"></text>
|
||||||
<input type="text" class="uni-input ns-font-size-base" maxlength="50" :placeholder="value.title"
|
<input type="text" class="uni-input ns-font-size-base" maxlength="50" :placeholder="value.title"
|
||||||
v-model="searchText" @confirm="handlerSearchClick" disabled="true"
|
v-model="searchText" @confirm="handlerSearchClick" disabled="true"
|
||||||
@click.stop="handlerSearchClick" @tap="handlerSearchClick"
|
@tap.stop="handlerSearchClick"
|
||||||
:placeholderStyle="placeholderStyle" />
|
:placeholderStyle="placeholderStyle" />
|
||||||
<text class="search-content-btn" @click.stop="handlerSearchClick" @tap="handlerSearchClick"
|
<text class="search-content-btn" @tap.stop="handlerSearchClick"
|
||||||
:style="{ 'backgroundColor': value.pageBgColor ? value.pageBgColor : 'rgba(0,0,0,0)' }">搜索</text>
|
:style="{ 'backgroundColor': value.pageBgColor ? value.pageBgColor : 'rgba(0,0,0,0)' }">搜索</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="img" v-if="value.iconType == 'img'"
|
<view class="img" v-if="value.iconType == 'img'"
|
||||||
@click.stop="handlerRedirectToClick(value.searchLink)"
|
@tap.stop="handlerRedirectToClick(value.searchLink)">
|
||||||
@tap="handlerRedirectToClick(value.searchLink)">
|
|
||||||
<image :src="$util.img(value.imageUrl)" mode="heightFix" />
|
<image :src="$util.img(value.imageUrl)" mode="heightFix" />
|
||||||
</view>
|
</view>
|
||||||
<diy-icon class="icon" v-if="value.iconType == 'icon'" :icon="value.icon"
|
<diy-icon class="icon" v-if="value.iconType == 'icon'" :icon="value.icon"
|
||||||
:value="value.style ? value.style : 'null'"
|
:value="value.style ? value.style : 'null'"
|
||||||
:style="{ maxWidth: 30 * 2 + 'rpx', maxHeight: 30 * 2 + 'rpx' }"
|
:style="{ maxWidth: 30 * 2 + 'rpx', maxHeight: 30 * 2 + 'rpx' }"
|
||||||
@click.stop="handlerRedirectToClick(value.searchLink)"
|
@tap.stop="handlerRedirectToClick(value.searchLink)"></diy-icon>
|
||||||
@tap="handlerRedirectToClick(value.searchLink)"></diy-icon>
|
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -198,7 +196,7 @@ export default {
|
|||||||
},
|
},
|
||||||
redirectTo(link) {
|
redirectTo(link) {
|
||||||
if (link.wap_url) {
|
if (link.wap_url) {
|
||||||
if (this.$util.getCurrRoute() == 'pages/member/index' && !this.storeToken) {
|
if (this.$util.getCurrRoute() == this.$util.MEMBER_PAGE_URL && !this.storeToken) {
|
||||||
this.$refs.login.open(link.wap_url);
|
this.$refs.login.open(link.wap_url);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -257,7 +255,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
/deep/ .uni-input-placeholder {
|
::v-deep .uni-input-placeholder {
|
||||||
overflow: initial;
|
overflow: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="marketimg-box-title-right" v-if="value.titleStyle.moreSupport"
|
<view class="marketimg-box-title-right" v-if="value.titleStyle.moreSupport"
|
||||||
:style="{ fontSize: value.titleStyle.moreFontSize * 2 + 'rpx', color: value.titleStyle.moreColor }"
|
:style="{ fontSize: value.titleStyle.moreFontSize * 2 + 'rpx', color: value.titleStyle.moreColor }"
|
||||||
@click="toMore">
|
@tap.stop="toMore">
|
||||||
<text>{{ value.titleStyle.more }}</text>
|
<text>{{ value.titleStyle.more }}</text>
|
||||||
<text class="iconfont icon-right"></text>
|
<text class="iconfont icon-right"></text>
|
||||||
</view>
|
</view>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
<view class="content-wrap">
|
<view class="content-wrap">
|
||||||
<template v-if="value.template == 'row1-of1'">
|
<template v-if="value.template == 'row1-of1'">
|
||||||
<view class="item" v-for="(item, index) in dataList" :key="index" @click="toDetail(item.id)"
|
<view class="item" v-for="(item, index) in dataList" :key="index" @tap.stop="toDetail(item.id)"
|
||||||
:class="[value.ornament.type]" :style="goodsItemCss">
|
:class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="value.template == 'row1-of2'">
|
<template v-if="value.template == 'row1-of2'">
|
||||||
<view class="item" v-for="(item, index) in dataList" :key="index" @click="toDetail(item.id)"
|
<view class="item" v-for="(item, index) in dataList" :key="index" @tap.stop="toDetail(item.id)"
|
||||||
:class="[value.ornament.type]" :style="goodsItemCss">
|
:class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
@@ -150,7 +150,7 @@
|
|||||||
<template v-if="value.template == 'horizontal-slide'">
|
<template v-if="value.template == 'horizontal-slide'">
|
||||||
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true"
|
<scroll-view v-if="value.slideMode == 'scroll'" class="scroll" :scroll-x="true"
|
||||||
:show-scrollbar="false">
|
:show-scrollbar="false">
|
||||||
<view class="item" v-for="(item, index) in dataList" :key="index" @click="toDetail(item.id)"
|
<view class="item" v-for="(item, index) in dataList" :key="index" @tap.stop="toDetail(item.id)"
|
||||||
:class="[value.ornament.type]" :style="goodsItemCss">
|
:class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
@@ -190,7 +190,7 @@
|
|||||||
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
|
<swiper-item v-for="(pageItem, pageIndex) in page" :key="pageIndex"
|
||||||
:class="['swiper-item', dataList[pageIndex] && [dataList[pageIndex].length / 3].length >= 1 && 'flex-between']">
|
:class="['swiper-item', dataList[pageIndex] && [dataList[pageIndex].length / 3].length >= 1 && 'flex-between']">
|
||||||
<view class="item" v-for="(item, dataIndex) in dataList[pageIndex]" :key="dataIndex"
|
<view class="item" v-for="(item, dataIndex) in dataList[pageIndex]" :key="dataIndex"
|
||||||
@click="toDetail(item.id)" :class="[value.ornament.type]" :style="goodsItemCss">
|
@tap.stop="toDetail(item.id)" :class="[value.ornament.type]" :style="goodsItemCss">
|
||||||
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
<view class="img-wrap" :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }">
|
||||||
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
<image :style="{ borderRadius: value.imgAroundRadius * 2 + 'rpx' }"
|
||||||
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
:src="$util.img(item.goods_image, { size: 'mid' })" mode="widthFix"
|
||||||
@@ -536,7 +536,7 @@ scroll-view ::-webkit-scrollbar {
|
|||||||
touch-action: none;
|
touch-action: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/.uni-scroll-view ::-webkit-scrollbar {
|
::v-deep .uni-scroll-view ::-webkit-scrollbar {
|
||||||
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
/* 隐藏滚动条,但依旧具备可以滚动的功能 */
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
@@ -545,7 +545,7 @@ scroll-view ::-webkit-scrollbar {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/::-webkit-scrollbar {
|
::v-deep ::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
|
|
||||||
/deep/ .uni-scroll-view-content {
|
::v-deep .uni-scroll-view-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
@@ -148,7 +148,7 @@ export default {
|
|||||||
&.between {
|
&.between {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
/deep/.uni-scroll-view-content {
|
::v-deep .uni-scroll-view-content {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<block v-if="value.style == 1">
|
<block v-if="value.style == 1">
|
||||||
<view class="store-box store-one">
|
<view class="store-box store-one">
|
||||||
<view class="store-info">
|
<view class="store-info">
|
||||||
<view class="info-box" :style="{ color: value.textColor }" @click="toStoreList()">
|
<view class="info-box" :style="{ color: value.textColor }" @tap.stop="toStoreList()">
|
||||||
<block v-if="globalStoreInfo && globalStoreInfo.store_id">
|
<block v-if="globalStoreInfo && globalStoreInfo.store_id">
|
||||||
<text class="title">{{ globalStoreInfo.store_name }}</text>
|
<text class="title">{{ globalStoreInfo.store_name }}</text>
|
||||||
<text>
|
<text>
|
||||||
@@ -15,12 +15,12 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="address-wrap" :style="{ color: value.textColor }">
|
<view class="address-wrap" :style="{ color: value.textColor }">
|
||||||
<text class="iconfont icon-dizhi"></text>
|
<text class="iconfont icon-dizhi"></text>
|
||||||
<text v-if="globalStoreInfo && globalStoreInfo.store_id" @click="mapRoute" class="address">{{
|
<text v-if="globalStoreInfo && globalStoreInfo.store_id" @tap.stop="mapRoute" class="address">{{
|
||||||
globalStoreInfo.show_address }}</text>
|
globalStoreInfo.show_address }}</text>
|
||||||
<text v-else>获取当前位置...</text>
|
<text v-else>获取当前位置...</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="store-image" @click="selectStore()">
|
<view class="store-image" @tap.stop="selectStore()">
|
||||||
<image :src="$util.img(globalStoreInfo.store_image)"
|
<image :src="$util.img(globalStoreInfo.store_image)"
|
||||||
v-if="globalStoreInfo && globalStoreInfo.store_image" mode="aspectFill"></image>
|
v-if="globalStoreInfo && globalStoreInfo.store_image" mode="aspectFill"></image>
|
||||||
<image :src="$util.getDefaultImage().store" v-else mode="aspectFill"></image>
|
<image :src="$util.getDefaultImage().store" v-else mode="aspectFill"></image>
|
||||||
@@ -29,9 +29,9 @@
|
|||||||
</block>
|
</block>
|
||||||
|
|
||||||
<block v-if="value.style == 2">
|
<block v-if="value.style == 2">
|
||||||
<view class="store-box store-three" @click="toStoreList()">
|
<view class="store-box store-three" @tap.stop="toStoreList()">
|
||||||
<view class="store-info">
|
<view class="store-info">
|
||||||
<view class="store-image" @click="selectStore()">
|
<view class="store-image" @tap.stop="selectStore()">
|
||||||
<image :src="$util.img(globalStoreInfo.store_image)"
|
<image :src="$util.img(globalStoreInfo.store_image)"
|
||||||
v-if="globalStoreInfo && globalStoreInfo.store_image" mode="aspectFill"></image>
|
v-if="globalStoreInfo && globalStoreInfo.store_image" mode="aspectFill"></image>
|
||||||
<image :src="$util.getDefaultImage().store" v-else mode="aspectFill"></image>
|
<image :src="$util.getDefaultImage().store" v-else mode="aspectFill"></image>
|
||||||
@@ -47,13 +47,13 @@
|
|||||||
<text class="title" v-else>定位中...</text>
|
<text class="title" v-else>定位中...</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="store-icon" @click.stop="search()"><text class="iconfont icon-sousuo3"
|
<view class="store-icon" @tap.stop="search()"><text class="iconfont icon-sousuo3"
|
||||||
:style="{ color: value.textColor }"></text></view>
|
:style="{ color: value.textColor }"></text></view>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
|
|
||||||
<block v-if="value.style == 3">
|
<block v-if="value.style == 3">
|
||||||
<view class="store-box store-four" @click="toStoreList()">
|
<view class="store-box store-four" @tap.stop="toStoreList()">
|
||||||
<view class="store-left-wrap">
|
<view class="store-left-wrap">
|
||||||
<block v-if="globalStoreInfo && globalStoreInfo.store_id">
|
<block v-if="globalStoreInfo && globalStoreInfo.store_id">
|
||||||
<text class="iconfont icon-weizhi" :style="{ color: value.textColor }"></text>
|
<text class="iconfont icon-weizhi" :style="{ color: value.textColor }"></text>
|
||||||
@@ -64,8 +64,8 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="store-right-search">
|
<view class="store-right-search">
|
||||||
<input type="text" class="uni-input font-size-tag" disabled placeholder="商品搜索"
|
<input type="text" class="uni-input font-size-tag" disabled placeholder="商品搜索"
|
||||||
@click.stop="search()" />
|
@tap.stop="search()" />
|
||||||
<text class="iconfont icon-sousuo3" @click.stop="search()"></text>
|
<text class="iconfont icon-sousuo3" @tap.stop="search()"></text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
|
|||||||
1283
components-diy/diy-tab.vue
Normal file
1283
components-diy/diy-tab.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<view data-component-name="diy-text" class="diy-text" @click="handlerClick(value.link)"
|
<view data-component-name="diy-text" class="diy-text" @tap="handlerClick(value.link)" :style="warpCss">
|
||||||
@tap="handlerClick(value.link)" :style="warpCss">
|
|
||||||
<view :class="value.style == 'style-8' ? 'title2' : 'title'"
|
<view :class="value.style == 'style-8' ? 'title2' : 'title'"
|
||||||
:style="{ fontSize: value.fontSize * 2 + 'rpx', color: value.textColor }">
|
:style="{ fontSize: value.fontSize * 2 + 'rpx', color: value.textColor }">
|
||||||
<block v-if="value.style == 'style-0'" style="height: 40rpx; line-height: 40rpx;">
|
<block v-if="value.style == 'style-0'" style="height: 40rpx; line-height: 40rpx;">
|
||||||
@@ -100,13 +99,13 @@
|
|||||||
<image :src="$util.img('public/uniapp/diy/style9-2.png')" />
|
<image :src="$util.img('public/uniapp/diy/style9-2.png')" />
|
||||||
</view>
|
</view>
|
||||||
<view class="style9-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
|
<view class="style9-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
|
||||||
@click.stop="handlerClick(value.more.link)" @tap="handlerClick(value.more.link)">
|
@tap.stop="handlerClick(value.more.link)">
|
||||||
{{ value.more.text }}
|
{{ value.more.text }}
|
||||||
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
|
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<text class="sub-title" :style="{ color: value.subTitle.color }">{{ value.subTitle.text
|
<text class="sub-title" :style="{ color: value.subTitle.color }">{{ value.subTitle.text
|
||||||
}}</text>
|
}}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
@@ -132,13 +131,13 @@
|
|||||||
<image :src="$util.img('public/uniapp/diy/style10-2.png')"></image>
|
<image :src="$util.img('public/uniapp/diy/style10-2.png')"></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="style10-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
|
<view class="style10-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
|
||||||
@click.stop="handlerClick(value.more.link)" @tap="handlerClick(value.more.link)">
|
@tap.stop="handlerClick(value.more.link)">
|
||||||
{{ value.more.text }}
|
{{ value.more.text }}
|
||||||
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
|
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<text class="sub-title" :style="{ color: value.subTitle.color }">{{ value.subTitle.text
|
<text class="sub-title" :style="{ color: value.subTitle.color }">{{ value.subTitle.text
|
||||||
}}</text>
|
}}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
@@ -159,7 +158,7 @@
|
|||||||
value.subTitle.text }}</view>
|
value.subTitle.text }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="style11-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
|
<view class="style11-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
|
||||||
@click.stop="$util.diyRedirectTo(value.more.link)">
|
@tap.stop="$util.diyRedirectTo(value.more.link)">
|
||||||
{{ value.more.text }}
|
{{ value.more.text }}
|
||||||
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
|
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
|
||||||
</view>
|
</view>
|
||||||
@@ -183,9 +182,9 @@
|
|||||||
{{ value.text }}
|
{{ value.text }}
|
||||||
</view>
|
</view>
|
||||||
<text class="style12-sub-title" :style="{ color: value.subTitle.color }">{{ value.subTitle.text
|
<text class="style12-sub-title" :style="{ color: value.subTitle.color }">{{ value.subTitle.text
|
||||||
}}</text>
|
}}</text>
|
||||||
<view class="style12-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
|
<view class="style12-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
|
||||||
@click.stop="$util.diyRedirectTo(value.more.link)">
|
@tap.stop="$util.diyRedirectTo(value.more.link)">
|
||||||
<text>{{ value.more.text }}</text>
|
<text>{{ value.more.text }}</text>
|
||||||
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
|
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
|
||||||
</view>
|
</view>
|
||||||
@@ -278,7 +277,7 @@
|
|||||||
<text :style="{ fontWeight: value.subTitle.fontWeight }">{{ value.subTitle.text }}</text>
|
<text :style="{ fontWeight: value.subTitle.fontWeight }">{{ value.subTitle.text }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="style16-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
|
<view class="style16-more" v-if="value.more.isShow" :style="{ color: value.more.color }"
|
||||||
@click.stop="$util.diyRedirectTo(value.more.link)">
|
@tap.stop="$util.diyRedirectTo(value.more.link)">
|
||||||
<text>{{ value.more.text }}</text>
|
<text>{{ value.more.text }}</text>
|
||||||
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
|
<view class="iconfont icon-right" :style="{ color: value.more.color }"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -53,9 +53,19 @@
|
|||||||
|
|
||||||
<!-- 视频播放弹窗 -->
|
<!-- 视频播放弹窗 -->
|
||||||
<uni-popup ref="videoPopup" type="center" style="background:transparent;width:100%;height:100%;">
|
<uni-popup ref="videoPopup" type="center" style="background:transparent;width:100%;height:100%;">
|
||||||
<view class="video-container" style="position:fixed;top:30%;width:100%;left:0;">
|
<view class="video-container" :style="popupVideoStyle">
|
||||||
<video class="adaptive-video" :autoPauseIfNavigate="true" :autoPauseIfOpenNative="true"
|
<video
|
||||||
:autoplay="false" :enableAutoRotation="true" id="myVideo" :src="video_url" :controls="true"></video>
|
ref="popupVideoRef"
|
||||||
|
class="adaptive-video"
|
||||||
|
:autoPauseIfNavigate="true"
|
||||||
|
:autoPauseIfOpenNative="true"
|
||||||
|
:autoplay="false"
|
||||||
|
:enableAutoRotation="true"
|
||||||
|
id="myVideo"
|
||||||
|
:src="video_url"
|
||||||
|
:controls="true"
|
||||||
|
@loadedmetadata="onPopupVideoLoaded"
|
||||||
|
></video>
|
||||||
</view>
|
</view>
|
||||||
</uni-popup>
|
</uni-popup>
|
||||||
</view>
|
</view>
|
||||||
@@ -126,12 +136,38 @@ export default {
|
|||||||
isIndicatorDots() {
|
isIndicatorDots() {
|
||||||
return this.value.carousel.type != 'hide' &&
|
return this.value.carousel.type != 'hide' &&
|
||||||
1 != Math.ceil(this.value.list.length / (this.value.pageCount * this.value.rowCount));
|
1 != Math.ceil(this.value.list.length / (this.value.pageCount * this.value.rowCount));
|
||||||
|
},
|
||||||
|
|
||||||
|
// 弹窗视频容器样式
|
||||||
|
popupVideoStyle() {
|
||||||
|
let style = 'position:fixed;width:100%;left:0;';
|
||||||
|
|
||||||
|
if (this.popupVideoRatio) {
|
||||||
|
// 根据视频比例设置位置
|
||||||
|
if (this.popupVideoRatio === 'portrait') {
|
||||||
|
// 竖屏视频:居中显示,使用9/16比例
|
||||||
|
style += 'top:50%;transform:translateY(-50%);';
|
||||||
|
style += 'aspect-ratio: 9 / 16;';
|
||||||
|
} else {
|
||||||
|
// 横屏视频:靠近顶部显示,使用16/9比例
|
||||||
|
style += 'top:20%;';
|
||||||
|
style += 'aspect-ratio: 16 / 9;';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 默认:居中显示,使用竖屏比例
|
||||||
|
style += 'top:50%;transform:translateY(-50%);';
|
||||||
|
style += 'aspect-ratio: 9 / 16;';
|
||||||
|
}
|
||||||
|
|
||||||
|
return style;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 显示视频播放弹窗
|
// 显示视频播放弹窗
|
||||||
showVideo(item) {
|
showVideo(item) {
|
||||||
this.video_url = item.videoUrl;
|
this.video_url = item.videoUrl;
|
||||||
|
// 重置视频比例为null,等待新的检测结果
|
||||||
|
this.popupVideoRatio = null;
|
||||||
this.$refs.videoPopup.open();
|
this.$refs.videoPopup.open();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -139,6 +175,39 @@ export default {
|
|||||||
imgError(index) {
|
imgError(index) {
|
||||||
// 图片加载失败的处理逻辑
|
// 图片加载失败的处理逻辑
|
||||||
console.log('图片加载失败:', index);
|
console.log('图片加载失败:', index);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 弹窗视频加载完成事件
|
||||||
|
onPopupVideoLoaded(event) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
let width = 0;
|
||||||
|
let height = 0;
|
||||||
|
|
||||||
|
// H5 端:获取真实视频宽高
|
||||||
|
// #ifdef H5
|
||||||
|
const video = this.$refs.popupVideoRef;
|
||||||
|
if (video) {
|
||||||
|
width = video.videoWidth || 0;
|
||||||
|
height = video.videoHeight || 0;
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// 微信小程序端:通过事件对象获取视频宽高
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
if (event && event.detail) {
|
||||||
|
width = event.detail.width || 0;
|
||||||
|
height = event.detail.height || 0;
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
if (width && height) {
|
||||||
|
this.popupVideoRatio = width >= height ? 'landscape' : 'portrait';
|
||||||
|
console.log(`弹窗视频宽高比检测: ${width}x${height}, 比例: ${this.popupVideoRatio}`);
|
||||||
|
} else {
|
||||||
|
// 获取失败,默认竖屏
|
||||||
|
this.popupVideoRatio = 'portrait';
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<video data-component-name="diy-video" class="diy-video" :src="$util.img(value.videoUrl)"
|
<view class="video-container" :style="containerStyle">
|
||||||
:poster="$util.img(value.imageUrl)" :style="videoWarpCss" objectFit="cover"
|
<!-- H5端使用原生video标签 -->
|
||||||
@click="handlerClick(value.videoUrl)" @tap="handlerClick(value.videoUrl)"></video>
|
<!-- #ifdef H5 -->
|
||||||
|
<video
|
||||||
|
ref="videoRef"
|
||||||
|
data-component-name="diy-video"
|
||||||
|
class="diy-video h5-video"
|
||||||
|
:src="videoUrl"
|
||||||
|
:poster="posterUrl"
|
||||||
|
:style="videoWarpCss"
|
||||||
|
controls
|
||||||
|
preload="metadata"
|
||||||
|
@loadedmetadata="onVideoLoaded"
|
||||||
|
@click="handlerClick(videoUrl)"
|
||||||
|
></video>
|
||||||
|
<!-- #endif -->
|
||||||
|
|
||||||
|
<!-- 微信小程序端使用uni-app的video组件 -->
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<video
|
||||||
|
ref="videoRef"
|
||||||
|
data-component-name="diy-video"
|
||||||
|
class="diy-video mp-video"
|
||||||
|
:src="videoUrl"
|
||||||
|
:poster="posterUrl"
|
||||||
|
:style="videoWarpCss"
|
||||||
|
controls
|
||||||
|
@loadedmetadata="onVideoLoaded"
|
||||||
|
@error="onVideoError"
|
||||||
|
></video>
|
||||||
|
<!-- #endif -->
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -15,27 +44,160 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {};
|
return {
|
||||||
|
videoRatio: null, // 视频比例:'landscape'(横屏)或 'portrait'(竖屏)
|
||||||
|
videoUrl: '',
|
||||||
|
posterUrl: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.initVideoData();
|
||||||
},
|
},
|
||||||
created() { },
|
|
||||||
mixins: [DiyMinx],
|
mixins: [DiyMinx],
|
||||||
watch: {
|
watch: {
|
||||||
// 组件刷新监听
|
value: {
|
||||||
componentRefresh: function (nval) { }
|
immediate: true,
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.initVideoData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
videoWarpCss: function () {
|
videoWarpCss: function () {
|
||||||
var obj = '';
|
var obj = '';
|
||||||
if (this.value.componentAngle == 'round') {
|
if (this.value && this.value.componentAngle == 'round') {
|
||||||
obj += 'border-top-left-radius:' + this.value.topAroundRadius * 2 + 'rpx;';
|
obj += 'border-top-left-radius:' + this.value.topAroundRadius * 2 + 'rpx;';
|
||||||
obj += 'border-top-right-radius:' + this.value.topAroundRadius * 2 + 'rpx;';
|
obj += 'border-top-right-radius:' + this.value.topAroundRadius * 2 + 'rpx;';
|
||||||
obj += 'border-bottom-left-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
|
obj += 'border-bottom-left-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
|
||||||
obj += 'border-bottom-right-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
|
obj += 'border-bottom-right-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
|
},
|
||||||
|
// 动态计算容器比例
|
||||||
|
containerStyle() {
|
||||||
|
let style = '';
|
||||||
|
|
||||||
|
if (this.videoRatio === 'landscape') {
|
||||||
|
// 横屏视频:16/9比例
|
||||||
|
style += 'aspect-ratio: 16 / 9;';
|
||||||
|
} else if (this.videoRatio === 'portrait') {
|
||||||
|
// 竖屏视频:9/16比例
|
||||||
|
style += 'aspect-ratio: 9 / 16;';
|
||||||
|
} else {
|
||||||
|
// 未加载完成 → 默认使用竖屏比例
|
||||||
|
style += 'aspect-ratio: 9 / 16;';
|
||||||
|
}
|
||||||
|
|
||||||
|
return style;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// 初始化视频数据
|
||||||
|
initVideoData() {
|
||||||
|
this.videoUrl = this.getVideoUrl();
|
||||||
|
this.posterUrl = this.getPosterUrl();
|
||||||
|
// H5端:预加载视频获取宽高
|
||||||
|
// #ifdef H5
|
||||||
|
this.preloadVideoForRatio();
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取视频URL
|
||||||
|
getVideoUrl() {
|
||||||
|
if (!this.value || !this.value.videoUrl) return '';
|
||||||
|
const videoUrl = this.value.videoUrl;
|
||||||
|
// 如果是完整的http/https链接,直接使用
|
||||||
|
if (videoUrl.indexOf('http://') === 0 || videoUrl.indexOf('https://') === 0) {
|
||||||
|
return videoUrl;
|
||||||
|
}
|
||||||
|
// 否则使用$util.img()处理
|
||||||
|
return this.$util.img(videoUrl);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取封面图URL
|
||||||
|
getPosterUrl() {
|
||||||
|
if (!this.value || !this.value.imageUrl) return '';
|
||||||
|
return this.$util.img(this.value.imageUrl);
|
||||||
|
},
|
||||||
|
|
||||||
|
// H5端:预加载视频获取宽高比例
|
||||||
|
preloadVideoForRatio() {
|
||||||
|
const videoUrl = this.videoUrl;
|
||||||
|
if (!videoUrl) return;
|
||||||
|
|
||||||
|
console.log('H5端预加载视频获取比例:', videoUrl);
|
||||||
|
|
||||||
|
const video = document.createElement('video');
|
||||||
|
video.preload = 'metadata';
|
||||||
|
video.src = videoUrl;
|
||||||
|
video.muted = true;
|
||||||
|
video.playsInline = true;
|
||||||
|
|
||||||
|
video.addEventListener('loadedmetadata', () => {
|
||||||
|
const width = video.videoWidth;
|
||||||
|
const height = video.videoHeight;
|
||||||
|
console.log('H5端预加载获取视频宽高:', width, height);
|
||||||
|
|
||||||
|
if (width && height) {
|
||||||
|
this.videoRatio = width >= height ? 'landscape' : 'portrait';
|
||||||
|
console.log('H5端视频比例判定:', this.videoRatio, width >= height ? '横屏' : '竖屏');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
video.addEventListener('error', (e) => {
|
||||||
|
console.error('H5端预加载视频失败:', e);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 触发加载
|
||||||
|
video.load();
|
||||||
|
},
|
||||||
|
|
||||||
|
// 视频加载完成事件(组件内video标签)
|
||||||
|
onVideoLoaded(event) {
|
||||||
|
console.log('视频组件 loadedmetadata 事件触发');
|
||||||
|
|
||||||
|
let width = 0;
|
||||||
|
let height = 0;
|
||||||
|
|
||||||
|
// #ifdef H5
|
||||||
|
if (event && event.target) {
|
||||||
|
width = event.target.videoWidth || 0;
|
||||||
|
height = event.target.videoHeight || 0;
|
||||||
|
console.log('H5端组件视频宽高:', width, height);
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
if (event && event.detail) {
|
||||||
|
width = event.detail.width || 0;
|
||||||
|
height = event.detail.height || 0;
|
||||||
|
console.log('微信小程序端视频宽高:', width, height);
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
if (width && height) {
|
||||||
|
this.videoRatio = width >= height ? 'landscape' : 'portrait';
|
||||||
|
console.log('视频比例最终判定:', this.videoRatio);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 视频播放事件
|
||||||
|
onVideoPlay() {
|
||||||
|
console.log('视频开始播放');
|
||||||
|
},
|
||||||
|
|
||||||
|
// 视频暂停事件
|
||||||
|
onVideoPause() {
|
||||||
|
console.log('视频暂停');
|
||||||
|
},
|
||||||
|
|
||||||
|
// 视频错误事件
|
||||||
|
onVideoError(event) {
|
||||||
|
console.error('视频加载失败:', event);
|
||||||
|
},
|
||||||
|
|
||||||
async handlerClick(videoUrl) {
|
async handlerClick(videoUrl) {
|
||||||
await this.__$emitEvent({
|
await this.__$emitEvent({
|
||||||
eventName: 'video-tap', data: videoUrl, promiseCallback: (event, handler, awaitedResult) => {
|
eventName: 'video-tap', data: videoUrl, promiseCallback: (event, handler, awaitedResult) => {
|
||||||
@@ -48,11 +210,32 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
video {
|
.video-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.diy-video>>>.uni-video-container {
|
/* 基础样式 */
|
||||||
background-color: transparent;
|
.diy-video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* H5端样式 */
|
||||||
|
/* #ifdef H5 */
|
||||||
|
.h5-video {
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
/* #endif */
|
||||||
|
|
||||||
|
/* 微信小程序端样式 */
|
||||||
|
/* #ifdef MP-WEIXIN */
|
||||||
|
.mp-video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
/* #endif */
|
||||||
</style>
|
</style>
|
||||||
188
components-diy/js/wechat-channel.js
Normal file
188
components-diy/js/wechat-channel.js
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
// 微信视频号组件配置
|
||||||
|
export const wechatChannelConfig = {
|
||||||
|
// 图标相关
|
||||||
|
icon: {
|
||||||
|
defaultSize: 80, // 默认图标尺寸(rpx)
|
||||||
|
smallSize: 60, // 小图标尺寸(rpx)
|
||||||
|
channelArrowSize: 24, // 频道箭头图标尺寸(rpx)
|
||||||
|
channelArrow: 'addon/personnel/shop/view/enterprise/arrow.png', // 频道箭头图标路径
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// 视频相关配置
|
||||||
|
video: {
|
||||||
|
defaultHeight: 320, // 默认视频高度(rpx)
|
||||||
|
minHeight: 200, // 最小视频高度(rpx)
|
||||||
|
maxHeight: 500, // 最大视频高度(rpx)
|
||||||
|
defaultCoverUrl: 'addon/personnel/shop/view/enterprise/default-video-cover.png', // 默认视频封面
|
||||||
|
},
|
||||||
|
|
||||||
|
// 播放按钮配置
|
||||||
|
playButton: {
|
||||||
|
size: 80, // 标准尺寸(rpx)
|
||||||
|
smallSize: 60, // 小尺寸(rpx)
|
||||||
|
iconSize: 40, // 标准图标尺寸(rpx)
|
||||||
|
smallIconSize: 30, // 小图标尺寸(rpx)
|
||||||
|
background: 'rgba(0, 0, 0, 0.4)', // 背景颜色
|
||||||
|
},
|
||||||
|
|
||||||
|
// 布局配置
|
||||||
|
layout: {
|
||||||
|
borderRadius: 12, // 圆角(rpx)
|
||||||
|
padding: 16, // 内边距(rpx)
|
||||||
|
margin: 10, // 外边距(rpx)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 字体配置
|
||||||
|
font: {
|
||||||
|
channelNameSize: 28, // 频道名称字体大小(rpx)
|
||||||
|
videoTitleSize: 28, // 视频标题字体大小(rpx)
|
||||||
|
statsSize: 24, // 统计信息字体大小(rpx)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 颜色配置
|
||||||
|
color: {
|
||||||
|
channelName: '#333', // 频道名称颜色
|
||||||
|
videoTitle: '#333', // 视频标题颜色
|
||||||
|
stats: '#999', // 统计信息颜色
|
||||||
|
border: '#f0f0f0', // 边框颜色
|
||||||
|
},
|
||||||
|
|
||||||
|
// 微信相关配置
|
||||||
|
wechat: {
|
||||||
|
minSdkVersion: '2.19.2', // 最小微信基础库版本
|
||||||
|
embedComponent: 'channel-video', // 嵌入式视频组件名称
|
||||||
|
},
|
||||||
|
|
||||||
|
// 错误提示配置
|
||||||
|
error: {
|
||||||
|
notWechat: '当前环境不是微信小程序',
|
||||||
|
lowVersion: '当前微信基础库版本过低,需要 2.19.2 或以上版本',
|
||||||
|
notSupported: '当前环境不支持微信视频号',
|
||||||
|
missingFields: '缺少必要字段: {field}',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取配置项的辅助函数
|
||||||
|
export const getwechatChannelConfig = (key, defaultValue = null) => {
|
||||||
|
const keys = key.split('.');
|
||||||
|
let config = wechatChannelConfig;
|
||||||
|
|
||||||
|
for (const k of keys) {
|
||||||
|
if (config[k] === undefined) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
config = config[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 微信视频号工具函数
|
||||||
|
export const wechatChannelUtil = {
|
||||||
|
// 版本比较
|
||||||
|
versionCompare(v1, v2) {
|
||||||
|
const arr1 = v1.split('.');
|
||||||
|
const arr2 = v2.split('.');
|
||||||
|
for (let i = 0; i < Math.max(arr1.length, arr2.length); i++) {
|
||||||
|
const num1 = parseInt(arr1[i] || 0);
|
||||||
|
const num2 = parseInt(arr2[i] || 0);
|
||||||
|
if (num1 > num2) return 1;
|
||||||
|
if (num1 < num2) return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 检查是否支持嵌入式播放
|
||||||
|
isEmbedModeSupported() {
|
||||||
|
return typeof wx !== 'undefined' && wx.canIUse(wechatChannelConfig.wechat.embedComponent);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取默认视频封面
|
||||||
|
getDefaultCoverUrl() {
|
||||||
|
return wechatChannelConfig.video.defaultCoverUrl;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 数据校验
|
||||||
|
validateVideoData(item) {
|
||||||
|
const requiredFields = ['feedId', 'finderUserName'];
|
||||||
|
for (const field of requiredFields) {
|
||||||
|
if (!item[field]) {
|
||||||
|
throw new Error(`缺少必要字段: ${field}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 播放视频
|
||||||
|
playVideo(item) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
// 数据校验
|
||||||
|
this.validateVideoData(item);
|
||||||
|
} catch (err) {
|
||||||
|
this.handleError(err, item);
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查微信环境
|
||||||
|
if (typeof wx === 'undefined') {
|
||||||
|
const err = new Error(wechatChannelConfig.error.notWechat);
|
||||||
|
this.handleError(err, item);
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查基础库版本
|
||||||
|
const systemInfo = wx.getSystemInfoSync();
|
||||||
|
const SDKVersion = systemInfo.SDKVersion;
|
||||||
|
if (this.versionCompare(SDKVersion, wechatChannelConfig.wechat.minSdkVersion) < 0) {
|
||||||
|
const err = new Error(wechatChannelConfig.error.lowVersion);
|
||||||
|
this.handleError(err, item);
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用微信视频号播放API
|
||||||
|
if (wx.openChannelsActivity) {
|
||||||
|
wx.openChannelsActivity({
|
||||||
|
feedId: item.feedId,
|
||||||
|
finderUserName: item.finderUserName,
|
||||||
|
success: (res) => {
|
||||||
|
console.log('打开视频号成功', res);
|
||||||
|
resolve(res);
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
this.handleError(err, item);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const err = new Error(wechatChannelConfig.error.notSupported);
|
||||||
|
this.handleError(err, item);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 统一错误处理
|
||||||
|
handleError(err, item) {
|
||||||
|
console.error('微信视频号错误:', err);
|
||||||
|
const { message = '', errMsg = '微信视频播放失败!', errCode = -1 } = err;
|
||||||
|
|
||||||
|
// 错误码5: 表示用户点击了取消. openChannelsActivity:fail cancel
|
||||||
|
if ([5].includes(errCode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const showErrorToast = (otherMsgs = []) => {
|
||||||
|
uni.showToast({
|
||||||
|
title: [errMsg, message,...otherMsgs].join('\n'),
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
showErrorToast();
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -5,5 +5,18 @@ export default {
|
|||||||
// console.log('__$emitEvent', payload)
|
// console.log('__$emitEvent', payload)
|
||||||
await this.$eventBus.emit(payload.eventName, payload.data, payload.promiseCallback)
|
await this.$eventBus.emit(payload.eventName, payload.data, payload.promiseCallback)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 预览图片
|
||||||
|
previewImg(img) {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
uni.previewImage({
|
||||||
|
current: 0,
|
||||||
|
urls: [this.$util.img(img)],
|
||||||
|
success: function (res) { },
|
||||||
|
fail: function (res) { },
|
||||||
|
complete: function (res) { },
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"component": true,
|
|
||||||
"usingComponents": {
|
|
||||||
"ns-loading": "../ns-loading/ns-loading"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -156,11 +156,11 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
/deep/.uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
::v-deep .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
||||||
background-color: #000;
|
background-color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/.uni-popup__wrapper.uni-custom.center .uni-popup__wrapper-box {
|
::v-deep .uni-popup__wrapper.uni-custom.center .uni-popup__wrapper-box {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
@@ -179,6 +179,7 @@
|
|||||||
.chat-message {
|
.chat-message {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
background: white; /* 白色 */
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
padding: 13rpx 20rpx;
|
padding: 13rpx 20rpx;
|
||||||
@@ -270,7 +271,7 @@
|
|||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
background-color: #4cd964;
|
background-color: #c4e0ff; /* 浅蓝色 */
|
||||||
margin-right: 28rpx;
|
margin-right: 28rpx;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
line-height: 36rpx;
|
line-height: 36rpx;
|
||||||
|
|||||||
@@ -1,243 +1,330 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 悬浮按钮 -->
|
<view v-if="pageCount === 1 || need" class="fixed-box"
|
||||||
<view v-if="pageCount == 1 || need" class="fixed-box"
|
:style="[customContainerStyle, {
|
||||||
:style="[{ height: fixBtnShow ? '400rpx' : '320rpx' }, customContainerStyle]">
|
height: containerHeight,
|
||||||
<!-- <view class="btn-item" v-if="fixBtnShow" @click="$util.redirectTo('/pages/index/index')"> -->
|
backgroundImage: bgUrl ? `url(${bgUrl})` : '',
|
||||||
<!-- 切换语言按钮 -->
|
backgroundSize: 'cover'
|
||||||
<button class="btn-item" v-if="fixBtnShow && isLanguageSwitchEnabled" @click="toggleLanguage()"
|
}]">
|
||||||
:style="[{ backgroundSize: '100% 100%' }, customButtonStyle]">
|
<!-- 中英文切换按钮 -->
|
||||||
<text :style="customTextStyle">{{ currentLangDisplayName }}</text>
|
<view
|
||||||
</button>
|
v-if="isLanguageSwitchEnabled && fixBtnShow"
|
||||||
<!-- 客服按钮 -->
|
class="btn-item common-bg"
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
@click="toggleLanguage"
|
||||||
<button class="btn-item" v-if="fixBtnShow" hoverClass="none" openType="contact" sessionFrom="weapp"
|
>
|
||||||
showMessageCard="true"
|
<text>{{ currentLangDisplayName }}</text>
|
||||||
:style="[{ backgroundImage: 'url(' + (kefuimg ? kefuimg : '') + ')', backgroundSize: '100% 100%' }, customButtonStyle]">
|
</view>
|
||||||
<text class="icox icox-kefu" v-if="!kefuimg"></text>
|
<!-- 微信官方客服按钮-->
|
||||||
<!-- <view>首页</view> -->
|
<button
|
||||||
</button>
|
v-if="fixBtnShow && hasValidKefuConfig && isWeappOfficialKefu"
|
||||||
<!-- #endif -->
|
class="btn-item common-bg"
|
||||||
<view class="btn-item" v-if="fixBtnShow" @click="call()"
|
open-type="contact"
|
||||||
:style="[{ backgroundImage: 'url(' + (phoneimg ? phoneimg : '') + ')', backgroundSize: '100% 100%' }, customButtonStyle]">
|
>
|
||||||
<text class="iconfont icon-dianhua" v-if="!phoneimg"></text>
|
<text class="icox icox-kefu" v-if="!kefuimg"></text>
|
||||||
<!-- <view>我的</view> -->
|
</button>
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- <view class="btn-item icon-xiala" v-if="fixBtnShow" @click="fixBtnShow ? (fixBtnShow = false) : (fixBtnShow = true)">
|
<!-- 其他类型客服按钮(智能客服、企业微信等)-->
|
||||||
<text class="iconfont icon-unfold"></text>
|
<view
|
||||||
</view>
|
v-if="fixBtnShow && hasValidKefuConfig && !isWeappOfficialKefu && !showWeappSystemKefu"
|
||||||
<view class="btn-item switch" v-else :class="{ show: fixBtnShow }"
|
class="btn-item common-bg"
|
||||||
@click="fixBtnShow ? (fixBtnShow = false) : (fixBtnShow = true)">
|
@click="handleUnifiedKefuClick"
|
||||||
<view class="">快捷</view>
|
>
|
||||||
<view>导航</view>
|
<image
|
||||||
</view> -->
|
v-if="currentKefuImg"
|
||||||
</view>
|
:src="currentKefuImg"
|
||||||
|
mode="aspectFill"
|
||||||
|
class="kefu-img"
|
||||||
|
></image>
|
||||||
|
<text class="ai-icon" v-if="isAIKefu && !currentKefuImg">🤖</text>
|
||||||
|
<text class="ai-icon" v-if="isWxworkKefu && !currentKefuImg">💬</text>
|
||||||
|
<text class="ai-icon" v-if="!isAIKefu && !isWxworkKefu && !currentKefuImg">👤</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 智能客服按钮(当同时显示小程序系统客服时)-->
|
||||||
|
<view
|
||||||
|
v-if="fixBtnShow && hasValidKefuConfig && !isWeappOfficialKefu && showWeappSystemKefu"
|
||||||
|
class="btn-item common-bg"
|
||||||
|
@click="handleUnifiedKefuClick"
|
||||||
|
>
|
||||||
|
<image
|
||||||
|
v-if="currentKefuImg"
|
||||||
|
:src="currentKefuImg"
|
||||||
|
mode="aspectFill"
|
||||||
|
class="kefu-img"
|
||||||
|
></image>
|
||||||
|
<text class="ai-icon" v-if="isAIKefu && !currentKefuImg">🤖</text>
|
||||||
|
<text class="ai-icon" v-if="isWxworkKefu && !currentKefuImg">💬</text>
|
||||||
|
<text class="ai-icon" v-if="!isAIKefu && !isWxworkKefu && !currentKefuImg">👤</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 小程序系统客服按钮 -->
|
||||||
|
<button
|
||||||
|
v-if="fixBtnShow && hasValidKefuConfig && showWeappSystemKefu"
|
||||||
|
class="btn-item common-bg"
|
||||||
|
open-type="contact"
|
||||||
|
>
|
||||||
|
<text class="icox icox-kefu" v-if="!kefuimg"></text>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 电话按钮 -->
|
||||||
|
<view
|
||||||
|
v-if="fixBtnShow"
|
||||||
|
class="btn-item common-bg"
|
||||||
|
@click="call"
|
||||||
|
:style="[{ backgroundImage: phoneimg ? `url(${phoneimg})` : '', backgroundSize: '100% 100%' }, customButtonStyle]"
|
||||||
|
>
|
||||||
|
<text class="iconfont icon-dianhua" v-if="!phoneimg"></text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { createCustomerService } from '@/common/js/customer-service.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'hover-nav',
|
name: 'hover-nav',
|
||||||
props: {
|
props: {
|
||||||
need: {
|
need: { type: Boolean, default: false }
|
||||||
type: Boolean,
|
},
|
||||||
default: false
|
data() {
|
||||||
}
|
return {
|
||||||
},
|
pageCount: 0,
|
||||||
data() {
|
fixBtnShow: true,
|
||||||
return {
|
shopInfo: null,
|
||||||
pageCount: 0,
|
currentLangIndex: 0,
|
||||||
fixBtnShow: true,
|
langIndexMap: {},
|
||||||
tel: '',
|
customerService: null,
|
||||||
kefuimg: '',
|
};
|
||||||
phoneimg: '',
|
},
|
||||||
shopInfo: null, // 店铺信息对象
|
computed: {
|
||||||
|
bgUrl() {
|
||||||
|
return this.shopInfo?.bgUrl || '';
|
||||||
|
},
|
||||||
|
aiAgentimg() {
|
||||||
|
return this.shopInfo?.aiAgentimg || '';
|
||||||
|
},
|
||||||
|
kefuimg() {
|
||||||
|
return this.shopInfo?.kefuimg || this.$util.getDefaultImage().kefu;
|
||||||
|
},
|
||||||
|
phoneimg() {
|
||||||
|
return this.shopInfo?.phoneimg || this.$util.getDefaultImage().phone;
|
||||||
|
},
|
||||||
|
tel() {
|
||||||
|
return this.shopInfo?.mobile || '';
|
||||||
|
},
|
||||||
|
isLanguageSwitchEnabled() {
|
||||||
|
return !!this.shopInfo?.ischina;
|
||||||
|
},
|
||||||
|
currentLangDisplayName() {
|
||||||
|
const lang = this.langIndexMap[this.currentLangIndex];
|
||||||
|
return lang === 'zh-cn' ? 'EN' : 'CN';
|
||||||
|
},
|
||||||
|
customContainerStyle() {
|
||||||
|
return this.shopInfo?.floatingButton?.container || {};
|
||||||
|
},
|
||||||
|
customButtonStyle() {
|
||||||
|
return this.shopInfo?.floatingButton?.button || {};
|
||||||
|
},
|
||||||
|
unreadCount() {
|
||||||
|
const customerService = createCustomerService(this);
|
||||||
|
return customerService.getUnreadCount();
|
||||||
|
},
|
||||||
|
currentKefuImg() {
|
||||||
|
const customerService = createCustomerService(this);
|
||||||
|
return customerService.getKefuButtonIcon();
|
||||||
|
},
|
||||||
|
isWeappOfficialKefu() {
|
||||||
|
const customerService = createCustomerService(this);
|
||||||
|
return customerService.isWeappOfficialKefu();
|
||||||
|
},
|
||||||
|
isAIKefu() {
|
||||||
|
const customerService = createCustomerService(this);
|
||||||
|
return customerService.isAIKefu();
|
||||||
|
},
|
||||||
|
isWxworkKefu() {
|
||||||
|
const customerService = createCustomerService(this);
|
||||||
|
return customerService.isWxworkKefu();
|
||||||
|
},
|
||||||
|
isThirdKefu() {
|
||||||
|
const customerService = createCustomerService(this);
|
||||||
|
return customerService.isThirdKefu();
|
||||||
|
},
|
||||||
|
showWeappSystemKefu() {
|
||||||
|
const customerService = createCustomerService(this);
|
||||||
|
return customerService.shouldShowExtraWeappSystemKefu();
|
||||||
|
},
|
||||||
|
hasValidKefuConfig() {
|
||||||
|
const customerService = createCustomerService(this);
|
||||||
|
return customerService.hasValidKefuConfig();
|
||||||
|
},
|
||||||
|
//根据显示的按钮数量动态计算容器高度
|
||||||
|
containerHeight() {
|
||||||
|
if (!this.fixBtnShow) return '320rpx';
|
||||||
|
|
||||||
// --- 语言切换相关 ---
|
let buttonCount = 1;
|
||||||
currentLangIndex: 0, // 当前选中的语言索引
|
if (this.isLanguageSwitchEnabled) buttonCount++;
|
||||||
langIndexMap: {}, // 语言索引到语言值的映射
|
if (this.showWeappSystemKefu) buttonCount++;
|
||||||
isLanguageSwitchEnabled: false, // 是否启用语言切换
|
buttonCount++;
|
||||||
|
const totalRpx = 94 * buttonCount - 14;
|
||||||
|
const pxValue = Math.round(totalRpx * 0.5);
|
||||||
|
|
||||||
// --- 其他 ---
|
return ` $ {pxValue}px`;
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
created() {
|
watch: {
|
||||||
// 初始化语言设置
|
shopInfo: {
|
||||||
this.initLanguage();
|
handler(newVal) {
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.initLanguage();
|
||||||
|
this.pageCount = getCurrentPages().length;
|
||||||
|
uni.getStorage({
|
||||||
|
key: 'shopInfo',
|
||||||
|
success: (e) => {
|
||||||
|
console.log('【调试】当前 shopInfo:', e.data);
|
||||||
|
this.shopInfo = e.data;
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
console.warn('未获取到 shopInfo,使用默认设置');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 初始化 customerService 实例
|
||||||
|
this.customerService = createCustomerService(this);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initLanguage() {
|
||||||
|
this.langList = this.$langConfig.list();
|
||||||
|
this.langIndexMap = {};
|
||||||
|
for (let i = 0; i < this.langList.length; i++) {
|
||||||
|
this.langIndexMap[i] = this.langList[i].value;
|
||||||
|
}
|
||||||
|
const savedLang = this.$langConfig.getCurrentLocale();
|
||||||
|
if (savedLang) {
|
||||||
|
for (let i = 0; i < this.langList.length; i++) {
|
||||||
|
if (this.langList[i].value === savedLang) {
|
||||||
|
this.currentLangIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.currentLangIndex = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
this.kefuimg = this.$util.getDefaultImage().kefu
|
call() {
|
||||||
this.phoneimg = this.$util.getDefaultImage().phone
|
this.customerService.makePhoneCallByNumber(this.tel);
|
||||||
this.pageCount = getCurrentPages().length;
|
},
|
||||||
|
|
||||||
// 从店铺信息中获取相关信息
|
toggleLanguage() {
|
||||||
uni.getStorage({
|
this.currentLangIndex = this.currentLangIndex === 0 ? 1 : 0;
|
||||||
key: 'shopInfo',
|
const targetLang = this.langIndexMap[this.currentLangIndex];
|
||||||
success: (e) => {
|
|
||||||
console.log(`获取到的shopInfo:${JSON.stringify(e.data)}`)
|
|
||||||
this.shopInfo = e.data;
|
|
||||||
|
|
||||||
// 从店铺信息中获取手机号
|
uni.setStorageSync("lang", targetLang);
|
||||||
this.tel = e.data.mobile
|
|
||||||
// 从店铺信息中获取是否启用语言切换
|
|
||||||
this.isLanguageSwitchEnabled = e.data.ischina;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
currentLangDisplayName() {
|
|
||||||
// 语言切换按钮显示名称,当前语言为中文时显示EN,否则显示CN
|
|
||||||
// 根据 langIndexMap 获取当前语言值
|
|
||||||
const lang = this.langIndexMap[this.currentLangIndex];
|
|
||||||
return lang == 'zh-cn' ? 'EN' : 'CN';
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获取容器的自定义样式
|
const pages = getCurrentPages();
|
||||||
customContainerStyle() {
|
const currentPage = pages[pages.length - 1];
|
||||||
// 只返回非空的样式属性,确保不覆盖原有的默认样式
|
const currentRoute = currentPage.route;
|
||||||
return this.shopInfo?.floatingButton?.container || {};
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获取按钮的自定义样式
|
uni.reLaunch({
|
||||||
customButtonStyle() {
|
url: '/' + currentRoute
|
||||||
// 只返回非空的样式属性,确保不覆盖原有的默认样式
|
});
|
||||||
return this.shopInfo?.floatingButton?.button || {};
|
},
|
||||||
},
|
|
||||||
|
|
||||||
// 获取文本的自定义样式
|
handleUnifiedKefuClick() {
|
||||||
customTextStyle() {
|
this.customerService.handleUnifiedKefuClick({
|
||||||
// 只返回非空的样式属性,确保不覆盖原有的默认样式
|
sendMessageTitle: '来自悬浮按钮的咨询',
|
||||||
return this.shopInfo?.floatingButton?.text || {};
|
sendMessagePath: '/pages/index/index'
|
||||||
}
|
});
|
||||||
},
|
}
|
||||||
methods: {
|
}
|
||||||
initLanguage() {
|
}
|
||||||
// 初始化语言列表
|
|
||||||
this.langList = this.$langConfig.list();
|
|
||||||
|
|
||||||
// 初始化语言索引映射
|
|
||||||
for (let i = 0; i < this.langList.length; i++) {
|
|
||||||
this.langIndexMap[i] = this.langList[i].value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取存储的语言设置
|
|
||||||
const savedLang = uni.getStorageSync('lang');
|
|
||||||
if (savedLang) {
|
|
||||||
for (let i = 0; i < this.langList.length; i++) {
|
|
||||||
if (this.langList[i].value == savedLang) {
|
|
||||||
this.currentLangIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.currentLangIndex = 0;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//拨打电话
|
|
||||||
call() {
|
|
||||||
uni.makePhoneCall({
|
|
||||||
phoneNumber: this.tel + ''
|
|
||||||
})
|
|
||||||
},
|
|
||||||
toggleLanguage() {
|
|
||||||
// 切换语言索引
|
|
||||||
this.currentLangIndex = this.currentLangIndex == 0 ? 1 : 0;
|
|
||||||
const targetLang = this.langIndexMap[this.currentLangIndex];
|
|
||||||
|
|
||||||
// 获取当前页面路由
|
|
||||||
let currentRoute = this.$util.getCurrentRoute().path;
|
|
||||||
// 切换语言并重新加载当前页面
|
|
||||||
this.$langConfig.change(targetLang, currentRoute);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss" scoped>
|
||||||
.container-box {
|
.fixed-box {
|
||||||
width: 100%;
|
position: fixed;
|
||||||
|
right: 0rpx;
|
||||||
.item-wrap {
|
bottom: 240rpx;
|
||||||
border-radius: 10rpx;
|
/* #ifdef H5 */
|
||||||
|
bottom: 320rpx;
|
||||||
.image-box {
|
/* #endif */
|
||||||
border-radius: 10rpx;
|
z-index: 10;
|
||||||
}
|
border-radius: 120rpx;
|
||||||
|
padding: 20rpx 0;
|
||||||
image {
|
display: flex;
|
||||||
width: 100%;
|
justify-content: center;
|
||||||
height: auto;
|
flex-direction: column;
|
||||||
border-radius: 10rpx;
|
width: 100rpx;
|
||||||
will-change: transform;
|
box-sizing: border-box;
|
||||||
}
|
transition: 0.3s;
|
||||||
}
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
//悬浮按钮
|
.btn-item {
|
||||||
.fixed-box {
|
display: flex;
|
||||||
position: fixed;
|
justify-content: center;
|
||||||
right: 0rpx;
|
text-align: center;
|
||||||
bottom: 240rpx;
|
flex-direction: column;
|
||||||
// #ifdef H5
|
line-height: 1;
|
||||||
bottom: 320rpx;
|
margin: 14rpx 0;
|
||||||
// #endif
|
transition: 0.1s;
|
||||||
z-index: 10;
|
color: var(--hover-nav-text-color);
|
||||||
border-radius: 120rpx;
|
border-radius: 50%;
|
||||||
padding: 20rpx 0;
|
width: 80rpx;
|
||||||
display: flex;
|
height: 80rpx;
|
||||||
justify-content: center;
|
padding: 0;
|
||||||
flex-direction: column;
|
overflow: hidden;
|
||||||
width: 100rpx;
|
}
|
||||||
box-sizing: border-box;
|
|
||||||
transition: 0.3s;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.btn-item {
|
.common-bg {
|
||||||
display: flex;
|
background-color: var(--hover-nav-bg-color);
|
||||||
justify-content: center;
|
}
|
||||||
text-align: center;
|
|
||||||
flex-direction: column;
|
|
||||||
line-height: 1;
|
|
||||||
margin: 14rpx 0;
|
|
||||||
transition: 0.1s;
|
|
||||||
background: var(--hover-nav-bg-color);
|
|
||||||
color: var(--hover-nav-text-color);
|
|
||||||
border-radius: 40rpx;
|
|
||||||
width: 80rpx;
|
|
||||||
height: 80rpx;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
text {
|
.btn-item text {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
}
|
}
|
||||||
.iconfont,
|
|
||||||
.icox {
|
|
||||||
font-size: 36rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 40rpx;
|
|
||||||
/* 确保图标本身也是圆形 */
|
|
||||||
}
|
|
||||||
|
|
||||||
view {
|
.iconfont,
|
||||||
font-size: 26rpx;
|
.icox {
|
||||||
font-weight: bold;
|
font-size: 36rpx;
|
||||||
}
|
font-weight: bold;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin: 14rpx 0;
|
||||||
|
border-radius: 50rpx;
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
&.show {
|
.iconfont text,
|
||||||
transform: rotate(180deg);
|
.icox text {
|
||||||
}
|
font-size: 36rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
&.switch {}
|
.kefu-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
&.icon-xiala {
|
.ai-icon {
|
||||||
margin: 0;
|
font-size: 40rpx;
|
||||||
margin-top: 0.1rpx;
|
width: 100%;
|
||||||
}
|
height: 100%;
|
||||||
}
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -3,28 +3,37 @@
|
|||||||
<slot></slot>
|
<slot></slot>
|
||||||
<!-- #ifdef MP-ALIPAY -->
|
<!-- #ifdef MP-ALIPAY -->
|
||||||
<view class="contact-button" @click="contactServicer">
|
<view class="contact-button" @click="contactServicer">
|
||||||
<contact-button :tnt-inst-id="config.instid" :scene="config.scene" size="1000rpx" v-if="config.type == 'aliapp'"/>
|
<contact-button :tnt-inst-id="config.instid" :scene="config.scene" size="1000rpx"
|
||||||
|
v-if="config.type == 'aliapp'" />
|
||||||
</view>
|
</view>
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
<!-- #ifndef MP-ALIPAY -->
|
|
||||||
<button
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
type="default"
|
<!-- 微信小程序官方客服按钮 -->
|
||||||
hover-class="none"
|
<button v-if="useOfficialService" type="default" hover-class="none" open-type="contact"
|
||||||
:open-type="openType"
|
class="contact-button" sessionFrom="weapp" showMessageCard="true"
|
||||||
class="contact-button"
|
:send-message-title="sendMessageTitle" :send-message-path="sendMessagePath"
|
||||||
@click="contactServicer"
|
:send-message-img="sendMessageImg"></button>
|
||||||
:send-message-title="sendMessageTitle"
|
|
||||||
:send-message-path="sendMessagePath"
|
<!-- 微信小程序自定义客服按钮 -->
|
||||||
:send-message-img="sendMessageImg"
|
<button v-else type="default" hover-class="none" class="contact-button" @click="contactServicer"></button>
|
||||||
:show-message-card="true"></button>
|
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
|
|
||||||
|
<!-- #ifndef MP-WEIXIN && MP-ALIPAY -->
|
||||||
|
<!-- 其他平台保持原逻辑 -->
|
||||||
|
<button type="default" hover-class="none" :open-type="openType" class="contact-button" @click="contactServicer"
|
||||||
|
:send-message-title="sendMessageTitle" :send-message-path="sendMessagePath"
|
||||||
|
:send-message-img="sendMessageImg" :show-message-card="true"></button>
|
||||||
|
<!-- #endif -->
|
||||||
|
|
||||||
<uni-popup ref="servicePopup" type="center">
|
<uni-popup ref="servicePopup" type="center">
|
||||||
<view class="service-popup-wrap">
|
<view class="service-popup-wrap">
|
||||||
<view class="head-wrap" @click="$refs.servicePopup.close()">
|
<view class="head-wrap" @click="$refs.servicePopup.close()">
|
||||||
<text>联系客服</text>
|
<text>联系客服</text>
|
||||||
<text class="iconfont icon-close"></text>
|
<text class="iconfont icon-close"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="body-wrap">{{ siteInfo.site_tel ? '请联系客服,客服电话是' + siteInfo.site_tel : '抱歉,商家暂无客服,请线下联系' }}</view>
|
<view class="body-wrap">{{ siteInfo.site_tel ? '请联系客服,客服电话是' + siteInfo.site_tel : '抱歉,商家暂无客服,请线下联系' }}
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</uni-popup>
|
</uni-popup>
|
||||||
</view>
|
</view>
|
||||||
@@ -32,140 +41,130 @@
|
|||||||
|
|
||||||
<!-- 客服组件 -->
|
<!-- 客服组件 -->
|
||||||
<script>
|
<script>
|
||||||
export default {
|
import { createCustomerService } from '@/common/js/customer-service.js';
|
||||||
name: 'ns-contact',
|
|
||||||
props: {
|
export default {
|
||||||
niushop: {
|
name: 'ns-contact',
|
||||||
type: Object,
|
props: {
|
||||||
default: function() {
|
niushop: {
|
||||||
return {};
|
type: Object,
|
||||||
}
|
default: function () {
|
||||||
},
|
return {};
|
||||||
sendMessageTitle: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
sendMessagePath: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
sendMessageImg: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
sendMessageTitle: {
|
||||||
return {
|
type: String,
|
||||||
config: null,
|
default: ''
|
||||||
openType: ''
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
created() {
|
sendMessagePath: {
|
||||||
if (this.servicerConfig) {
|
type: String,
|
||||||
// #ifdef H5
|
default: ''
|
||||||
this.config = this.servicerConfig.h5;
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
this.config = this.servicerConfig.weapp;
|
|
||||||
if (this.config.type == 'weapp') this.openType = 'contact';
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifdef MP-ALIPAY
|
|
||||||
this.config = this.servicerConfig.aliapp;
|
|
||||||
if (this.config.type == 'aliapp') this.openType = 'contact';
|
|
||||||
// #endif
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
sendMessageImg: {
|
||||||
/**
|
type: String,
|
||||||
* 联系客服
|
default: ''
|
||||||
*/
|
|
||||||
contactServicer() {
|
|
||||||
if (this.config.type == 'none') {
|
|
||||||
this.$refs.servicePopup.open();
|
|
||||||
}
|
|
||||||
if (this.openType == 'contact') return;
|
|
||||||
|
|
||||||
switch (this.config.type) {
|
|
||||||
case 'wxwork':
|
|
||||||
// #ifdef H5
|
|
||||||
location.href = this.config.wxwork_url;
|
|
||||||
// #endif
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
wx.openCustomerServiceChat({
|
|
||||||
extInfo: { url: this.config.wxwork_url },
|
|
||||||
corpId: this.config.corpid,
|
|
||||||
showMessageCard: true,
|
|
||||||
sendMessageTitle: this.sendMessageTitle,
|
|
||||||
sendMessagePath: this.sendMessagePath,
|
|
||||||
sendMessageImg: this.sendMessageImg
|
|
||||||
});
|
|
||||||
// #endif
|
|
||||||
break;
|
|
||||||
case 'third':
|
|
||||||
location.href = this.config.third_url;
|
|
||||||
break;
|
|
||||||
case 'niushop':
|
|
||||||
this.$util.redirectTo('/pages_tool/chat/room', this.niushop);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.makePhoneCall();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 店铺联系方式
|
|
||||||
*/
|
|
||||||
makePhoneCall() {
|
|
||||||
this.$api.sendRequest({
|
|
||||||
url: '/api/site/shopcontact',
|
|
||||||
success: res => {
|
|
||||||
if (res.code == 0 && res.data.mobile) uni.makePhoneCall({
|
|
||||||
phoneNumber: res.data
|
|
||||||
.mobile
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
customerService: null,
|
||||||
|
buttonConfig: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* 是否使用官方客服
|
||||||
|
*/
|
||||||
|
useOfficialService() {
|
||||||
|
if (!this.buttonConfig) return true;
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
// 如果是微信小程序,检查配置
|
||||||
|
if (this.buttonConfig.type === 'weapp') {
|
||||||
|
// 默认使用官方客服,除非明确设置为false
|
||||||
|
return this.buttonConfig.useOfficial !== false;
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客服配置
|
||||||
|
*/
|
||||||
|
config() {
|
||||||
|
return this.customerService?.getPlatformConfig() || {};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开类型
|
||||||
|
*/
|
||||||
|
openType() {
|
||||||
|
return this.buttonConfig?.openType || '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// 初始化客服服务
|
||||||
|
this.customerService = createCustomerService(this);
|
||||||
|
this.buttonConfig = this.customerService.getButtonConfig();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 联系客服
|
||||||
|
*/
|
||||||
|
contactServicer() {
|
||||||
|
// 如果是微信/支付宝小程序客服,由系统自动处理
|
||||||
|
if (this.buttonConfig.openType === 'contact') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用统一客服处理
|
||||||
|
this.customerService.handleCustomerClick({
|
||||||
|
niushop: this.niushop,
|
||||||
|
sendMessageTitle: this.sendMessageTitle,
|
||||||
|
sendMessagePath: this.sendMessagePath,
|
||||||
|
sendMessageImg: this.sendMessageImg
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.contact-wrap {
|
.contact-wrap {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.contact-button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 5;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.contact-button {
|
.service-popup-wrap {
|
||||||
width: 100%;
|
width: 600rpx;
|
||||||
height: 100%;
|
|
||||||
position: absolute;
|
.head-wrap {
|
||||||
left: 0;
|
display: flex;
|
||||||
top: 0;
|
justify-content: space-between;
|
||||||
z-index: 5;
|
align-items: center;
|
||||||
padding: 0;
|
padding: 0 30rpx;
|
||||||
margin: 0;
|
height: 90rpx;
|
||||||
opacity: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-popup-wrap {
|
.body-wrap {
|
||||||
width: 600rpx;
|
text-align: center;
|
||||||
|
padding: 30rpx;
|
||||||
.head-wrap {
|
height: 100rpx;
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 30rpx;
|
|
||||||
height: 90rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.body-wrap {
|
|
||||||
text-align: center;
|
|
||||||
padding: 30rpx;
|
|
||||||
height: 100rpx;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -45,7 +45,7 @@ export default {
|
|||||||
if (this.emptyBtn.url) {
|
if (this.emptyBtn.url) {
|
||||||
this.$util.redirectTo(this.emptyBtn.url, {}, 'redirectTo');
|
this.$util.redirectTo(this.emptyBtn.url, {}, 'redirectTo');
|
||||||
} else {
|
} else {
|
||||||
this.$util.redirectTo('/pages/index/index');
|
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
re(text) {
|
re(text) {
|
||||||
|
|||||||
@@ -843,15 +843,15 @@ export default {
|
|||||||
z-index: 5;
|
z-index: 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .uni-popup__wrapper {
|
::v-deep .uni-popup__wrapper {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .sku-layer .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
::v-deep .sku-layer .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
::v-deep .uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -28,7 +28,7 @@ export default {
|
|||||||
*/
|
*/
|
||||||
addCart(config, data, event) {
|
addCart(config, data, event) {
|
||||||
if (!this.storeToken) {
|
if (!this.storeToken) {
|
||||||
this.$refs.login.open('/pages/index/index')
|
this.$refs.login.open(this.$util.INDEX_PAGE_URL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (config == "detail" || data.is_virtual) {
|
if (config == "detail" || data.is_virtual) {
|
||||||
|
|||||||
@@ -413,7 +413,7 @@
|
|||||||
this.isLoad = false;
|
this.isLoad = false;
|
||||||
if (callback) callback();
|
if (callback) callback();
|
||||||
} else {
|
} else {
|
||||||
this.$util.redirectTo('/pages/index/index');
|
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -441,7 +441,7 @@
|
|||||||
this.skuId = res.data[0].sku_id;
|
this.skuId = res.data[0].sku_id;
|
||||||
this.goodsSkuInfo = obj;
|
this.goodsSkuInfo = obj;
|
||||||
} else {
|
} else {
|
||||||
this.$util.redirectTo('/pages/index/index');
|
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -467,7 +467,7 @@
|
|||||||
this.skuId = res.data[0].sku_id;
|
this.skuId = res.data[0].sku_id;
|
||||||
this.goodsSkuInfo = obj;
|
this.goodsSkuInfo = obj;
|
||||||
} else {
|
} else {
|
||||||
this.$util.redirectTo('/pages/index/index');
|
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -500,7 +500,7 @@
|
|||||||
this.skuId = res.data[0].sku_id;
|
this.skuId = res.data[0].sku_id;
|
||||||
this.goodsSkuInfo = obj;
|
this.goodsSkuInfo = obj;
|
||||||
} else {
|
} else {
|
||||||
this.$util.redirectTo('/pages/index/index');
|
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -527,7 +527,7 @@
|
|||||||
this.skuId = res.data[0].sku_id;
|
this.skuId = res.data[0].sku_id;
|
||||||
this.goodsSkuInfo = obj;
|
this.goodsSkuInfo = obj;
|
||||||
} else {
|
} else {
|
||||||
this.$util.redirectTo('/pages/index/index');
|
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -553,7 +553,7 @@
|
|||||||
this.skuId = res.data[0].sku_id;
|
this.skuId = res.data[0].sku_id;
|
||||||
this.goodsSkuInfo = obj;
|
this.goodsSkuInfo = obj;
|
||||||
} else {
|
} else {
|
||||||
this.$util.redirectTo('/pages/index/index');
|
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -579,7 +579,7 @@
|
|||||||
this.skuId = res.data[0].sku_id;
|
this.skuId = res.data[0].sku_id;
|
||||||
this.goodsSkuInfo = obj;
|
this.goodsSkuInfo = obj;
|
||||||
} else {
|
} else {
|
||||||
this.$util.redirectTo('/pages/index/index');
|
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -606,7 +606,7 @@
|
|||||||
this.skuId = res.data[0].sku_id;
|
this.skuId = res.data[0].sku_id;
|
||||||
this.goodsSkuInfo = obj;
|
this.goodsSkuInfo = obj;
|
||||||
} else {
|
} else {
|
||||||
this.$util.redirectTo('/pages/index/index');
|
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -630,7 +630,7 @@
|
|||||||
this.skuId = res.data[0].sku_id;
|
this.skuId = res.data[0].sku_id;
|
||||||
this.goodsSkuInfo = obj;
|
this.goodsSkuInfo = obj;
|
||||||
} else {
|
} else {
|
||||||
this.$util.redirectTo('/pages/index/index');
|
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -42,26 +42,22 @@
|
|||||||
|
|
||||||
<!-- 协议勾选 -->
|
<!-- 协议勾选 -->
|
||||||
<view style="display: flex;">
|
<view style="display: flex;">
|
||||||
<view
|
<view class="iconfont"
|
||||||
class="iconfont"
|
|
||||||
:class="isAgree ? 'icon-fuxuankuang1 color-base-text' : 'icon-fuxuankuang2'"
|
:class="isAgree ? 'icon-fuxuankuang1 color-base-text' : 'icon-fuxuankuang2'"
|
||||||
@click="isAgree = !isAgree"
|
@click="isAgree = !isAgree"></view>
|
||||||
></view>
|
|
||||||
<view style="text-align: left; margin-left: 10rpx; padding-top: 2rpx; font-size: 28rpx;">
|
<view style="text-align: left; margin-left: 10rpx; padding-top: 2rpx; font-size: 28rpx;">
|
||||||
我同意
|
我同意
|
||||||
<text style="color: #4395ff;" @click="tourl('/pages_tool/agreement/contenr?type=0')">《隐私条款》</text>
|
<text style="color: #4395ff;"
|
||||||
<text style="color: #4395ff;" @click="tourl('/pages_tool/agreement/contenr?type=1')">《用户服务协议》</text>
|
@click="tourl('/pages_tool/agreement/contenr?type=0')">《隐私条款》</text>
|
||||||
|
<text style="color: #4395ff;"
|
||||||
|
@click="tourl('/pages_tool/agreement/contenr?type=1')">《用户服务协议》</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 手机号授权按钮 -->
|
<!-- 手机号授权按钮 -->
|
||||||
<block v-if="isAgree">
|
<block v-if="isAgree">
|
||||||
<button
|
<button class="auth-login save-btn border-0" style="border: none;" open-type="getPhoneNumber"
|
||||||
class="auth-login save-btn border-0"
|
@getphonenumber="getPhoneNumber">
|
||||||
style="border: none;"
|
|
||||||
open-type="getPhoneNumber"
|
|
||||||
@getphonenumber="getPhoneNumber"
|
|
||||||
>
|
|
||||||
<text style="color: #fff; font-size: 30rpx;">同意隐私协议并授权手机号</text>
|
<text style="color: #fff; font-size: 30rpx;">同意隐私协议并授权手机号</text>
|
||||||
</button>
|
</button>
|
||||||
</block>
|
</block>
|
||||||
@@ -71,7 +67,8 @@
|
|||||||
</button>
|
</button>
|
||||||
</block>
|
</block>
|
||||||
|
|
||||||
<view @click="closeLogin" style="font-size: 30rpx; color: #888; text-align: center; padding-top: 10rpx;">
|
<view @click="closeLogin"
|
||||||
|
style="font-size: 30rpx; color: #888; text-align: center; padding-top: 10rpx;">
|
||||||
暂不登录
|
暂不登录
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -254,7 +251,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 打开弹窗
|
// 打开弹窗
|
||||||
open(url) {
|
open(url = '', mode = 'navigateTo') {
|
||||||
if (url) this.url = url;
|
if (url) this.url = url;
|
||||||
// #ifdef MP
|
// #ifdef MP
|
||||||
this.getCode(authData => {
|
this.getCode(authData => {
|
||||||
@@ -266,28 +263,33 @@ export default {
|
|||||||
if (this.$util.isWeChatMiniProgram()) {
|
if (this.$util.isWeChatMiniProgram()) {
|
||||||
// 微信小程序环境,使用微信授权登录
|
// 微信小程序环境,使用微信授权登录
|
||||||
let authData = uni.getStorageSync('authInfo');
|
let authData = uni.getStorageSync('authInfo');
|
||||||
if (authData) this.authLogin(authData);
|
if (authData) {
|
||||||
else this.getCode();
|
this.authLogin(authData);
|
||||||
|
} else {
|
||||||
|
this.getCode(authData => {
|
||||||
|
this.authLogin(authData, 'authOnlyLogin');
|
||||||
|
});
|
||||||
|
}
|
||||||
} else if (this.$util.isWeiXin()) {
|
} else if (this.$util.isWeiXin()) {
|
||||||
// 微信浏览器环境,显示登录页面
|
// 微信浏览器环境,显示登录页面
|
||||||
this.toLogin();
|
this.toLogin(mode);
|
||||||
} else {
|
} else {
|
||||||
// 其他浏览器环境,显示登录页面
|
// 其他浏览器环境,显示登录页面
|
||||||
this.toLogin();
|
this.toLogin(mode);
|
||||||
}
|
}
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifndef MP || H5
|
// #ifndef MP || H5
|
||||||
this.toLogin();
|
this.toLogin(mode);
|
||||||
// #endif
|
// #endif
|
||||||
},
|
},
|
||||||
|
|
||||||
// 跳转去登录页
|
// 跳转去登录页
|
||||||
toLogin() {
|
toLogin(mode = 'navigateTo') {
|
||||||
if (this.url) this.$util.redirectTo('/pages_tool/login/login', {
|
if (this.url) this.$util.redirectTo(this.$util.LOGIN_PAGE_URL, {
|
||||||
back: encodeURIComponent(this.url)
|
back: encodeURIComponent(this.url)
|
||||||
});
|
}, mode);
|
||||||
else this.$util.redirectTo('/pages_tool/login/login');
|
else this.$util.redirectTo(this.$util.LOGIN_PAGE_URL, {}, mode);
|
||||||
},
|
},
|
||||||
|
|
||||||
// 取消完善信息
|
// 取消完善信息
|
||||||
@@ -535,7 +537,8 @@ export default {
|
|||||||
getPhoneNumber(e) {
|
getPhoneNumber(e) {
|
||||||
if (e.detail.errMsg === 'getPhoneNumber:ok') {
|
if (e.detail.errMsg === 'getPhoneNumber:ok') {
|
||||||
const authInfo = uni.getStorageSync('authInfo');
|
const authInfo = uni.getStorageSync('authInfo');
|
||||||
if (authInfo) { Object.assign(this.authMobileData, authInfo, e.detail);
|
if (authInfo) {
|
||||||
|
Object.assign(this.authMobileData, authInfo, e.detail);
|
||||||
}
|
}
|
||||||
if (uni.getStorageSync('source_member')) {
|
if (uni.getStorageSync('source_member')) {
|
||||||
this.authMobileData.source_member = uni.getStorageSync('source_member');
|
this.authMobileData.source_member = uni.getStorageSync('source_member');
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.mp-html{
|
.mp-html{
|
||||||
/deep/ img{
|
::v-deep img{
|
||||||
width:100% !important;
|
width:100% !important;
|
||||||
display:block;
|
display:block;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ export default {
|
|||||||
title: '未获取到支付信息!'
|
title: '未获取到支付信息!'
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$util.redirectTo('/pages/index/index');
|
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||||||
}, 1500);
|
}, 1500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
145
components/ns-video-player-popup/ns-video-player-popup.vue
Normal file
145
components/ns-video-player-popup/ns-video-player-popup.vue
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<template>
|
||||||
|
<view class="video-player-popup">
|
||||||
|
<uni-popup ref="videoPopup" type="center" :mask-click="true" @change="onVideoPopupChange">
|
||||||
|
<view class="video-popup-content">
|
||||||
|
<view class="video-popup-header">
|
||||||
|
<text class="video-popup-title">{{ videoTitle }}</text>
|
||||||
|
<text class="iconfont icon-close" @click="close"></text>
|
||||||
|
</view>
|
||||||
|
<view class="video-popup-body">
|
||||||
|
<video
|
||||||
|
v-if="videoUrl"
|
||||||
|
:src="videoUrl"
|
||||||
|
controls
|
||||||
|
autoplay
|
||||||
|
class="video-player"
|
||||||
|
></video>
|
||||||
|
<view v-else class="video-error">视频地址不存在</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'ns-video-player-popup',
|
||||||
|
props: {
|
||||||
|
/**
|
||||||
|
* 当前播放的视频信息
|
||||||
|
* @type {Object}
|
||||||
|
* @property {string} title - 视频标题
|
||||||
|
* @property {string} videoUrl - 视频地址
|
||||||
|
*/
|
||||||
|
currentVideo: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
videoUrl() {
|
||||||
|
return this.currentVideo?.videoUrl || '';
|
||||||
|
},
|
||||||
|
|
||||||
|
videoTitle() {
|
||||||
|
return this.currentVideo?.title || '';
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 视频弹窗是否显示
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
isVisible() {
|
||||||
|
return this.$refs.videoPopup?.visible || false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 打开视频弹窗
|
||||||
|
*/
|
||||||
|
open() {
|
||||||
|
if (this.$refs.videoPopup) {
|
||||||
|
this.$refs.videoPopup.open();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭视频弹窗
|
||||||
|
*/
|
||||||
|
close() {
|
||||||
|
if (this.$refs.videoPopup) {
|
||||||
|
this.$refs.videoPopup.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频弹窗状态变化
|
||||||
|
* @param {Object} e - 弹窗状态变化事件
|
||||||
|
*/
|
||||||
|
onVideoPopupChange(e) {
|
||||||
|
this.$emit('popup-change', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
/* 视频播放弹窗样式 */
|
||||||
|
.video-player-popup {
|
||||||
|
.video-popup-content {
|
||||||
|
width: 90%;
|
||||||
|
max-width: 600rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-popup-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
background-color: #fafafa;
|
||||||
|
|
||||||
|
.video-popup-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-close {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #999;
|
||||||
|
padding: 0 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-popup-body {
|
||||||
|
padding: 20rpx;
|
||||||
|
|
||||||
|
.video-player {
|
||||||
|
width: 100%;
|
||||||
|
height: 400rpx;
|
||||||
|
background-color: #000;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-error {
|
||||||
|
width: 100%;
|
||||||
|
height: 400rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,237 +1,426 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- #ifdef MP -->
|
<view>
|
||||||
<view v-if="showPop">
|
<view class="privacy-mask" v-if="showPop">
|
||||||
<view class="privacy-mask">
|
|
||||||
<view class="privacy-wrap">
|
<view class="privacy-wrap">
|
||||||
<view class="privacy-title">用户隐私保护提示</view>
|
<view class="privacy-title">用户隐私保护提示</view>
|
||||||
<view class="privacy-desc">
|
<view class="privacy-desc">
|
||||||
感谢您使用本小程序,在使用前您应当阅读并同意
|
感谢您使用{{ appName }},在使用前您应当阅读并同意
|
||||||
<text class="privacy-link" @tap="openPrivacyContract">{{privacyContractName}}</text>,
|
<text class="privacy-link" @tap="openPrivacyContract">{{ privacyContractName }}</text>
|
||||||
当点击同意并继续时,即表示您已理解并同意该条款内容,该条款将对您产生法律约束力;如您不同意,将无法继续使用小程序相关功能。
|
<!-- #ifdef QUICKAPP-WEBVIEW || H5 -->
|
||||||
|
<text class="privacy-link" @tap="openPrivacyService">{{ privacyServiceName }}</text>
|
||||||
|
<!-- #endif -->
|
||||||
|
,当点击同意并继续时,即表示您已理解并同意该条款内容,该条款将对您产生法律约束力;如您不同意,将无法继续使用{{ appName }}相关功能。
|
||||||
</view>
|
</view>
|
||||||
<view class="privacy-button-flex">
|
<view class="privacy-button-flex">
|
||||||
<button class="privacy-button-btn bg-disagree" @tap="handleDisagree">不同意</button>
|
<button class="privacy-button-btn bg-disagree" @tap="handleDisagree">不同意</button>
|
||||||
<button id="agree-btn" class="privacy-button-btn bg-agree" open-type="agreePrivacyAuthorization" @agreeprivacyauthorization="handleAgree">同意并继续</button>
|
<!-- #ifdef QUICKAPP-WEBVIEW || MP-WEIXIN -->
|
||||||
|
<button id="agree-btn" class="privacy-button-btn bg-agree" open-type="agreePrivacyAuthorization"
|
||||||
|
@agreeprivacyauthorization="handleAgree">同意并继续</button>
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifdef WEB || H5 -->
|
||||||
|
<button id="agree-btn" class="privacy-button-btn bg-agree" @tap="handleAgree" @click="handleAgree">同意并继续</button>
|
||||||
|
<!-- #endif -->
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- #endif -->
|
|
||||||
<!-- #ifdef WEB || H5 -->
|
|
||||||
<view v-if="showPop"></view>
|
|
||||||
<!-- #endif -->
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
// #ifdef QUICKAPP-WEBVIEW
|
||||||
data() {
|
import has from '@system.has'
|
||||||
return {
|
// #endif
|
||||||
agree: false,
|
|
||||||
showPop: false,
|
const closeOtherPagePopUpHooks = new Set();
|
||||||
privacyAuthorization: null,
|
const privacyContractPage = '/pages_tool/agreement/contenr?type=0';
|
||||||
privacyResolves: new Set(),
|
const privacyServicePage = '/pages_tool/agreement/contenr?type=1';
|
||||||
closeOtherPagePopUpHooks: new Set(),
|
|
||||||
privacyContractName: '用户隐私保护指引'
|
export default {
|
||||||
}
|
name: 'PrivacyPopup',
|
||||||
},
|
data() {
|
||||||
mounted() {
|
return {
|
||||||
this.init()
|
agree: false,
|
||||||
|
showPop: false,
|
||||||
|
privacyAuthorization: null,
|
||||||
|
privacyResolves: new Set(),
|
||||||
|
privacyContractName: '用户隐私保护指引',
|
||||||
|
appName: '本小程序',
|
||||||
|
// #ifdef WEB || H5
|
||||||
|
appName: '本应用',
|
||||||
|
privacyContractName: '《隐私条款》',
|
||||||
|
privacyServiceName: '《用户服务协议》',
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// 检查配置,决定是否显示隐私协议
|
||||||
|
const shopInfo = uni.getStorageSync('shopInfo');
|
||||||
|
if (shopInfo?.ischina == 1 && shopInfo?.index_login_auth == 1) {
|
||||||
|
this.listenPrivacySettingChange()
|
||||||
this.curPageShow()
|
this.curPageShow()
|
||||||
},
|
} else {
|
||||||
created() {
|
// 如果不需要显示隐私协议,直接触发同意事件
|
||||||
let that = this
|
this.$emit('agree');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
try {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
//查询微信侧记录的用户是否有待同意的隐私政策信息
|
//查询微信侧记录的用户是否有待同意的隐私政策信息
|
||||||
try {
|
wx.getPrivacySetting({
|
||||||
wx.getPrivacySetting({
|
success: (res) => {
|
||||||
success(res) {
|
// console.log('隐私政策信息', res);
|
||||||
// console.log('隐私政策信息', res);
|
// console.log(res.privacyContractName);
|
||||||
// console.log(res.privacyContractName);
|
this.privacyContractName = res.privacyContractName
|
||||||
that.privacyContractName = res.privacyContractName
|
}
|
||||||
}
|
});
|
||||||
});
|
// #endif
|
||||||
} catch (e) {
|
|
||||||
// console.log("=========低版本基础库==========")
|
// #ifdef QUICKAPP-WEBVIEW || H5
|
||||||
}
|
if (this.$util.isQuickApp()) {
|
||||||
},
|
if (typeof has != 'undefined' && has?.getPrivacySetting) {
|
||||||
methods: {
|
has.getPrivacySetting({
|
||||||
// 监听何时需要提示用户阅读隐私政策
|
success: (res) => {
|
||||||
init() {
|
if (res.privacyContractName) {
|
||||||
let that = this;
|
this.privacyContractName = res.privacyContractName
|
||||||
if (wx.onNeedPrivacyAuthorization) {
|
}
|
||||||
wx.onNeedPrivacyAuthorization((resolve) => {
|
},
|
||||||
if (typeof that.privacyAuthorization === 'function') {
|
|
||||||
that.privacyAuthorization(resolve)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
proactive() {
|
// #endif
|
||||||
let that = this
|
} catch (e) {
|
||||||
if (wx.getPrivacySetting) {
|
console.error("=========低版本基础库==========", e)
|
||||||
wx.getPrivacySetting({
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getPrivacySettingByQuickApp() {
|
||||||
|
// #ifdef QUICKAPP-WEBVIEW || H5 || WEB
|
||||||
|
if (this.$util.isQuickApp()) {
|
||||||
|
if (typeof has != 'undefined' && has?.getPrivacySetting) {
|
||||||
|
has.getPrivacySetting({
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
// console.log(res)
|
// console.log(res)
|
||||||
if (res.needAuthorization) {
|
if (res.needAuthorization) {
|
||||||
that.popUp()
|
this.popUp()
|
||||||
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||||
this.closeOtherPagePopUp(this.disPopUp)
|
this.closeOtherPagePopUp(this.disPopUp)
|
||||||
} else {
|
} else {
|
||||||
this.$emit('agree')
|
this.$emit('agree')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
this.$emit('agree')
|
// 属于嵌入到快应用Webview组件中的情况,按照H5的方式来处理,判断是否已经授权
|
||||||
|
if (uni.getStorageSync('privacyAgreed')) {
|
||||||
|
this.$emit('agree')
|
||||||
|
} else {
|
||||||
|
this.popUp()
|
||||||
|
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||||
|
this.closeOtherPagePopUp(this.disPopUp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
//初始化监听程序
|
// #endif
|
||||||
curPageShow() {
|
},
|
||||||
this.privacyAuthorization = resolve => {
|
|
||||||
this.privacyResolves.add(resolve)
|
listenPrivacySettingChange() {
|
||||||
//打开弹窗
|
// #ifdef MP-WEIXIN
|
||||||
|
// 监听微信侧隐私政策授权变化事件
|
||||||
|
if (wx.onNeedPrivacyAuthorization) {
|
||||||
|
wx.onNeedPrivacyAuthorization((resolve) => {
|
||||||
|
if (typeof this.privacyAuthorization === 'function') {
|
||||||
|
this.privacyAuthorization(resolve)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef QUICKAPP-WEBVIEW || H5 || WEB
|
||||||
|
if (this.$util.isQuickApp()) {
|
||||||
|
// 监听快速应用侧隐私政策授权变化事件
|
||||||
|
if (typeof has != 'undefined' && has?.onPrivacySettingChange) {
|
||||||
|
has.onPrivacySettingChange((res) => {
|
||||||
|
// console.log(res)
|
||||||
|
if (res.needAuthorization) {
|
||||||
|
this.popUp()
|
||||||
|
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||||
|
this.closeOtherPagePopUp(this.disPopUp)
|
||||||
|
} else {
|
||||||
|
this.$emit('agree')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// 主动查询用户隐私政策授权状态,针对快速应用
|
||||||
|
this.getPrivacySettingByQuickApp();
|
||||||
|
},
|
||||||
|
// 主动查询用户隐私政策授权状态
|
||||||
|
proactive() {
|
||||||
|
// 检查配置,决定是否显示隐私协议
|
||||||
|
const shopInfo = uni.getStorageSync('shopInfo');
|
||||||
|
if (!(shopInfo?.ischina == 1 && shopInfo?.index_login_auth == 1)) {
|
||||||
|
// 如果不需要显示隐私协议,直接触发同意事件
|
||||||
|
this.$emit('agree');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
if (wx.getPrivacySetting) {
|
||||||
|
wx.getPrivacySetting({
|
||||||
|
success: (res) => {
|
||||||
|
// console.log(res)
|
||||||
|
if (res.needAuthorization) {
|
||||||
|
// 检查本地存储中是否已经同意过隐私政策
|
||||||
|
const privacyAgreed = uni.getStorageSync('privacyAgreed');
|
||||||
|
if (!privacyAgreed) {
|
||||||
|
this.popUp()
|
||||||
|
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||||
|
this.closeOtherPagePopUp(this.disPopUp)
|
||||||
|
} else {
|
||||||
|
this.$emit('agree')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$emit('agree')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 检查本地存储中是否已经同意过隐私政策
|
||||||
|
const privacyAgreed = uni.getStorageSync('privacyAgreed');
|
||||||
|
if (privacyAgreed) {
|
||||||
|
this.$emit('agree');
|
||||||
|
} else {
|
||||||
this.popUp()
|
this.popUp()
|
||||||
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||||
this.closeOtherPagePopUp(this.disPopUp)
|
this.closeOtherPagePopUp(this.disPopUp)
|
||||||
}
|
}
|
||||||
this.closeOtherPagePopUpHooks.add(this.disPopUp)
|
}
|
||||||
},
|
// #endif
|
||||||
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
|
||||||
closeOtherPagePopUp(closePopUp) {
|
// #ifdef QUICKAPP-WEBVIEW
|
||||||
this.closeOtherPagePopUpHooks.forEach(hook => {
|
this.getPrivacySettingByQuickApp();
|
||||||
if (closePopUp !== hook) {
|
// #endif
|
||||||
hook()
|
|
||||||
}
|
// #ifdef H5 || WEB
|
||||||
})
|
// 检查本地存储中是否已经同意过隐私政策
|
||||||
},
|
const privacyAgreed = uni.getStorageSync('privacyAgreed');
|
||||||
//打开隐私协议
|
if (privacyAgreed) {
|
||||||
openPrivacyContract() {
|
this.$emit('agree');
|
||||||
wx.openPrivacyContract({
|
} else {
|
||||||
success(res) {
|
this.popUp()
|
||||||
// console.log('打开隐私协议', res);
|
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||||
},
|
this.closeOtherPagePopUp(this.disPopUp)
|
||||||
fail(err) {
|
}
|
||||||
// console.error('打开隐私协议失败', err)
|
// #endif
|
||||||
}
|
},
|
||||||
});
|
//初始化监听程序
|
||||||
},
|
curPageShow() {
|
||||||
// 不同意
|
closeOtherPagePopUpHooks.add(this.disPopUp)
|
||||||
handleDisagree() {
|
this.privacyAuthorization = resolve => {
|
||||||
this.privacyResolves.forEach(resolve => {
|
this.privacyResolves.add(resolve)
|
||||||
resolve({
|
//打开弹窗
|
||||||
event: 'disagree',
|
this.popUp()
|
||||||
})
|
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||||
})
|
this.closeOtherPagePopUp(this.disPopUp)
|
||||||
this.privacyResolves.clear()
|
}
|
||||||
//关闭弹窗
|
},
|
||||||
this.disPopUp()
|
// 额外逻辑:当前页面的隐私弹窗弹起的时候,关掉其他页面的隐私弹窗
|
||||||
//退出小程序
|
closeOtherPagePopUp(closePopUp) {
|
||||||
uni.showModal({
|
closeOtherPagePopUpHooks.forEach(hook => {
|
||||||
content: '未同意隐私协议,无法使用相关功能',
|
if (closePopUp !== hook) {
|
||||||
success: () => {
|
hook()
|
||||||
this.$emit('disagree')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 同意并继续
|
|
||||||
handleAgree() {
|
|
||||||
this.privacyResolves.forEach(resolve => {
|
|
||||||
resolve({
|
|
||||||
event: 'agree',
|
|
||||||
buttonId: 'agree-btn'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
this.privacyResolves.clear()
|
|
||||||
//关闭弹窗
|
|
||||||
this.disPopUp()
|
|
||||||
this.$emit('agree')
|
|
||||||
},
|
|
||||||
//打开弹窗
|
|
||||||
popUp() {
|
|
||||||
if (this.showPop === false) {
|
|
||||||
this.showPop = true
|
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
|
},
|
||||||
|
//打开隐私协议
|
||||||
|
openPrivacyContract() {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
wx.openPrivacyContract({
|
||||||
|
success(res) {
|
||||||
|
// console.log('打开隐私协议', res);
|
||||||
|
},
|
||||||
|
fail(err) {
|
||||||
|
// console.error('打开隐私协议失败', err)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef QUICKAPP-WEBVIEW || H5 || WEB
|
||||||
|
if (this.$util.isQuickApp()) {
|
||||||
|
if (typeof has != 'undefined' && has?.openPrivacySetting) {
|
||||||
|
has.openPrivacySetting({
|
||||||
|
success: (res) => {
|
||||||
|
// console.log('打开隐私协议', res);
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
// console.error('打开隐私协议失败', err)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 属于嵌入到快应用Webview组件中的情况,按照H5的方式来处理
|
||||||
|
this.$util.redirectTo(privacyContractPage);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// H5 环境下的处理逻辑
|
||||||
|
this.$util.redirectTo(privacyContractPage);
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
//打开用户服务协议
|
||||||
|
openPrivacyService() {
|
||||||
|
// #ifdef QUICKAPP-WEBVIEW || H5 || WEB
|
||||||
|
if (this.$util.isQuickApp()) {
|
||||||
|
if (typeof has != 'undefined' && has?.openPrivacySetting) {
|
||||||
|
has.openPrivacySetting({
|
||||||
|
success: (res) => {
|
||||||
|
// console.log('打开用户服务协议', res);
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
// console.error('打开用户服务协议失败', err)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 属于嵌入到快应用Webview组件中的情况,按照H5的方式来处理
|
||||||
|
this.$util.redirectTo(privacyServicePage);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// H5 环境下的处理逻辑
|
||||||
|
this.$util.redirectTo(privacyServicePage);
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
// 不同意
|
||||||
|
handleDisagree() {
|
||||||
|
this.privacyResolves.forEach(resolve => {
|
||||||
|
resolve({
|
||||||
|
event: 'disagree',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.privacyResolves.clear()
|
||||||
//关闭弹窗
|
//关闭弹窗
|
||||||
disPopUp() {
|
this.disPopUp()
|
||||||
if (this.showPop === true) {
|
// 保存用户授权状态到本地存储,无论同意还是不同意,都不再重复弹出
|
||||||
this.showPop = false
|
uni.setStorageSync('privacyAgreed', true);
|
||||||
|
//退出小程序
|
||||||
|
uni.showModal({
|
||||||
|
content: '未同意隐私协议,无法使用相关功能',
|
||||||
|
success: () => {
|
||||||
|
this.$emit('disagree')
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
}
|
},
|
||||||
}
|
// 同意并继续
|
||||||
|
handleAgree() {
|
||||||
|
this.privacyResolves.forEach(resolve => {
|
||||||
|
resolve({
|
||||||
|
event: 'agree',
|
||||||
|
buttonId: 'agree-btn'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.privacyResolves.clear()
|
||||||
|
//关闭弹窗
|
||||||
|
this.disPopUp()
|
||||||
|
|
||||||
|
// 保存用户授权状态到本地存储,用于所有环境
|
||||||
|
uni.setStorageSync('privacyAgreed', true);
|
||||||
|
this.$emit('agree')
|
||||||
|
},
|
||||||
|
//打开弹窗
|
||||||
|
popUp() {
|
||||||
|
this.showPop = true;
|
||||||
|
},
|
||||||
|
//关闭弹窗
|
||||||
|
disPopUp() {
|
||||||
|
this.showPop = false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
// 清理事件监听器和集合
|
||||||
|
this.privacyResolves.clear()
|
||||||
|
closeOtherPagePopUpHooks.delete(this.disPopUp)
|
||||||
|
// 注意:这里需要根据实际情况清理微信和快速应用的事件监听器
|
||||||
|
// 由于微信的 wx.onNeedPrivacyAuthorization 没有对应的 off 方法,这里可能需要其他方式处理
|
||||||
|
},
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.privacy-mask {
|
.privacy-mask {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 5000;
|
z-index: 5000;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: rgba(0, 0, 0, 0.2);
|
background: rgba(0, 0, 0, 0.2);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.privacy-wrap {
|
.privacy-wrap {
|
||||||
width: 632rpx;
|
width: 632rpx;
|
||||||
padding: 48rpx 30rpx;
|
padding: 48rpx 30rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.privacy-title {
|
.privacy-title {
|
||||||
padding: 0rpx 30rpx 40rpx 30rpx;
|
padding: 0rpx 30rpx 40rpx 30rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 36rpx;
|
font-size: 36rpx;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.privacy-desc {
|
.privacy-desc {
|
||||||
font-size: 30rpx;
|
font-size: 30rpx;
|
||||||
color: #555;
|
color: #555;
|
||||||
line-height: 2;
|
line-height: 2;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 0 40rpx;
|
padding: 0 40rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.privacy-link {
|
.privacy-link {
|
||||||
color: #2f80ed;
|
color: #2f80ed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.privacy-button-flex {
|
.privacy-button-flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 20rpx 40rpx;
|
padding: 20rpx 40rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.privacy-button-btn {
|
.privacy-button-btn {
|
||||||
color: #FFF;
|
color: #FFF;
|
||||||
font-size: 30rpx;
|
font-size: 30rpx;
|
||||||
font-weight: 500;
|
// #ifdef QUICKAPP-WEBVIEW || H5 || WEB
|
||||||
line-height: 100rpx;
|
font-size: 28rpx;
|
||||||
text-align: center;
|
// #endif
|
||||||
height: 100rpx;
|
font-weight: 500;
|
||||||
border-radius: 20rpx;
|
line-height: 100rpx;
|
||||||
border: none;
|
text-align: center;
|
||||||
background: #07c160;
|
height: 100rpx;
|
||||||
flex: 1;
|
border-radius: 20rpx;
|
||||||
margin-right: 30rpx;
|
border: none;
|
||||||
justify-content: center;
|
background: #07c160;
|
||||||
}
|
flex: 1;
|
||||||
|
margin-right: 30rpx;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.privacy-button-btn::after {
|
.privacy-button-btn::after {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-disagree {
|
.bg-disagree {
|
||||||
color: #07c160;
|
color: #07c160;
|
||||||
background: #f2f2f2;
|
background: #f2f2f2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-agree {
|
.bg-agree {
|
||||||
margin-right: 0rpx;
|
margin-right: 0rpx;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (this.back) this.$util.redirectTo(decodeURIComponent(this.back), {}, 'redirectTo');
|
if (this.back) this.$util.redirectTo(decodeURIComponent(this.back), {}, 'redirectTo');
|
||||||
else this.$util.redirectTo('/pages/index/index');
|
else this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.register-box /deep/ .uni-scroll-view {
|
.register-box ::v-deep .uni-scroll-view {
|
||||||
background: unset !important;
|
background: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,11 +155,11 @@
|
|||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.register-box /deep/.uni-popup__wrapper-box {
|
.register-box ::v-deep .uni-popup__wrapper-box {
|
||||||
overflow: unset !important;
|
overflow: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reward-popup /deep/.uni-popup__wrapper {
|
.reward-popup ::v-deep .uni-popup__wrapper {
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default {
|
|||||||
<style>
|
<style>
|
||||||
/* 回到顶部的按钮 */
|
/* 回到顶部的按钮 */
|
||||||
.mescroll-totop {
|
.mescroll-totop {
|
||||||
z-index: 99;
|
z-index: 2147483647;
|
||||||
position: fixed !important; /* 加上important避免编译到H5,在多mescroll中定位失效 */
|
position: fixed !important; /* 加上important避免编译到H5,在多mescroll中定位失效 */
|
||||||
right: 46rpx !important;
|
right: 46rpx !important;
|
||||||
bottom: 272rpx !important;
|
bottom: 272rpx !important;
|
||||||
|
|||||||
568
docs/WECHAT_CHANNEL_INTEGRATION.md
Normal file
568
docs/WECHAT_CHANNEL_INTEGRATION.md
Normal file
@@ -0,0 +1,568 @@
|
|||||||
|
# 微信视频号集成指南
|
||||||
|
|
||||||
|
## 1. 概述
|
||||||
|
|
||||||
|
本文档提供了在UniApp项目中集成微信视频号的详细指导,包括组件实现、配置修改和使用方法。
|
||||||
|
|
||||||
|
## 2. 准备工作
|
||||||
|
|
||||||
|
### 2.1 微信小程序配置
|
||||||
|
|
||||||
|
在 `manifest.json` 文件中,需要确保微信小程序配置正确:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"mp-weixin": {
|
||||||
|
"appid": "你的小程序appid",
|
||||||
|
"setting": {
|
||||||
|
"urlCheck": false,
|
||||||
|
"postcss": false,
|
||||||
|
"es6": true,
|
||||||
|
"minified": true
|
||||||
|
},
|
||||||
|
"usingComponents": true,
|
||||||
|
"permission": {
|
||||||
|
"scope.userLocation": {
|
||||||
|
"desc": "为了更好地为您提供服务"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"requiredPrivateInfos": ["chooseLocation", "getLocation", "chooseAddress"],
|
||||||
|
"__usePrivacyCheck__": true,
|
||||||
|
"optimization": {
|
||||||
|
"subPackages": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 微信视频号API权限与关联
|
||||||
|
|
||||||
|
#### 2.2.1 权限说明
|
||||||
|
|
||||||
|
`wx.openChannelsActivity` API 是小程序的基础能力,不需要单独开通权限,但需要满足以下条件:
|
||||||
|
|
||||||
|
- **跳转打开视频号视频**:无主体限制,基础库版本 2.19.2 及以上
|
||||||
|
- **内嵌视频号视频**:
|
||||||
|
- 从基础库版本 2.25.1 至 2.31.1,小程序需与视频号视频相同主体或关联主体
|
||||||
|
- 从基础库版本 2.31.1 开始,非个人主体小程序可内嵌非同主体/关联主体视频号视频
|
||||||
|
|
||||||
|
#### 2.2.2 小程序与视频号关联(可选)
|
||||||
|
|
||||||
|
虽然跳转打开视频号视频无主体限制,但如果需要内嵌非同主体视频号视频(基础库 2.31.1 以下),则需要关联小程序与视频号:
|
||||||
|
|
||||||
|
1. **小程序管理员操作**
|
||||||
|
- 登录微信小程序后台
|
||||||
|
- 左侧菜单:设置 → 基本设置
|
||||||
|
- 向下滚动找到「关联设置」部分
|
||||||
|
- 点击「关联视频号」按钮
|
||||||
|
- 输入视频号名称或视频号ID
|
||||||
|
- 点击「搜索」找到对应的视频号
|
||||||
|
- 点击「发送关联邀请」
|
||||||
|
|
||||||
|
2. **视频号管理员操作**
|
||||||
|
- 视频号管理员需要在24小时内确认关联邀请
|
||||||
|
- 视频号管理员登录 [视频号助手](https://channels.weixin.qq.com/)
|
||||||
|
- 进入「设置」→「关联设置」→「小程序关联」
|
||||||
|
- 找到待确认的关联邀请
|
||||||
|
- 点击「同意」完成关联
|
||||||
|
|
||||||
|
3. **关联失败处理**
|
||||||
|
- 检查视频号是否已完成实名认证
|
||||||
|
- 检查小程序是否已发布上线
|
||||||
|
- 检查视频号名称或ID是否输入正确
|
||||||
|
- 如超过24小时未确认,需要重新发送关联邀请
|
||||||
|
|
||||||
|
#### 2.2.3 关联条件
|
||||||
|
|
||||||
|
- **小程序要求**:必须已经发布上线,未被处罚或限制
|
||||||
|
- **视频号要求**:必须已经完成实名认证,状态正常
|
||||||
|
- **数量限制**:同一个小程序最多可以关联50个视频号,同一个视频号最多可以关联50个小程序
|
||||||
|
- **权限要求**:关联操作需要小程序管理员和视频号管理员共同确认
|
||||||
|
- **主体要求**:小程序和视频号的主体可以不同,但需要双方管理员确认
|
||||||
|
|
||||||
|
#### 2.2.4 功能验证
|
||||||
|
|
||||||
|
可以通过以下方式验证视频号功能是否可用:
|
||||||
|
|
||||||
|
1. **在微信开发者工具中验证**
|
||||||
|
- 在微信开发者工具中打开小程序
|
||||||
|
- 在控制台执行以下代码:
|
||||||
|
```javascript
|
||||||
|
console.log('是否支持视频号API:', typeof wx !== 'undefined' && typeof wx.openChannelsActivity === 'function');
|
||||||
|
```
|
||||||
|
- 如果返回 `true`,则表示API可用
|
||||||
|
|
||||||
|
2. **在真机环境中验证**
|
||||||
|
- 使用微信扫码打开小程序
|
||||||
|
- 触发视频号相关功能
|
||||||
|
- 检查是否能正常打开视频号内容
|
||||||
|
|
||||||
|
3. **功能验证失败处理**
|
||||||
|
- 检查微信小程序基础库版本是否满足要求(跳转打开需要 2.19.2+,内嵌打开需要 2.25.1+)
|
||||||
|
- 对于内嵌打开,检查主体是否满足要求或是否已关联
|
||||||
|
- 检查 `feedId` 等参数是否正确
|
||||||
|
|
||||||
|
## 3. 组件使用
|
||||||
|
|
||||||
|
### 3.1 视频号视频卡片组件 (diy-channel-video.vue)
|
||||||
|
|
||||||
|
#### 基本用法
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<diy-channel-video :value="videoData" @video-play="handlerVideoPlay" />
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 属性说明
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
|--------|------|--------|------|
|
||||||
|
| value | Object | 必填 | 视频数据对象 |
|
||||||
|
| listMode | Boolean | false | 是否为列表模式 |
|
||||||
|
| videoHeight | Number | 220 | 视频高度(仅适用于嵌入式播放) |
|
||||||
|
| titleLineClamp | Number | 1 | 标题显示行数 |
|
||||||
|
| showPlayBtn | Boolean | true | 是否显示播放按钮 |
|
||||||
|
| aspectRatio | String | '16:9' | 视频比例,可选值:'16:9', '3:4' |
|
||||||
|
| coverStyle | Object | {} | 视频封面图样式 |
|
||||||
|
| playBtnStyle | Object | {} | 播放按钮样式 |
|
||||||
|
| autoPlay | Boolean | false | 是否自动播放(仅适用于嵌入式播放) |
|
||||||
|
|
||||||
|
#### value对象属性说明
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
|--------|------|--------|------|
|
||||||
|
| feedId | String | "" | 视频 feedId |
|
||||||
|
| finderUserName | String | "" | 视频号用户名 |
|
||||||
|
| feedToken | String | "" | 视频 token |
|
||||||
|
| coverUrl | String | "" | 视频封面图 |
|
||||||
|
| videoTitle | String | "" | 视频标题 |
|
||||||
|
| viewCount | Number | 0 | 观看次数 |
|
||||||
|
| showViewCount | Boolean | false | 是否显示观看次数 |
|
||||||
|
| embedMode | Boolean | false | 是否启用嵌入式播放 |
|
||||||
|
|
||||||
|
#### 事件说明
|
||||||
|
|
||||||
|
| 事件名 | 说明 | 参数 |
|
||||||
|
|--------|------|------|
|
||||||
|
| video-play | 点击视频播放时触发 | 视频数据对象 |
|
||||||
|
|
||||||
|
### 3.2 视频号列表组件 (diy-channel-list.vue)
|
||||||
|
|
||||||
|
#### 基本用法
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<diy-channel-list :value="channelListData" />
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 属性说明
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
|--------|------|--------|------|
|
||||||
|
| value | Object | 必填 | 组件配置数据 |
|
||||||
|
| value.list | Array | [] | 视频号列表数据 |
|
||||||
|
| value.rowCount | Number | 2 | 每行显示的视频号数量 |
|
||||||
|
| value.showStyle | String | "fixed" | 显示样式,可选值:fixed, singleSlide |
|
||||||
|
| value.mode | String | "" | 显示模式 |
|
||||||
|
| value.font | Object | {} | 字体样式配置 |
|
||||||
|
| value.componentBgColor | String | "#fff" | 组件背景颜色 |
|
||||||
|
| value.componentAngle | String | "" | 组件圆角类型,可选值:round |
|
||||||
|
| value.topAroundRadius | Number | 0 | 顶部圆角半径 |
|
||||||
|
| value.bottomAroundRadius | Number | 0 | 底部圆角半径 |
|
||||||
|
| value.ornament | Object | {} | 装饰样式配置 |
|
||||||
|
| value.titleLineClamp | Number | 1 | 标题显示行数 |
|
||||||
|
| value.showPlayBtn | Boolean | true | 是否显示播放按钮 |
|
||||||
|
| value.aspectRatio | String | '16:9' | 视频比例,可选值:'16:9', '3:4' |
|
||||||
|
| value.coverStyle | Object | {} | 视频封面图样式 |
|
||||||
|
| value.playBtnStyle | Object | {} | 播放按钮样式 |
|
||||||
|
| value.imageSize | Number | 0 | 图片尺寸(仅在特定模式下使用) |
|
||||||
|
| value.pageCount | Number | 1 | 每页显示的视频数量 |
|
||||||
|
| value.carousel | Object | {} | 轮播配置 |
|
||||||
|
| value.carousel.type | String | "" | 轮播类型,可选值:default, hide |
|
||||||
|
| value.carousel.autoplay | Boolean | false | 是否自动播放 |
|
||||||
|
| value.carousel.interval | Number | 3000 | 自动播放间隔,单位毫秒 |
|
||||||
|
| value.carousel.duration | Number | 500 | 切换动画时长,单位毫秒 |
|
||||||
|
| value.carousel.circular | Boolean | false | 是否循环播放 |
|
||||||
|
|
||||||
|
#### list数组项说明
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
|--------|------|--------|------|
|
||||||
|
| channelName | String | "" | 视频号名称 |
|
||||||
|
| coverUrl | String | "" | 视频封面URL |
|
||||||
|
| videoTitle | String | "" | 视频标题 |
|
||||||
|
| feedId | String | "" | 视频号内容ID,用于播放视频 |
|
||||||
|
| viewCount | Number | 0 | 视频观看次数 |
|
||||||
|
| embedMode | Boolean | false | 是否启用嵌入式播放 |
|
||||||
|
| showViewCount | Boolean | false | 是否显示观看次数 |
|
||||||
|
| finderUserName | String | "" | 视频号ID,用于嵌入式播放 |
|
||||||
|
| feedToken | String | "" | 视频token,用于嵌入式播放 |
|
||||||
|
|
||||||
|
## 4. 视频播放实现
|
||||||
|
|
||||||
|
小程序提供两种打开视频号视频的方式:跳转打开和内嵌打开。
|
||||||
|
|
||||||
|
### 4.1 跳转打开视频号视频
|
||||||
|
|
||||||
|
组件内部使用微信小程序的 `wx.openChannelsActivity` API 来打开视频号内容:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if (typeof wx !== 'undefined' && wx.openChannelsActivity) {
|
||||||
|
wx.openChannelsActivity({
|
||||||
|
feedId: item.feedId, // 视频号内容ID,必填
|
||||||
|
finderUserName: item.finderUserName, // 视频号ID(可选)
|
||||||
|
success: (res) => {
|
||||||
|
console.log('打开视频号成功', res);
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('打开视频号失败', err);
|
||||||
|
// 常见错误处理
|
||||||
|
if (err.errCode === 40001) {
|
||||||
|
console.error('未授权,请检查小程序是否与视频号关联');
|
||||||
|
} else if (err.errCode === 40002) {
|
||||||
|
console.error('参数错误,请检查feedId是否正确');
|
||||||
|
} else if (err.errCode === 40003) {
|
||||||
|
console.error('视频号内容不存在或已被删除');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('当前环境不支持微信视频号');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 内嵌视频号视频
|
||||||
|
|
||||||
|
从基础库 2.25.1 开始支持,小程序可以通过 `channel-video` 组件在小程序中内嵌视频号视频,且支持无弹窗跳转打开视频号对应视频,使用该组件时需注意:
|
||||||
|
|
||||||
|
- 组件调用无资质要求
|
||||||
|
- 暂不支持纯图片视频号内容
|
||||||
|
- 在 UniApp 中使用时,需要用 `<native-component>` 包裹以确保组件正确显示
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<native-component>
|
||||||
|
<channel-video
|
||||||
|
feed-id="{{feedId}}"
|
||||||
|
finder-user-name="{{finderUserName}}"
|
||||||
|
feed-token="{{feedToken}}"
|
||||||
|
bindenterfullscreen="enterFullscreen"
|
||||||
|
bindexitfullscreen="exitFullscreen"
|
||||||
|
binderror="videoError"
|
||||||
|
></channel-video>
|
||||||
|
</native-component>
|
||||||
|
<!-- #endif -->
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 API参数说明
|
||||||
|
|
||||||
|
- **feedId**:视频号内容ID,必填,必须是有效的视频号内容ID
|
||||||
|
- **finderUserName**:视频号ID,可选
|
||||||
|
- **feedToken**:非同主体视频号视频的标识,从基础库 2.31.1 开始支持,非个人主体小程序可使用
|
||||||
|
- **success**:调用成功的回调函数
|
||||||
|
- **fail**:调用失败的回调函数
|
||||||
|
- **complete**:调用完成的回调函数(无论成功或失败)
|
||||||
|
|
||||||
|
### 4.4 错误码说明
|
||||||
|
|
||||||
|
| 错误码 | 说明 | 解决方案 |
|
||||||
|
|--------|------|----------|
|
||||||
|
| 40001 | 未授权 | 对于内嵌打开,检查主体是否满足要求或是否已关联 |
|
||||||
|
| 40002 | 参数错误 | 检查feedId是否正确,确保是有效的视频号内容ID |
|
||||||
|
| 40003 | 视频号内容不存在或已被删除 | 确认feedId对应的视频号内容是否仍然存在 |
|
||||||
|
| 40004 | 系统错误 | 稍后重试,或联系微信公众平台客服 |
|
||||||
|
|
||||||
|
### 4.5 获取参数方法
|
||||||
|
|
||||||
|
1. **获取finderUserName(视频号ID)**
|
||||||
|
- 登录视频号助手
|
||||||
|
- 在首页可以查看自己的视频号ID
|
||||||
|
|
||||||
|
2. **获取feedId(视频号内容ID)**
|
||||||
|
- 登录视频号助手
|
||||||
|
- 在「动态管理」模块可以复制自己发表的每个视频对应的feedId
|
||||||
|
|
||||||
|
3. **获取feed-token(非同主体视频号视频标识)**
|
||||||
|
- 登录MP平台,在「设置-基本设置-隐私与安全」找到「获取视频号视频ID权限」,并将开关打开
|
||||||
|
- 移动端找到想要内嵌的视频号视频,并复制该视频的feed-token
|
||||||
|
- 注意:打开开关后24小时内生效,失效后需要再次打开开关;开关打开状态仅对当前操作者生效
|
||||||
|
|
||||||
|
### 4.6 主体判断逻辑
|
||||||
|
|
||||||
|
#### 主体信息查询
|
||||||
|
小程序主体信息可通过小程序资料页-开发团队进行查询,视频号主体信息可通过视频号首页-认证进行查询。
|
||||||
|
|
||||||
|
#### 主体判断逻辑
|
||||||
|
- **跳转打开视频号视频**:无主体限制,基础库版本 2.19.2 及以上
|
||||||
|
- **内嵌视频号视频**:
|
||||||
|
- 从基础库版本 2.25.1 至 2.31.1,小程序需与视频号视频相同主体或关联主体
|
||||||
|
- 从基础库版本 2.31.1 开始,非个人主体小程序可内嵌非同主体/关联主体视频号视频
|
||||||
|
|
||||||
|
关联主体申请流程可以参考:https://kf.qq.com/faq/190726e6JFja190726qMJBn6.html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 5. 注意事项
|
||||||
|
|
||||||
|
### 5.1 环境限制
|
||||||
|
|
||||||
|
- 微信视频号API只能在微信小程序环境中使用,其他环境(如H5、App)不支持
|
||||||
|
- 跳转打开视频号视频需要微信小程序基础库版本 2.19.2 及以上
|
||||||
|
- 内嵌视频号视频需要微信小程序基础库版本 2.25.1 及以上
|
||||||
|
- 小程序与视频号关联仅在需要内嵌非同主体视频号视频(基础库 2.31.1 以下)时需要
|
||||||
|
- 必须确保在 `manifest.json` 中设置 `usingComponents: true` 以启用组件支持
|
||||||
|
|
||||||
|
### 5.2 数据格式
|
||||||
|
|
||||||
|
确保传递给组件的数据格式正确,特别是 `feedId` 必须是有效的视频号内容ID
|
||||||
|
|
||||||
|
### 5.3 图片资源
|
||||||
|
|
||||||
|
- 视频号头像和封面图片建议使用CDN地址,确保加载速度
|
||||||
|
- 图片尺寸建议:
|
||||||
|
- 头像:200x200px
|
||||||
|
- 封面:640x360px
|
||||||
|
|
||||||
|
### 5.4 播放按钮图标
|
||||||
|
|
||||||
|
- 组件支持两种播放按钮图标方式:
|
||||||
|
1. **图片图标**:通过 `playIcon` 属性设置,优先级较高
|
||||||
|
2. **纯 CSS 图标**:当 `playIcon` 不可用时,组件会自动使用纯 CSS 生成的播放图标,确保播放按钮始终可见
|
||||||
|
|
||||||
|
- 纯 CSS 图标特点:
|
||||||
|
- 不依赖外部图片资源,加载更快
|
||||||
|
- 支持响应式调整大小
|
||||||
|
- 具有与图片图标相同的悬停效果
|
||||||
|
- 在所有环境中都能正常显示
|
||||||
|
|
||||||
|
### 5.5 权限和关联
|
||||||
|
|
||||||
|
- 确保在微信小程序后台开通了「打开视频号内容」权限
|
||||||
|
- 确保小程序已与视频号成功关联
|
||||||
|
- 关联操作需要小程序管理员和视频号管理员共同确认
|
||||||
|
|
||||||
|
## 6. 常见问题
|
||||||
|
|
||||||
|
### 6.1 视频无法播放
|
||||||
|
|
||||||
|
- 检查 `feedId` 是否正确,确保是有效的视频号内容ID
|
||||||
|
- 检查微信小程序基础库版本是否满足要求(跳转打开需要 2.19.2+,内嵌打开需要 2.25.1+)
|
||||||
|
- 对于内嵌打开,检查主体是否满足要求(基础库 2.31.1 以下需要相同主体或关联主体)
|
||||||
|
- 检查视频号内容是否仍然存在(未被删除)
|
||||||
|
|
||||||
|
### 6.2 功能不可用
|
||||||
|
|
||||||
|
- 确认当前环境是否为微信小程序环境
|
||||||
|
- 检查微信小程序基础库版本是否满足要求
|
||||||
|
- 对于内嵌打开,检查主体条件是否满足
|
||||||
|
- 检查 `feedId` 等参数是否正确
|
||||||
|
|
||||||
|
### 6.3 关联失败
|
||||||
|
|
||||||
|
- 检查视频号是否已完成实名认证
|
||||||
|
- 检查小程序是否已发布上线
|
||||||
|
- 检查视频号名称或ID是否输入正确
|
||||||
|
- 确认视频号管理员是否在24小时内确认关联邀请
|
||||||
|
- 如超过24小时未确认,需要重新发送关联邀请
|
||||||
|
|
||||||
|
### 6.4 组件显示异常
|
||||||
|
|
||||||
|
- 检查传递给组件的数据格式是否正确
|
||||||
|
- 确认图片资源是否可访问
|
||||||
|
- 检查微信小程序基础库版本是否支持视频号API
|
||||||
|
|
||||||
|
### 6.5 样式问题
|
||||||
|
|
||||||
|
- 可通过组件的 `componentAngle`、`topAroundRadius`、`bottomAroundRadius` 等属性调整组件样式
|
||||||
|
- 可通过 `componentBgColor` 属性调整组件背景颜色
|
||||||
|
- 可通过 `font` 属性调整字体样式
|
||||||
|
- 可通过 `aspectRatio` 属性调整视频比例,支持 '16:9' 和 '3:4' 两种比例
|
||||||
|
|
||||||
|
### 6.6 环境兼容性问题
|
||||||
|
|
||||||
|
- 微信视频号API只能在微信小程序环境中使用
|
||||||
|
- 在其他环境中,组件会显示但无法播放视频
|
||||||
|
- 建议在非小程序环境中添加友好的提示信息
|
||||||
|
|
||||||
|
### 6.7 在uni-app中使用原生组件时,可能会遇到组件不显示的问题
|
||||||
|
|
||||||
|
在uni-app中使用原生组件时,可能会遇到组件不显示的问题。以下是已验证的解决方案:
|
||||||
|
|
||||||
|
#### 解决方案
|
||||||
|
|
||||||
|
**方法一:使用 `<native-component>` 包裹**
|
||||||
|
|
||||||
|
在 UniApp 中使用 `channel-video` 组件时,必须用 `<native-component>` 包裹以确保组件正确显示:
|
||||||
|
|
||||||
|
``` vue
|
||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<native-component>
|
||||||
|
<channel-video
|
||||||
|
feed-id="{{feedId}}"
|
||||||
|
finder-user-name="{{finderUserName}}"
|
||||||
|
feed-token="{{feedToken}}"
|
||||||
|
style="width: 100%; height: 320rpx;"
|
||||||
|
></channel-video>
|
||||||
|
</native-component>
|
||||||
|
<!-- #endif -->
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
**方法二:检查 `manifest.json` 配置**
|
||||||
|
|
||||||
|
确保在 `manifest.json` 文件中正确设置了 `usingComponents: true`,特别是在 `mp-weixin` 部分:
|
||||||
|
|
||||||
|
``` json
|
||||||
|
"mp-weixin": {
|
||||||
|
"appid": "你的小程序appid",
|
||||||
|
"usingComponents": true,
|
||||||
|
// 其他配置...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**方法三:检查微信小程序基础库版本**
|
||||||
|
|
||||||
|
确保微信小程序基础库版本满足要求:
|
||||||
|
- 跳转打开视频号视频需要基础库版本 2.19.2 及以上
|
||||||
|
- 内嵌视频号视频需要基础库版本 2.25.1 及以上
|
||||||
|
|
||||||
|
**方法四:检查参数是否正确**
|
||||||
|
|
||||||
|
确保传递给 `channel-video` 组件的参数正确:
|
||||||
|
- `feedId`:视频号内容ID,必填
|
||||||
|
- `finderUserName`:视频号ID,可选
|
||||||
|
- `feedToken`:非同主体视频号视频的标识,可选(基础库 2.31.1+)
|
||||||
|
|
||||||
|
## 7. 示例代码
|
||||||
|
|
||||||
|
### 7.1 视频号视频卡片组件示例
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 视频卡片数据
|
||||||
|
const videoData = {
|
||||||
|
channelName: "示例视频号",
|
||||||
|
coverUrl: "https://example.com/cover.jpg",
|
||||||
|
videoTitle: "这是一个示例视频,标题可能会很长,需要测试多行显示效果",
|
||||||
|
feedId: "v02004g10000c3f7l7j5u87l33n8f160",
|
||||||
|
viewCount: 12345,
|
||||||
|
embedMode: false,
|
||||||
|
showViewCount: true,
|
||||||
|
finderUserName: "example_finder",
|
||||||
|
feedToken: "example_feed_token"
|
||||||
|
};
|
||||||
|
|
||||||
|
// 视频播放事件处理
|
||||||
|
function handlerVideoPlay(data) {
|
||||||
|
console.log("视频播放", data);
|
||||||
|
// 可以在这里处理视频播放逻辑
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 单个视频号示例
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 单个视频号数据
|
||||||
|
const channelData = {
|
||||||
|
channelName: "示例视频号",
|
||||||
|
avatarUrl: "https://example.com/avatar.jpg",
|
||||||
|
videoTitle: "这是一个示例视频,标题可能会很长,需要测试多行显示效果",
|
||||||
|
coverUrl: "https://example.com/cover.jpg",
|
||||||
|
feedId: "v02004g10000c3f7l7j5u87l33n8f160",
|
||||||
|
viewCount: 12345,
|
||||||
|
showFollow: true,
|
||||||
|
componentAngle: "round",
|
||||||
|
topAroundRadius: 10,
|
||||||
|
bottomAroundRadius: 10,
|
||||||
|
embedMode: false,
|
||||||
|
showViewCount: true,
|
||||||
|
finderUserName: "example_finder",
|
||||||
|
feedToken: "example_feed_token",
|
||||||
|
titleLineClamp: 2,
|
||||||
|
showPlayBtn: true,
|
||||||
|
aspectRatio: "16:9",
|
||||||
|
coverStyle: {
|
||||||
|
width: "100%",
|
||||||
|
height: "0",
|
||||||
|
paddingTop: "56.25%" // 16:9 比例
|
||||||
|
},
|
||||||
|
playBtnStyle: {
|
||||||
|
width: "60rpx",
|
||||||
|
height: "60rpx",
|
||||||
|
borderRadius: "30rpx",
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.6)"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 视频号列表示例
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 视频号列表数据
|
||||||
|
const channelListData = {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
channelName: "示例视频号1",
|
||||||
|
coverUrl: "https://example.com/cover1.jpg",
|
||||||
|
videoTitle: "这是示例视频1,标题可能会很长,需要测试多行显示效果",
|
||||||
|
feedId: "v02004g10000c3f7l7j5u87l33n8f160",
|
||||||
|
viewCount: 12345,
|
||||||
|
embedMode: false,
|
||||||
|
showViewCount: true,
|
||||||
|
finderUserName: "example_finder1",
|
||||||
|
feedToken: "example_feed_token1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
channelName: "示例视频号2",
|
||||||
|
coverUrl: "https://example.com/cover2.jpg",
|
||||||
|
videoTitle: "这是示例视频2,标题可能会很长,需要测试多行显示效果",
|
||||||
|
feedId: "v02004g10000c3f7m7j5u87l33n8f161",
|
||||||
|
viewCount: 67890,
|
||||||
|
embedMode: false,
|
||||||
|
showViewCount: true,
|
||||||
|
finderUserName: "example_finder2",
|
||||||
|
feedToken: "example_feed_token2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
rowCount: 2,
|
||||||
|
showStyle: "fixed",
|
||||||
|
mode: "",
|
||||||
|
font: {
|
||||||
|
size: 14,
|
||||||
|
weight: "normal",
|
||||||
|
color: "#333"
|
||||||
|
},
|
||||||
|
componentBgColor: "#fff",
|
||||||
|
componentAngle: "round",
|
||||||
|
topAroundRadius: 10,
|
||||||
|
bottomAroundRadius: 10,
|
||||||
|
ornament: {},
|
||||||
|
titleLineClamp: 2,
|
||||||
|
showPlayBtn: true,
|
||||||
|
aspectRatio: "16:9",
|
||||||
|
coverStyle: {
|
||||||
|
width: "100%",
|
||||||
|
height: "0",
|
||||||
|
paddingTop: "56.25%" // 16:9 比例
|
||||||
|
},
|
||||||
|
playBtnStyle: {
|
||||||
|
width: "60rpx",
|
||||||
|
height: "60rpx",
|
||||||
|
borderRadius: "30rpx",
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.6)"
|
||||||
|
},
|
||||||
|
imageSize: 150,
|
||||||
|
pageCount: 1,
|
||||||
|
carousel: {
|
||||||
|
type: "default",
|
||||||
|
autoplay: true,
|
||||||
|
interval: 3000,
|
||||||
|
duration: 500,
|
||||||
|
circular: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. 总结
|
||||||
|
|
||||||
|
通过本文档提供的组件和配置指导,你可以在UniApp项目中轻松集成微信视频号功能,为用户提供更加丰富的内容体验。
|
||||||
|
|
||||||
|
如果在集成过程中遇到问题,请参考本文档的常见问题部分,或查阅微信官方文档获取更多帮助。
|
||||||
834
docs/components_usage_documentation.md
Normal file
834
docs/components_usage_documentation.md
Normal file
@@ -0,0 +1,834 @@
|
|||||||
|
# 组件使用情况文档
|
||||||
|
|
||||||
|
提示词:请重新帮我分析 components 及 uni_modules 目录下的组件,被哪些页面/组件使用,按照Vue组件精确引用次数由多到少来排序,并更新到该文档中。
|
||||||
|
|
||||||
|
## 1. 组件目录结构
|
||||||
|
|
||||||
|
### 1.1 components 目录包含以下组件:
|
||||||
|
|
||||||
|
- chat-message
|
||||||
|
- hover-nav
|
||||||
|
- l-time
|
||||||
|
- loading-cover
|
||||||
|
- mescroll
|
||||||
|
- ns-adv
|
||||||
|
- ns-chat
|
||||||
|
- ns-contact
|
||||||
|
- ns-copyright
|
||||||
|
- ns-empty
|
||||||
|
- ns-form
|
||||||
|
- ns-goods-action
|
||||||
|
- ns-goods-action-button
|
||||||
|
- ns-goods-action-icon
|
||||||
|
- ns-goods-recommend
|
||||||
|
- ns-goods-sku
|
||||||
|
- ns-loading
|
||||||
|
- ns-login
|
||||||
|
- ns-mp-html
|
||||||
|
- ns-navbar
|
||||||
|
- ns-payment
|
||||||
|
- ns-progress
|
||||||
|
- ns-select-time
|
||||||
|
- ns-switch
|
||||||
|
- ns-video-player-popup
|
||||||
|
- payment
|
||||||
|
- pick-regions
|
||||||
|
- privacy-popup
|
||||||
|
- register-reward
|
||||||
|
- sx-rate
|
||||||
|
- to-top
|
||||||
|
- uv-count-to
|
||||||
|
- wxwork-contact
|
||||||
|
- xiao-star-component
|
||||||
|
- yuyue-date
|
||||||
|
|
||||||
|
### 1.2 uni_modules 目录包含以下组件:
|
||||||
|
|
||||||
|
- mp-html
|
||||||
|
- uni-badge
|
||||||
|
- uni-calendar
|
||||||
|
- uni-count-down
|
||||||
|
- uni-datetime-picker
|
||||||
|
- uni-drawer
|
||||||
|
- uni-grid
|
||||||
|
- uni-grid-item
|
||||||
|
- uni-icons
|
||||||
|
- uni-nav-bar
|
||||||
|
- uni-number-box
|
||||||
|
- uni-popup
|
||||||
|
- uni-popup-sku
|
||||||
|
- uni-popup-sku-category
|
||||||
|
- uni-scss
|
||||||
|
- uni-status-bar
|
||||||
|
- uni-tag
|
||||||
|
- x-skeleton
|
||||||
|
|
||||||
|
## 2. 组件使用情况详细列表(按引用次数排序)
|
||||||
|
|
||||||
|
### 2.1 loading-cover 组件(78次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/loading-cover/loading-cover.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/form/formdata.vue
|
||||||
|
- pages_goods/detail.vue
|
||||||
|
- pages_order/_components/common-payment/common-payment.vue
|
||||||
|
- pages_tool/recharge/order_list.vue
|
||||||
|
- pages_tool/recharge/list.vue
|
||||||
|
- pages_tool/pay/index.vue
|
||||||
|
- pages_tool/pay/result.vue
|
||||||
|
- pages_tool/order/refund_type_select.vue
|
||||||
|
- pages_tool/order/refund_goods_select.vue
|
||||||
|
- pages_tool/order/refund_detail.vue
|
||||||
|
- pages_tool/order/detail_virtual.vue
|
||||||
|
- pages_tool/order/evaluate.vue
|
||||||
|
- pages_tool/order/logistics.vue
|
||||||
|
- pages_tool/order/refund.vue
|
||||||
|
- pages_tool/order/refund_batch.vue
|
||||||
|
- pages_tool/member/withdrawal.vue
|
||||||
|
- pages_tool/member/withdrawal_detail.vue
|
||||||
|
- pages_tool/notice/detail.vue
|
||||||
|
- pages_tool/notice/list.vue
|
||||||
|
- pages_tool/member/point.vue
|
||||||
|
- pages_tool/member/point_detail.vue
|
||||||
|
- pages_tool/member/invite_friends.vue
|
||||||
|
- pages_tool/member/level.vue
|
||||||
|
- pages_tool/member/level_growth_rules.vue
|
||||||
|
- pages_tool/member/coupon.vue
|
||||||
|
- pages_tool/member/footprint.vue
|
||||||
|
- pages_tool/member/card_buy.vue
|
||||||
|
- pages_tool/member/collection.vue
|
||||||
|
- pages_tool/member/balance.vue
|
||||||
|
- pages_tool/member/balance_detail.vue
|
||||||
|
- pages_tool/member/account.vue
|
||||||
|
- pages_tool/member/account_edit.vue
|
||||||
|
- pages_tool/member/address.vue
|
||||||
|
- pages_tool/member/address_edit.vue
|
||||||
|
- pages_tool/member/apply_withdrawal.vue
|
||||||
|
- pages_tool/login/login.vue
|
||||||
|
- pages_tool/help/detail.vue
|
||||||
|
- pages_tool/help/list.vue
|
||||||
|
- pages_tool/goods/coupon.vue
|
||||||
|
- pages_tool/goods/coupon_receive.vue
|
||||||
|
- pages_tool/goods/evaluate.vue
|
||||||
|
- pages_tool/files/list.vue
|
||||||
|
- pages_tool/form/form.vue
|
||||||
|
- pages_tool/goods/brand.vue
|
||||||
|
- pages_tool/article/detail.vue
|
||||||
|
- pages_tool/article/list.vue
|
||||||
|
- pages_promotion/point/list.vue
|
||||||
|
- pages_promotion/point/order_list.vue
|
||||||
|
- pages_promotion/point/payment.vue
|
||||||
|
- pages_promotion/point/detail.vue
|
||||||
|
- pages_promotion/merch/merchcategory.vue
|
||||||
|
- pages_promotion/merch/detail.vue
|
||||||
|
- pages_promotion/fenxiao/withdrawal_detail.vue
|
||||||
|
- pages_promotion/fenxiao/withdraw_list.vue
|
||||||
|
- pages_promotion/fenxiao/withdraw_apply.vue
|
||||||
|
- pages_promotion/fenxiao/ranking_list.vue
|
||||||
|
- pages_promotion/fenxiao/relation.vue
|
||||||
|
- pages_promotion/fenxiao/team.vue
|
||||||
|
- pages_promotion/fenxiao/order.vue
|
||||||
|
- pages_promotion/fenxiao/order_detail.vue
|
||||||
|
- pages_promotion/fenxiao/promote.vue
|
||||||
|
- pages_promotion/fenxiao/promote_code.vue
|
||||||
|
- pages_promotion/fenxiao/index.vue
|
||||||
|
- pages_promotion/fenxiao/level.vue
|
||||||
|
- pages_promotion/fenxiao/goods_list.vue
|
||||||
|
- pages_promotion/fenxiao/apply.vue
|
||||||
|
- pages_promotion/fenxiao/bill.vue
|
||||||
|
- pages_promotion/fenxiao/child_fenxiao.vue
|
||||||
|
- pages_order/detail_point.vue
|
||||||
|
- pages_order/list.vue
|
||||||
|
- pages_order/detail.vue
|
||||||
|
- pages_order/detail_local_delivery.vue
|
||||||
|
- pages_order/detail_pickup.vue
|
||||||
|
- pages_goods/list.vue
|
||||||
|
- pages_goods/cart.vue
|
||||||
|
- pages_goods/category.vue
|
||||||
|
- components-diy/diy-category-item.vue
|
||||||
|
|
||||||
|
### 2.2 uni-popup 组件(66次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-popup/components/uni-popup/uni-popup.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_goods/_components/goods-detail-view/goods-detail-view.vue
|
||||||
|
- pages_tool/member/index.vue
|
||||||
|
- pages_goods/detail.vue
|
||||||
|
- pages_order/payment.vue
|
||||||
|
- pages_order/_components/common-payment/common-payment.vue
|
||||||
|
- pages_tool/contact/contact.vue
|
||||||
|
- components/ns-contact/ns-contact.vue
|
||||||
|
- pages/index/index.vue
|
||||||
|
- components/ns-video-player-popup/ns-video-player-popup.vue
|
||||||
|
- pages/contact/contact.vue
|
||||||
|
- uni_modules/uni-popup-sku/components/uni-popup-sku/uni-popup-sku.vue
|
||||||
|
- uni_modules/uni-popup-sku-category/components/uni-popup-sku-category/uni-popup-sku-category.vue
|
||||||
|
- pages_tool/recharge/list.vue
|
||||||
|
- pages_tool/pay/result.vue
|
||||||
|
- pages_tool/order/refund_type_select.vue
|
||||||
|
- pages_tool/order/refund.vue
|
||||||
|
- pages_tool/order/refund_batch.vue
|
||||||
|
- pages_tool/member/invite_friends.vue
|
||||||
|
- pages_tool/member/level.vue
|
||||||
|
- pages_tool/member/card_buy.vue
|
||||||
|
- pages_tool/member/collection.vue
|
||||||
|
- pages_tool/member/card.vue
|
||||||
|
- pages_tool/login/login.vue
|
||||||
|
- pages_tool/index/diy.vue
|
||||||
|
- pages_tool/cases/index.vue
|
||||||
|
- pages_promotion/point/list.vue
|
||||||
|
- pages_promotion/point/payment.vue
|
||||||
|
- pages_promotion/merch/public/category.scss
|
||||||
|
- pages_promotion/point/detail.vue
|
||||||
|
- pages_promotion/merch/merchcategory.vue
|
||||||
|
- pages_promotion/merch/detail.vue
|
||||||
|
- pages_promotion/fenxiao/promote_code.vue
|
||||||
|
- pages_promotion/fenxiao/index.vue
|
||||||
|
- pages_promotion/fenxiao/level.vue
|
||||||
|
- pages_promotion/fenxiao/goods_list.vue
|
||||||
|
- pages_promotion/fenxiao/apply.vue
|
||||||
|
- pages_order/detail_point.vue
|
||||||
|
- pages_order/detail.vue
|
||||||
|
- pages_goods/list.vue
|
||||||
|
- pages_goods/public/css/cart.scss
|
||||||
|
- pages_goods/cart.vue
|
||||||
|
- pages_goods/category.vue
|
||||||
|
- components/yuyue-date/yuyue-date.vue
|
||||||
|
- components/register-reward/register-reward.vue
|
||||||
|
- components/pick-regions/pick-regions.vue
|
||||||
|
- components/ns-select-time/ns-select-time.vue
|
||||||
|
- components/payment/payment.vue
|
||||||
|
- components/ns-payment/ns-payment.vue
|
||||||
|
- components/ns-goods-sku/ns-goods-sku.vue
|
||||||
|
- components/ns-login/ns-login.vue
|
||||||
|
- pages_tool/_components/ns-new-gift/ns-new-gift.vue
|
||||||
|
- components/ns-goods-sku/ns-goods-sku-category.vue
|
||||||
|
- pages_tool/_components/ns-birthday-gift/ns-birthday-gift.vue
|
||||||
|
- components/chat-message/chat-message.vue
|
||||||
|
- components-diy/diy-video-list.vue
|
||||||
|
- components-diy/diy-payment-qrcode.vue
|
||||||
|
- components-diy/diy-notice.vue
|
||||||
|
- components-diy/diy-member-info.vue
|
||||||
|
- components-diy/diy-index-page.vue
|
||||||
|
- components-diy/diy-category.vue
|
||||||
|
- components-diy/diy-category-item.vue
|
||||||
|
|
||||||
|
### 2.3 mescroll 组件(63次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/mescroll/
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_order/_components/common-payment/common-payment.vue
|
||||||
|
- pages_tool/contact/contact.vue
|
||||||
|
- lang/zh-cn/common.js
|
||||||
|
- lang/en-us/common.js
|
||||||
|
- pages.json
|
||||||
|
- pages/index/index.vue
|
||||||
|
- pages_tool/recharge/order_list.vue
|
||||||
|
- pages_tool/recharge/list.vue
|
||||||
|
- pages_tool/order/logistics.vue
|
||||||
|
- pages_tool/member/withdrawal.vue
|
||||||
|
- pages_tool/notice/list.vue
|
||||||
|
- pages_tool/order/activist.vue
|
||||||
|
- pages_tool/member/public/js/collection.js
|
||||||
|
- pages_tool/member/point_detail.vue
|
||||||
|
- pages_tool/member/coupon.vue
|
||||||
|
- pages_tool/member/footprint.vue
|
||||||
|
- pages_tool/member/collection.vue
|
||||||
|
- pages_tool/member/balance.vue
|
||||||
|
- pages_tool/member/balance_detail.vue
|
||||||
|
- pages_tool/member/account.vue
|
||||||
|
- pages_tool/member/address.vue
|
||||||
|
- pages_tool/goods/coupon.vue
|
||||||
|
- pages_tool/goods/evaluate.vue
|
||||||
|
- pages_tool/files/list.vue
|
||||||
|
- pages_tool/goods/brand.vue
|
||||||
|
- pages_tool/article/list.vue
|
||||||
|
- pages_promotion/point/goods_list.vue
|
||||||
|
- pages_promotion/point/list.vue
|
||||||
|
- pages_promotion/point/order_list.vue
|
||||||
|
- pages_promotion/merch/public/js/list.js
|
||||||
|
- pages_promotion/merch/detail.vue
|
||||||
|
- pages_promotion/fenxiao/withdraw_list.vue
|
||||||
|
- pages_promotion/fenxiao/ranking_list.vue
|
||||||
|
- pages_promotion/fenxiao/relation.vue
|
||||||
|
- pages_promotion/fenxiao/team.vue
|
||||||
|
- pages_promotion/fenxiao/public/js/goods_list.js
|
||||||
|
- pages_promotion/fenxiao/order.vue
|
||||||
|
- pages_promotion/fenxiao/goods_list.vue
|
||||||
|
- pages_promotion/fenxiao/bill.vue
|
||||||
|
- pages_promotion/fenxiao/child_fenxiao.vue
|
||||||
|
- pages_order/list.vue
|
||||||
|
- pages_goods/public/js/list.js
|
||||||
|
- pages_goods/list.vue
|
||||||
|
- components/to-top/to-top.vue
|
||||||
|
- components-diy/diy-index-page.vue
|
||||||
|
- components/ns-loading/ns-loading.vue
|
||||||
|
|
||||||
|
### 2.4 ns-login 组件(53次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-login/ns-login.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/form/formdata.vue
|
||||||
|
- pages_goods/detail.vue
|
||||||
|
- pages_order/_components/common-payment/common-payment.vue
|
||||||
|
- pages/index/index.vue
|
||||||
|
- pages_tool/recharge/order_list.vue
|
||||||
|
- pages_tool/pay/cashier.vue
|
||||||
|
- pages_tool/member/point.vue
|
||||||
|
- pages_tool/member/invite_friends.vue
|
||||||
|
- pages_tool/member/level.vue
|
||||||
|
- pages_tool/member/coupon.vue
|
||||||
|
- pages_tool/member/footprint.vue
|
||||||
|
- pages_tool/member/info.vue
|
||||||
|
- pages_tool/member/info_edit.vue
|
||||||
|
- pages_tool/member/card_buy.vue
|
||||||
|
- pages_tool/member/collection.vue
|
||||||
|
- pages_tool/member/card.vue
|
||||||
|
- pages_tool/member/balance.vue
|
||||||
|
- pages_tool/member/balance_detail.vue
|
||||||
|
- pages_tool/member/account.vue
|
||||||
|
- pages_tool/member/address.vue
|
||||||
|
- pages_tool/goods/coupon.vue
|
||||||
|
- pages_tool/goods/coupon_receive.vue
|
||||||
|
- pages_tool/form/form.vue
|
||||||
|
- pages_promotion/point/list.vue
|
||||||
|
- pages_promotion/point/order_list.vue
|
||||||
|
- pages_promotion/point/detail.vue
|
||||||
|
- pages_promotion/merch/merchcategory.vue
|
||||||
|
- pages_promotion/fenxiao/withdraw_list.vue
|
||||||
|
- pages_promotion/fenxiao/relation.vue
|
||||||
|
- pages_promotion/fenxiao/team.vue
|
||||||
|
- pages_promotion/fenxiao/order.vue
|
||||||
|
- pages_promotion/fenxiao/order_detail.vue
|
||||||
|
- pages_promotion/fenxiao/promote.vue
|
||||||
|
- pages_promotion/fenxiao/promote_code.vue
|
||||||
|
- pages_promotion/fenxiao/index.vue
|
||||||
|
- pages_promotion/fenxiao/child_fenxiao.vue
|
||||||
|
- pages_order/list.vue
|
||||||
|
- pages_goods/cart.vue
|
||||||
|
- pages_goods/category.vue
|
||||||
|
- components/ns-goods-sku/ns-goods-sku.vue
|
||||||
|
- components/ns-goods-sku/ns-goods-sku-index.vue
|
||||||
|
- components/ns-goods-sku/ns-goods-sku-category.vue
|
||||||
|
- pages_goods/_components/goods-detail-view/goods-detail-view.vue
|
||||||
|
- components-diy/diy-search.vue
|
||||||
|
- components-diy/diy-quick-nav.vue
|
||||||
|
- components-diy/diy-notes.vue
|
||||||
|
- components-diy/diy-member-info.vue
|
||||||
|
- components-diy/diy-member-my-order.vue
|
||||||
|
- components-diy/diy-image-nav.vue
|
||||||
|
- components-diy/diy-graphic-nav.vue
|
||||||
|
- components-diy/diy-digit.vue
|
||||||
|
- components-diy/diy-coupon.vue
|
||||||
|
|
||||||
|
### 2.5 ns-empty 组件(37次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-empty/ns-empty.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/form/formdata.vue
|
||||||
|
- pages_order/_components/common-payment/common-payment.vue
|
||||||
|
- pages_tool/seal/medium/search.vue
|
||||||
|
- pages_tool/recharge/order_list.vue
|
||||||
|
- pages_tool/pay/cashier.vue
|
||||||
|
- pages_tool/member/withdrawal.vue
|
||||||
|
- pages_tool/notice/list.vue
|
||||||
|
- pages_tool/order/activist.vue
|
||||||
|
- pages_tool/member/point_detail.vue
|
||||||
|
- pages_tool/member/invite_friends.vue
|
||||||
|
- pages_tool/member/coupon.vue
|
||||||
|
- pages_tool/member/footprint.vue
|
||||||
|
- pages_tool/member/card_buy.vue
|
||||||
|
- pages_tool/member/collection.vue
|
||||||
|
- pages_tool/member/balance_detail.vue
|
||||||
|
- pages_tool/help/list.vue
|
||||||
|
- pages_tool/goods/coupon.vue
|
||||||
|
- pages_tool/goods/evaluate.vue
|
||||||
|
- pages_tool/files/list.vue
|
||||||
|
- pages_tool/form/form.vue
|
||||||
|
- pages_tool/goods/brand.vue
|
||||||
|
- pages_tool/article/list.vue
|
||||||
|
- pages_promotion/point/goods_list.vue
|
||||||
|
- pages_promotion/point/order_list.vue
|
||||||
|
- pages_promotion/merch/detail.vue
|
||||||
|
- pages_promotion/fenxiao/withdraw_list.vue
|
||||||
|
- pages_promotion/fenxiao/ranking_list.vue
|
||||||
|
- pages_promotion/fenxiao/relation.vue
|
||||||
|
- pages_promotion/fenxiao/team.vue
|
||||||
|
- pages_promotion/fenxiao/order.vue
|
||||||
|
- pages_promotion/fenxiao/goods_list.vue
|
||||||
|
- pages_promotion/fenxiao/bill.vue
|
||||||
|
- pages_promotion/fenxiao/child_fenxiao.vue
|
||||||
|
- pages_order/list.vue
|
||||||
|
- pages_goods/list.vue
|
||||||
|
- pages_goods/cart.vue
|
||||||
|
- components-diy/diy-index-page.vue
|
||||||
|
|
||||||
|
### 2.6 privacy-popup 组件(26次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/privacy-popup/privacy-popup.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/member/index.vue
|
||||||
|
- pages_goods/detail.vue
|
||||||
|
- pages/index/index.vue
|
||||||
|
- pages_tool/webview/webview.vue
|
||||||
|
- pages_tool/notice/detail.vue
|
||||||
|
- pages_tool/notice/list.vue
|
||||||
|
- pages_tool/help/detail.vue
|
||||||
|
- pages_tool/help/list.vue
|
||||||
|
- pages_tool/index/diy.vue
|
||||||
|
- pages_tool/goods/coupon.vue
|
||||||
|
- pages_tool/goods/coupon_receive.vue
|
||||||
|
- pages_tool/files/list.vue
|
||||||
|
- pages_tool/goods/brand.vue
|
||||||
|
- pages_tool/article/detail.vue
|
||||||
|
- pages_tool/article/list.vue
|
||||||
|
- pages_promotion/point/goods_list.vue
|
||||||
|
- pages_promotion/point/list.vue
|
||||||
|
- pages_promotion/point/detail.vue
|
||||||
|
- pages_promotion/merch/detail.vue
|
||||||
|
- pages_promotion/fenxiao/promote.vue
|
||||||
|
- pages_promotion/fenxiao/promote_code.vue
|
||||||
|
- pages_promotion/fenxiao/goods_list.vue
|
||||||
|
- pages_goods/list.vue
|
||||||
|
- pages_goods/cart.vue
|
||||||
|
- pages_goods/category.vue
|
||||||
|
- components/ns-login/ns-login.vue
|
||||||
|
|
||||||
|
### 2.7 x-skeleton 组件(19次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/x-skeleton/components/x-skeleton/x-skeleton.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages.json
|
||||||
|
- components-diy/diy-article.vue
|
||||||
|
- components-diy/diy-seckill.vue
|
||||||
|
- components-diy/diy-store-label.vue
|
||||||
|
- components-diy/diy-presale.vue
|
||||||
|
- components-diy/diy-pinfan.vue
|
||||||
|
- components-diy/diy-pintuan.vue
|
||||||
|
- components-diy/diy-notes.vue
|
||||||
|
- components-diy/diy-merch-list.vue
|
||||||
|
- components-diy/diy-live.vue
|
||||||
|
- components-diy/diy-groupbuy.vue
|
||||||
|
- components-diy/diy-goods-recommend.vue
|
||||||
|
- components-diy/diy-goods-brand.vue
|
||||||
|
- components-diy/diy-goods-list.vue
|
||||||
|
- components-diy/diy-coupon.vue
|
||||||
|
- components-diy/diy-bargain.vue
|
||||||
|
|
||||||
|
### 2.8 ns-goods-sku 组件(13次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-goods-sku/
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_goods/detail.vue
|
||||||
|
- pages.json
|
||||||
|
- store/index.js
|
||||||
|
- pages_promotion/point/detail.vue
|
||||||
|
- pages_promotion/merch/detail.vue
|
||||||
|
- pages_goods/list.vue
|
||||||
|
- pages_goods/cart.vue
|
||||||
|
- components-diy/diy-goods-list.vue
|
||||||
|
- components-diy/diy-category.vue
|
||||||
|
|
||||||
|
### 2.9 to-top 组件(11次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/to-top/to-top.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_goods/detail.vue
|
||||||
|
- pages_tool/contact/contact.vue
|
||||||
|
- pages/index/index.vue
|
||||||
|
- pages_tool/member/level.vue
|
||||||
|
- pages_tool/member/level_growth_rules.vue
|
||||||
|
- pages_promotion/point/detail.vue
|
||||||
|
- pages_goods/cart.vue
|
||||||
|
- components/mescroll/mescroll-uni.vue
|
||||||
|
- pages_goods/_components/goods-detail-view/goods-detail-view.vue
|
||||||
|
- common/css/goods_detail.scss
|
||||||
|
|
||||||
|
### 2.10 ns-goods-recommend 组件(10次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-goods-recommend/ns-goods-recommend.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/pay/result.vue
|
||||||
|
- pages_tool/member/collection.vue
|
||||||
|
- pages_tool/member/card.vue
|
||||||
|
- pages_promotion/fenxiao/level.vue
|
||||||
|
- pages_order/detail_point.vue
|
||||||
|
- pages_order/detail.vue
|
||||||
|
- pages_goods/cart.vue
|
||||||
|
- pages_goods/_components/goods-detail-view/goods-detail-view.vue
|
||||||
|
|
||||||
|
### 2.11 ns-payment 组件(9次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-payment/ns-payment.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/recharge/list.vue
|
||||||
|
- pages_tool/member/card_buy.vue
|
||||||
|
- pages_promotion/point/order_list.vue
|
||||||
|
- pages_promotion/point/payment.vue
|
||||||
|
- pages_order/detail_point.vue
|
||||||
|
- pages_order/list.vue
|
||||||
|
- pages_order/detail.vue
|
||||||
|
|
||||||
|
### 2.12 ns-adv 组件(8次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-adv/ns-adv.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/files/list.vue
|
||||||
|
- pages_tool/goods/brand.vue
|
||||||
|
- pages_tool/article/list.vue
|
||||||
|
- pages_promotion/point/list.vue
|
||||||
|
- pages_promotion/point/public/css/list.scss
|
||||||
|
- pages_promotion/fenxiao/public/css/follow.scss
|
||||||
|
|
||||||
|
### 2.13 ns-form 组件(8次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-form/ns-form.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_goods/detail.vue
|
||||||
|
- pages_order/_components/common-payment/common-payment.vue
|
||||||
|
- pages_tool/form/form.vue
|
||||||
|
- pages_tool/_components/ns-newform/ns-newform.vue
|
||||||
|
- components/ns-goods-sku/ns-goods-sku.vue
|
||||||
|
- common/css/order_parment.scss
|
||||||
|
|
||||||
|
### 2.14 uni-icons 组件(8次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-icons/components/uni-icons/uni-icons.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue
|
||||||
|
- uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue
|
||||||
|
- uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue
|
||||||
|
|
||||||
|
### 2.15 ns-contact 组件(7次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-contact/ns-contact.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/order/refund_detail.vue
|
||||||
|
- pages_tool/member/contact.vue
|
||||||
|
- pages_order/detail_point.vue
|
||||||
|
- pages_order/detail.vue
|
||||||
|
- components/ns-goods-action-icon/ns-goods-action-icon.vue
|
||||||
|
|
||||||
|
### 2.16 ns-copyright 组件(7次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-copyright/ns-copyright.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/member/index.vue
|
||||||
|
- pages/index/index.vue
|
||||||
|
- pages_tool/index/diy.vue
|
||||||
|
- pages_promotion/fenxiao/promote.vue
|
||||||
|
- pages_promotion/fenxiao/index.vue
|
||||||
|
- pages_goods/_components/goods-detail-view/goods-detail-view.vue
|
||||||
|
|
||||||
|
### 2.17 hover-nav 组件(6次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/hover-nav/hover-nav.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/member/index.vue
|
||||||
|
- pages_tool/contact/contact.vue
|
||||||
|
- pages/index/index.vue
|
||||||
|
- pages/contact/contact.vue
|
||||||
|
|
||||||
|
### 2.18 pick-regions 组件(6次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/pick-regions/pick-regions.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/member/info_edit.vue
|
||||||
|
- pages_tool/member/address_edit.vue
|
||||||
|
- pages_tool/_components/ns-newform/ns-newform.vue
|
||||||
|
- components/ns-form/ns-form.vue
|
||||||
|
|
||||||
|
### 2.19 ns-navbar 组件(6次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-navbar/ns-navbar.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/member/index.vue
|
||||||
|
- pages_goods/detail.vue
|
||||||
|
- pages/index/index.vue
|
||||||
|
- pages_tool/index/diy.vue
|
||||||
|
|
||||||
|
### 2.20 uni-drawer 组件(4次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_promotion/point/goods_list.vue
|
||||||
|
- pages_promotion/merch/detail.vue
|
||||||
|
- pages_goods/list.vue
|
||||||
|
|
||||||
|
### 2.21 uni-calendar 组件(4次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue
|
||||||
|
- uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue
|
||||||
|
|
||||||
|
### 2.22 chat-message 组件(4次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/chat-message/chat-message.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/ai-chat/index.vue
|
||||||
|
- pages_tool/ai-chat/ai-chat-message.vue
|
||||||
|
|
||||||
|
### 2.23 ns-switch 组件(4次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-switch/ns-switch.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_order/_components/common-payment/common-payment.vue
|
||||||
|
- components/payment/payment.vue
|
||||||
|
- components/ns-payment/ns-payment.vue
|
||||||
|
|
||||||
|
### 2.24 register-reward 组件(4次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/register-reward/register-reward.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/login/login.vue
|
||||||
|
- components/ns-login/ns-login.vue
|
||||||
|
|
||||||
|
### 2.25 sx-rate 组件(4次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/sx-rate/sx-rate.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/order/evaluate.vue
|
||||||
|
|
||||||
|
### 2.26 uni-count-down 组件(3次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-count-down/components/uni-count-down/uni-count-down.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_goods/detail.vue
|
||||||
|
- pages_order/list.vue
|
||||||
|
- pages_order/detail.vue
|
||||||
|
|
||||||
|
### 2.27 uni-badge 组件(3次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-badge/components/uni-badge/uni-badge.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- uni_modules/uni-grid-item/components/uni-grid-item/uni-grid-item.vue
|
||||||
|
|
||||||
|
### 2.28 uni-grid 组件(3次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-grid/components/uni-grid/uni-grid.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/goods/brand.vue
|
||||||
|
|
||||||
|
### 2.29 ns-loading 组件(5次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-loading/ns-loading.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/ai-chat/ai-chat-message.vue
|
||||||
|
- components/mescroll/mescroll-uni.vue
|
||||||
|
- components-diy/diy-index-page.vue
|
||||||
|
|
||||||
|
### 2.30 ns-goods-action 组件(5次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-goods-action/ns-goods-action.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_goods/detail.vue
|
||||||
|
- components/ns-goods-action-icon/ns-goods-action-icon.vue
|
||||||
|
- components/ns-goods-action-button/ns-goods-action-button.vue
|
||||||
|
|
||||||
|
### 2.31 ns-chat 组件(5次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-chat/
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- components/chat-message/chat-message.vue
|
||||||
|
|
||||||
|
### 2.32 ns-select-time 组件(3次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-select-time/ns-select-time.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_order/_components/common-payment/common-payment.vue
|
||||||
|
- pages_promotion/point/payment.vue
|
||||||
|
|
||||||
|
### 2.33 uv-count-to 组件(3次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/uv-count-to/uv-count-to.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- components-diy/diy-digit.vue
|
||||||
|
|
||||||
|
### 2.34 ns-video-player-popup 组件(3次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-video-player-popup/ns-video-player-popup.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/contact/contact.vue
|
||||||
|
|
||||||
|
### 2.35 l-time 组件(3次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/l-time/l-time.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- common/css/icondiy.css
|
||||||
|
|
||||||
|
### 2.36 pengpai-fadein-out 组件(3次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/pengpai-fadein-out/pengpai-fadein-out.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_goods/_components/goods-detail-view/goods-detail-view.vue
|
||||||
|
|
||||||
|
### 2.37 ns-progress 组件(2次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-progress/ns-progress.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- pages_tool/member/level.vue
|
||||||
|
|
||||||
|
### 2.38 payment 组件(2次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/payment/payment.vue
|
||||||
|
|
||||||
|
**引用情况**:无直接引用
|
||||||
|
|
||||||
|
### 2.39 uni-popup-sku 组件(2次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-popup-sku/components/uni-popup-sku/uni-popup-sku.vue
|
||||||
|
|
||||||
|
**引用情况**:无直接引用
|
||||||
|
|
||||||
|
### 2.40 uni-popup-sku-category 组件(2次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-popup-sku-category/components/uni-popup-sku-category/uni-popup-sku-category.vue
|
||||||
|
|
||||||
|
**引用情况**:无直接引用
|
||||||
|
|
||||||
|
### 2.41 uni-datetime-picker 组件(13次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue
|
||||||
|
|
||||||
|
**引用情况**:
|
||||||
|
- components/yuyue-date/yuyue-date.vue
|
||||||
|
|
||||||
|
### 2.42 ns-mp-html 组件(1次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/ns-mp-html/ns-mp-html.vue
|
||||||
|
|
||||||
|
**引用情况**:无引用
|
||||||
|
|
||||||
|
### 2.43 wxwork-contact 组件(1次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/wxwork-contact/wxwork-contact.vue
|
||||||
|
|
||||||
|
**引用情况**:无引用
|
||||||
|
|
||||||
|
### 2.44 xiao-star-component 组件(1次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/xiao-star-component/xiao-star-component.vue
|
||||||
|
|
||||||
|
**引用情况**:无引用
|
||||||
|
|
||||||
|
### 2.45 yuyue-date 组件(1次引用)
|
||||||
|
|
||||||
|
**文件路径**:components/yuyue-date/yuyue-date.vue
|
||||||
|
|
||||||
|
**引用情况**:无引用
|
||||||
|
|
||||||
|
### 2.46 uni-nav-bar 组件(1次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue
|
||||||
|
|
||||||
|
**引用情况**:无直接引用
|
||||||
|
|
||||||
|
### 2.47 uni-number-box 组件(1次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue
|
||||||
|
|
||||||
|
**引用情况**:无直接引用
|
||||||
|
|
||||||
|
### 2.48 uni-status-bar 组件(1次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-status-bar/components/uni-status-bar/uni-status-bar.vue
|
||||||
|
|
||||||
|
**引用情况**:无直接引用
|
||||||
|
|
||||||
|
### 2.49 uni-tag 组件(1次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-tag/components/uni-tag/uni-tag.vue
|
||||||
|
|
||||||
|
**引用情况**:无直接引用
|
||||||
|
|
||||||
|
### 2.50 mp-html 组件(1次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/mp-html/components/mp-html/mp-html.vue
|
||||||
|
|
||||||
|
**引用情况**:无直接引用
|
||||||
|
|
||||||
|
### 2.51 uni-scss 组件(1次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-scss/components/uni-scss/
|
||||||
|
|
||||||
|
**引用情况**:无直接引用
|
||||||
|
|
||||||
|
### 2.52 uni-grid-item 组件(1次引用)
|
||||||
|
|
||||||
|
**文件路径**:uni_modules/uni-grid-item/components/uni-grid-item/uni-grid-item.vue
|
||||||
|
|
||||||
|
**引用情况**:无直接引用
|
||||||
|
|
||||||
|
## 3. 组件使用统计
|
||||||
|
|
||||||
|
### 3.1 使用最广泛的组件(前10)
|
||||||
|
|
||||||
|
1. loading-cover - 78个引用
|
||||||
|
2. uni-popup - 66个引用
|
||||||
|
3. mescroll - 63个引用
|
||||||
|
4. ns-login - 53个引用
|
||||||
|
5. ns-empty - 37个引用
|
||||||
|
6. privacy-popup - 26个引用
|
||||||
|
7. x-skeleton - 19个引用
|
||||||
|
8. ns-goods-sku - 13个引用
|
||||||
|
9. to-top - 11个引用
|
||||||
|
10. ns-goods-recommend - 10个引用
|
||||||
|
|
||||||
|
### 3.2 未被使用的组件
|
||||||
|
|
||||||
|
- ns-mp-html
|
||||||
|
- wxwork-contact
|
||||||
|
- xiao-star-component
|
||||||
|
- yuyue-date
|
||||||
|
- uni-nav-bar
|
||||||
|
- uni-number-box
|
||||||
|
- uni-status-bar
|
||||||
|
- uni-tag
|
||||||
|
- mp-html
|
||||||
|
- uni-scss
|
||||||
|
|
||||||
|
## 4. 总结
|
||||||
|
|
||||||
|
本项目的组件使用情况较为集中,核心组件如 loading-cover、uni-popup、mescroll、ns-login 等被广泛应用于多个页面。同时,也存在部分组件未被使用的情况,这些组件可能是为未来功能预留的,或者是已经被其他组件替代的。
|
||||||
|
|
||||||
|
通过本文档,可以清晰了解每个组件的使用范围,为后续的组件优化和维护提供参考。
|
||||||
|
|
||||||
|
**更新时间**:2026-01-16
|
||||||
165
docs/example/wechat-channel-demo.vue
Normal file
165
docs/example/wechat-channel-demo.vue
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
<template>
|
||||||
|
<view class="wechat-channel-demo">
|
||||||
|
<view class="demo-header">
|
||||||
|
<text class="demo-title">微信视频号集成示例</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="demo-section">
|
||||||
|
<view class="section-title">单个视频号</view>
|
||||||
|
<diy-wechat-channel :value="channelData" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="demo-section">
|
||||||
|
<view class="section-title">视频号列表</view>
|
||||||
|
<diy-wechat-channel-list :value="channelListData" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="demo-section">
|
||||||
|
<view class="section-title">使用说明</view>
|
||||||
|
<view class="section-content">
|
||||||
|
<view class="content-item">1. 确保微信小程序已开通视频号权限</view>
|
||||||
|
<view class="content-item">2. 小程序需与视频号关联</view>
|
||||||
|
<view class="content-item">3. 传递正确的feedId用于播放视频</view>
|
||||||
|
<view class="content-item">4. 仅支持微信小程序环境</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import diyWechatChannel from '@/components-diy/diy-wechat-channel.vue'
|
||||||
|
import diyWechatChannelList from '@/components-diy/diy-wechat-channel-list.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'wechat-channel-demo',
|
||||||
|
components: {
|
||||||
|
diyWechatChannel,
|
||||||
|
diyWechatChannelList
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 单个视频号数据
|
||||||
|
channelData: {
|
||||||
|
channelName: "示例视频号",
|
||||||
|
avatarUrl: "https://via.placeholder.com/100",
|
||||||
|
videoTitle: "这是一个示例视频,展示如何使用微信视频号组件",
|
||||||
|
coverUrl: "https://via.placeholder.com/640x360",
|
||||||
|
feedId: "v02004g10000c3f7l7j5u87l33n8f160",
|
||||||
|
viewCount: 12345,
|
||||||
|
showFollow: true,
|
||||||
|
componentAngle: "round",
|
||||||
|
topAroundRadius: 10,
|
||||||
|
bottomAroundRadius: 10
|
||||||
|
},
|
||||||
|
// 视频号列表数据
|
||||||
|
channelListData: {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
channelName: "示例视频号1",
|
||||||
|
coverUrl: "https://via.placeholder.com/640x360",
|
||||||
|
videoTitle: "这是示例视频1,展示如何使用微信视频号组件",
|
||||||
|
feedId: "v02004g10000c3f7l7j5u87l33n8f160",
|
||||||
|
viewCount: 12345
|
||||||
|
},
|
||||||
|
{
|
||||||
|
channelName: "示例视频号2",
|
||||||
|
coverUrl: "https://via.placeholder.com/640x360",
|
||||||
|
videoTitle: "这是示例视频2,展示如何使用微信视频号组件",
|
||||||
|
feedId: "v02004g10000c3f7m7j5u87l33n8f161",
|
||||||
|
viewCount: 67890
|
||||||
|
},
|
||||||
|
{
|
||||||
|
channelName: "示例视频号3",
|
||||||
|
coverUrl: "https://via.placeholder.com/640x360",
|
||||||
|
videoTitle: "这是示例视频3,展示如何使用微信视频号组件",
|
||||||
|
feedId: "v02004g10000c3f7n7j5u87l33n8f162",
|
||||||
|
viewCount: 98765
|
||||||
|
},
|
||||||
|
{
|
||||||
|
channelName: "示例视频号4",
|
||||||
|
coverUrl: "https://via.placeholder.com/640x360",
|
||||||
|
videoTitle: "这是示例视频4,展示如何使用微信视频号组件",
|
||||||
|
feedId: "v02004g10000c3f7o7j5u87l33n8f163",
|
||||||
|
viewCount: 54321
|
||||||
|
}
|
||||||
|
],
|
||||||
|
rowCount: 2,
|
||||||
|
showStyle: "fixed",
|
||||||
|
font: {
|
||||||
|
size: 14,
|
||||||
|
weight: "normal",
|
||||||
|
color: "#333"
|
||||||
|
},
|
||||||
|
componentBgColor: "#fff",
|
||||||
|
componentAngle: "round",
|
||||||
|
topAroundRadius: 10,
|
||||||
|
bottomAroundRadius: 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
console.log('微信视频号示例页面加载');
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 可以在这里添加自定义方法
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.wechat-channel-demo {
|
||||||
|
padding: 20rpx;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-header {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40rpx 0;
|
||||||
|
background-color: #fff;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-section {
|
||||||
|
background-color: #fff;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
padding: 20rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-content {
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-item {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 组件样式调整 */
|
||||||
|
.demo-section >>> .diy-wechat-channel {
|
||||||
|
margin: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-section >>> .diy-wechat-channel-list {
|
||||||
|
margin: 20rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -19,5 +19,17 @@ export const lang = {
|
|||||||
currencySymbol: '¥',
|
currencySymbol: '¥',
|
||||||
submit: 'Submit',
|
submit: 'Submit',
|
||||||
searchTip: 'Please enter search keywords'
|
searchTip: 'Please enter search keywords'
|
||||||
}
|
},
|
||||||
|
|
||||||
|
//customer service
|
||||||
|
customer: {
|
||||||
|
weChatKefu: 'WeChat Customer Service',
|
||||||
|
systemKefu: 'System Customer Service',
|
||||||
|
weChatWorkKefu: 'WeChat Work Customer Service',
|
||||||
|
},
|
||||||
|
|
||||||
|
login: 'Login/Register',
|
||||||
|
loginTips: 'Click to login and enjoy more exciting information',
|
||||||
|
toLogin: 'Go to login',
|
||||||
|
toGoodsCategoryPage: 'Go shopping',
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,17 @@ export const lang = {
|
|||||||
searchTip: '请输入搜索关键词'
|
searchTip: '请输入搜索关键词'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
//客服
|
||||||
|
customer: {
|
||||||
|
weChatKefu: '微信客服',
|
||||||
|
systemKefu: '专属客服',
|
||||||
|
weChatWorkKefu: '企业微信客服',
|
||||||
|
},
|
||||||
|
|
||||||
|
login: '登录/注册',
|
||||||
|
loginTpis: '点击登录 享受更多精彩信息',
|
||||||
|
toLogin: '去登录',
|
||||||
|
toGoodsCategoryPage: '去逛逛',
|
||||||
|
|
||||||
waitpay: '待付款',
|
waitpay: '待付款',
|
||||||
waitsend: '待发货',
|
waitsend: '待发货',
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ export const lang = {
|
|||||||
title: '商品分类',
|
title: '商品分类',
|
||||||
search:'商品搜索',
|
search:'商品搜索',
|
||||||
seeMore : '查看更多',
|
seeMore : '查看更多',
|
||||||
Make: "询底价"
|
Make: "立即咨询"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export const lang = {
|
|||||||
//title为每个页面的标题
|
//title为每个页面的标题
|
||||||
title: '',
|
title: '',
|
||||||
select: '选择',
|
select: '选择',
|
||||||
details:'资源详情',
|
details:'商品详情',
|
||||||
params: '参数',
|
params: '参数',
|
||||||
service: '商品服务',
|
service: '商品服务',
|
||||||
allGoods: '全部商品',
|
allGoods: '全部商品',
|
||||||
@@ -11,7 +11,7 @@ export const lang = {
|
|||||||
home:'首页',
|
home:'首页',
|
||||||
cart:'购物车',
|
cart:'购物车',
|
||||||
leave:'立即留言',
|
leave:'立即留言',
|
||||||
make:'联系我们',
|
make:'立即咨询',
|
||||||
|
|
||||||
send:'配送',
|
send:'配送',
|
||||||
express:'快递发货',
|
express:'快递发货',
|
||||||
@@ -22,7 +22,7 @@ export const lang = {
|
|||||||
status:'该商品已下架',
|
status:'该商品已下架',
|
||||||
sellout:'库存不足',
|
sellout:'库存不足',
|
||||||
max:'已达最大限购数量',
|
max:'已达最大限购数量',
|
||||||
makebtn:'价格电议',
|
makebtn:'立即咨询',
|
||||||
sales:'销量',
|
sales:'销量',
|
||||||
stock:'库存',
|
stock:'库存',
|
||||||
kefu:'客服',
|
kefu:'客服',
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ export const lang = {
|
|||||||
Price:'价格',
|
Price:'价格',
|
||||||
Filter:'筛选',
|
Filter:'筛选',
|
||||||
Search:'请输入您要搜索的商品',
|
Search:'请输入您要搜索的商品',
|
||||||
Make:'立即询底价',
|
Make:'立即咨询',
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export const lang = {
|
export const lang = {
|
||||||
//title为每个页面的标题
|
//title为每个页面的标题
|
||||||
title: '退款',
|
title: '售后',
|
||||||
checkDetail: '查看详情',
|
checkDetail: '查看详情',
|
||||||
emptyTips: '暂无退款记录'
|
emptyTips: '暂无售后记录'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
"quickapp" : {},
|
"quickapp" : {},
|
||||||
/* 小程序特有相关 */
|
/* 小程序特有相关 */
|
||||||
"mp-weixin" : {
|
"mp-weixin" : {
|
||||||
"appid" : "wx29215aa1bd97bbd6",
|
"appid" : "wx5e649e2286b29bdf",
|
||||||
"setting" : {
|
"setting" : {
|
||||||
"urlCheck" : false,
|
"urlCheck" : false,
|
||||||
"postcss" : false,
|
"postcss" : false,
|
||||||
@@ -66,15 +66,19 @@
|
|||||||
"minified" : true
|
"minified" : true
|
||||||
},
|
},
|
||||||
"usingComponents" : true,
|
"usingComponents" : true,
|
||||||
|
"lazyCodeLoading" : "requiredComponents",
|
||||||
"permission" : {
|
"permission" : {
|
||||||
"scope.userLocation" : {
|
"scope.userLocation" : {
|
||||||
"desc" : "为了更好地为您提供服务"
|
"desc" : "为了更好地为您提供服务"
|
||||||
},
|
},
|
||||||
"scope.writePhotosAlbum" : {
|
|
||||||
"desc" : "为了更好地为您提供服务"
|
|
||||||
},
|
|
||||||
"scope.userClipboard" : {
|
"scope.userClipboard" : {
|
||||||
"desc" : "为了方便您复制信息"
|
"desc" : "为了方便您复制信息"
|
||||||
|
},
|
||||||
|
"scope.camera" : {
|
||||||
|
"desc" : "用于拍照上传头像"
|
||||||
|
},
|
||||||
|
"scope.writePhotosAlbum" : {
|
||||||
|
"desc" : "用于从相册选择和保存头像"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"requiredPrivateInfos" : [ "chooseLocation", "getLocation", "chooseAddress" ],
|
"requiredPrivateInfos" : [ "chooseLocation", "getLocation", "chooseAddress" ],
|
||||||
@@ -103,7 +107,7 @@
|
|||||||
},
|
},
|
||||||
"router" : {
|
"router" : {
|
||||||
"mode" : "history",
|
"mode" : "history",
|
||||||
"base" : "/hwappx/common/"
|
"base" : "/hwappx/test/"
|
||||||
},
|
},
|
||||||
"title" : "",
|
"title" : "",
|
||||||
"devServer" : {
|
"devServer" : {
|
||||||
@@ -129,5 +133,5 @@
|
|||||||
"uniStatistics" : {
|
"uniStatistics" : {
|
||||||
"version" : "2"
|
"version" : "2"
|
||||||
},
|
},
|
||||||
"sassImplementationName" : "node-sass"
|
"sassImplementationName" : "dart-sass"
|
||||||
}
|
}
|
||||||
|
|||||||
4
node_modules/jweixin-module/README.md
generated
vendored
4
node_modules/jweixin-module/README.md
generated
vendored
@@ -19,8 +19,8 @@ https://unpkg.com/jweixin-module/out/index.js
|
|||||||
## 使用
|
## 使用
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var wx = require('jweixin-module')
|
var jweixin = require('jweixin-module')
|
||||||
wx.ready(function(){
|
jweixin.ready(function(){
|
||||||
// TODO
|
// TODO
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|||||||
1
node_modules/jweixin-module/out/index.js
generated
vendored
1
node_modules/jweixin-module/out/index.js
generated
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user