- 新增智慧教务系统插件,包含管理员和前端页面 - 实现了 hello world 功能作为插件示例 - 添加了插件安装、卸载和升级方法 - 创建了插件相关的路由、API 和页面组件master
@ -0,0 +1,7 @@ |
|||||
|
|
||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
/***************************************************** hello world ****************************************************/ |
||||
|
export function getHelloWorld() { |
||||
|
return request.get(`zhjw/hello_world`) |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
{} |
||||
@ -0,0 +1,17 @@ |
|||||
|
<template> |
||||
|
<span class="text-[20px]">{{hello_world_text}}</span> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { ref } from 'vue' |
||||
|
import { getHelloWorld } from '@/addon/zhjw/api/hello_world' |
||||
|
|
||||
|
const hello_world_text = ref(''); |
||||
|
const getHelloWorldInfo = async () => { |
||||
|
hello_world_text.value = await (await getHelloWorld()).data |
||||
|
} |
||||
|
getHelloWorldInfo() |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
</style> |
||||
@ -0,0 +1,36 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace addon\zhjw; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 插件安装之后单独的插件方法 |
||||
|
*/ |
||||
|
class Addon |
||||
|
{ |
||||
|
/** |
||||
|
* 插件安装执行 |
||||
|
*/ |
||||
|
public function install() |
||||
|
{ |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 插件卸载执行 |
||||
|
*/ |
||||
|
public function uninstall() |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 插件升级执行 |
||||
|
*/ |
||||
|
public function upgrade() |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
|
||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
/***************************************************** hello world ****************************************************/ |
||||
|
export function getHelloWorld() { |
||||
|
return request.get(`zhjw/hello_world`) |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
{} |
||||
@ -0,0 +1,17 @@ |
|||||
|
<template> |
||||
|
<span class="text-[20px]">{{hello_world_text}}</span> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { ref } from 'vue' |
||||
|
import { getHelloWorld } from '@/addon/zhjw/api/hello_world' |
||||
|
|
||||
|
const hello_world_text = ref(''); |
||||
|
const getHelloWorldInfo = async () => { |
||||
|
hello_world_text.value = await (await getHelloWorld()).data |
||||
|
} |
||||
|
getHelloWorldInfo() |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
</style> |
||||
@ -0,0 +1,29 @@ |
|||||
|
<?php |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | Niucloud-admin 企业快速开发的多应用管理平台 |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | 官方网址:https://www.niucloud.com |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | niucloud团队 版权所有 开源版本可自由商用 |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | Author: Niucloud Team |
||||
|
// +---------------------------------------------------------------------- |
||||
|
|
||||
|
namespace addon\zhjw\app\adminapi\controller\hello_world; |
||||
|
|
||||
|
use core\base\BaseAdminController; |
||||
|
use think\Response; |
||||
|
|
||||
|
class Index extends BaseAdminController |
||||
|
{ |
||||
|
/** |
||||
|
* Hello World |
||||
|
* @return Response |
||||
|
*/ |
||||
|
public function index() |
||||
|
{ |
||||
|
return success('SUCCESS', 'Hello World'); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?php |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | Niucloud-admin 企业快速开发的多应用管理平台 |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | 官方网址:https://www.niucloud.com |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | niucloud团队 版权所有 开源版本可自由商用 |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | Author: Niucloud Team |
||||
|
// +---------------------------------------------------------------------- |
||||
|
|
||||
|
use think\facade\Route; |
||||
|
|
||||
|
use app\adminapi\middleware\AdminCheckRole; |
||||
|
use app\adminapi\middleware\AdminCheckToken; |
||||
|
use app\adminapi\middleware\AdminLog; |
||||
|
|
||||
|
/** |
||||
|
* 智慧教务系统 |
||||
|
*/ |
||||
|
Route::group('zhjw', function () { |
||||
|
|
||||
|
/***************************************************** hello world ****************************************************/ |
||||
|
Route::get('hello_world', 'addon\zhjw\app\adminapi\controller\hello_world\Index@index'); |
||||
|
|
||||
|
})->middleware([ |
||||
|
AdminCheckToken::class, |
||||
|
AdminCheckRole::class, |
||||
|
AdminLog::class |
||||
|
]); |
||||
@ -0,0 +1,27 @@ |
|||||
|
<?php |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | Niucloud-admin 企业快速开发的多应用管理平台 |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | 官方网址:https://www.niucloud.com |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | niucloud团队 版权所有 开源版本可自由商用 |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | Author: Niucloud Team |
||||
|
// +---------------------------------------------------------------------- |
||||
|
|
||||
|
namespace addon\zhjw\app\api\controller\hello_world; |
||||
|
|
||||
|
use core\base\BaseApiController; |
||||
|
use think\Response; |
||||
|
|
||||
|
class Index extends BaseApiController |
||||
|
{ |
||||
|
/** |
||||
|
* Hello World |
||||
|
* @return Response |
||||
|
*/ |
||||
|
public function index() |
||||
|
{ |
||||
|
return success('SUCCESS', 'Hello World'); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
<?php |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | Niucloud-admin 企业快速开发的多应用管理平台 |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | 官方网址:https://www.niucloud.com |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | niucloud团队 版权所有 开源版本可自由商用 |
||||
|
// +---------------------------------------------------------------------- |
||||
|
// | Author: Niucloud Team |
||||
|
// +---------------------------------------------------------------------- |
||||
|
|
||||
|
use app\api\middleware\ApiCheckToken; |
||||
|
use app\api\middleware\ApiLog; |
||||
|
use app\api\middleware\ApiChannel; |
||||
|
use think\facade\Route; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 智慧教务系统 |
||||
|
*/ |
||||
|
Route::group('zhjw', function() { |
||||
|
/***************************************************** hello world ****************************************************/ |
||||
|
Route::get('hello_world', 'addon\zhjw\app\api\controller\hello_world\Index@index'); |
||||
|
|
||||
|
})->middleware(ApiChannel::class) |
||||
|
->middleware(ApiCheckToken::class, false) //false表示不验证登录 |
||||
|
->middleware(ApiLog::class); |
||||
|
|
||||
|
|
||||
|
|
||||
|
Route::group('zhjw', function() { |
||||
|
|
||||
|
})->middleware(ApiChannel::class) |
||||
|
->middleware(ApiCheckToken::class, true) //表示验证登录 |
||||
|
->middleware(ApiLog::class); |
||||
|
|
||||
@ -0,0 +1,33 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
[ |
||||
|
'menu_name' => '智慧教务系统', |
||||
|
'menu_key' => 'zhjw', |
||||
|
'menu_type' => 0, |
||||
|
'icon' => '', |
||||
|
'api_url' => '', |
||||
|
'router_path' => '', |
||||
|
'view_path' => '', |
||||
|
'methods' => '', |
||||
|
'sort' => 100, |
||||
|
'status' => 1, |
||||
|
'is_show' => 1, |
||||
|
'children' => [ |
||||
|
[ |
||||
|
'menu_name' => '智慧教务系统', |
||||
|
'menu_key' => 'zhjw_hello_world', |
||||
|
'menu_type' => 1, |
||||
|
'icon' => '', |
||||
|
'api_url' => 'zhjw/hello_world', |
||||
|
'router_path' => 'zhjw/hello_world', |
||||
|
'view_path' => 'hello_world/index', |
||||
|
'methods' => 'get', |
||||
|
'sort' => 100, |
||||
|
'status' => 1, |
||||
|
'is_show' => 1, |
||||
|
'children' => [] |
||||
|
], |
||||
|
] |
||||
|
] |
||||
|
]; |
||||
@ -0,0 +1,12 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
'bind' => [ |
||||
|
|
||||
|
], |
||||
|
'listen' => [ |
||||
|
|
||||
|
], |
||||
|
'subscribe' => [ |
||||
|
], |
||||
|
]; |
||||
@ -0,0 +1,4 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
]; |
||||
@ -0,0 +1,4 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
]; |
||||
@ -0,0 +1,4 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
]; |
||||
@ -0,0 +1,4 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
]; |
||||
@ -0,0 +1,4 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
]; |
||||
@ -0,0 +1,4 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
]; |
||||
@ -0,0 +1,11 @@ |
|||||
|
{ |
||||
|
"title": "智慧教务系统", |
||||
|
"desc": "智慧教务系统", |
||||
|
"key": "zhjw", |
||||
|
"version": "1.0.0", |
||||
|
"author": "836164388@qq.com", |
||||
|
"type": "app", |
||||
|
"support_app": "", |
||||
|
"compile":[], |
||||
|
"support_version": "1.5.1" |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
<?php |
||||
|
return [ |
||||
|
'pages' => <<<EOT |
||||
|
// PAGE_BEGIN |
||||
|
// *********************************** {{addon_name}} *********************************** |
||||
|
{ |
||||
|
"root": "addon/{{addon_name}}", |
||||
|
"pages": [ |
||||
|
{ |
||||
|
"path": "pages/hello_world/index", |
||||
|
"style": { |
||||
|
"navigationBarTitleText": "%{{addon_name}}.pages.hello_world.index%" |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
// PAGE_END |
||||
|
EOT |
||||
|
]; |
||||
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
@ -0,0 +1,8 @@ |
|||||
|
|
||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
/***************************************************** hello world ****************************************************/ |
||||
|
export function getHelloWorld() { |
||||
|
return request.get(`zhjw/hello_world`) |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,2 @@ |
|||||
|
{} |
||||
|
|
||||
@ -0,0 +1,2 @@ |
|||||
|
{} |
||||
|
|
||||
@ -0,0 +1,2 @@ |
|||||
|
{} |
||||
|
|
||||
@ -0,0 +1,18 @@ |
|||||
|
<template> |
||||
|
<text class="text-[20px]">{{helloWorld}}</text> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { ref } from 'vue'; |
||||
|
import { getHelloWorld } from '@/addon/zhjw/api/hello_world' |
||||
|
import { onLoad } from '@dcloudio/uni-app' |
||||
|
let helloWorld = ref(''); |
||||
|
onLoad(() => { |
||||
|
getHelloWorld().then((res) => { |
||||
|
helloWorld.value = res.data |
||||
|
}) |
||||
|
}) |
||||
|
</script> |
||||
|
<style lang="scss" scoped> |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,8 @@ |
|||||
|
|
||||
|
/** |
||||
|
* hello world |
||||
|
*/ |
||||
|
export function getHelloWorld() { |
||||
|
return request.get('zhjw/hello_world') |
||||
|
} |
||||
|
|
||||
@ -0,0 +1 @@ |
|||||
|
{} |
||||
@ -0,0 +1,9 @@ |
|||||
|
{ |
||||
|
"pages": { |
||||
|
"zhjw": { |
||||
|
"hello_world": { |
||||
|
"index": "hello_world" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
<template> |
||||
|
<span class="text-[24px]">{{hello_world_text}}</span> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { ref } from 'vue' |
||||
|
import { getHelloWorld } from '@/addon/zhjw/api/hello_world' |
||||
|
|
||||
|
const hello_world_text = ref(''); |
||||
|
|
||||
|
getHelloWorld().then(res => { |
||||
|
hello_world_text.value = res.data; |
||||
|
}) |
||||
|
</script> |
||||
|
<style lang="scss" scoped></style> |
||||
@ -0,0 +1,6 @@ |
|||||
|
export default [ |
||||
|
{ |
||||
|
path: "/zhjw/hello_world/index", |
||||
|
component: () => import('~/addon/zhjw/pages/hello_world/index.vue') |
||||
|
} |
||||
|
] |
||||
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
@ -0,0 +1,4 @@ |
|||||
|
<?php |
||||
|
//000000000000 |
||||
|
exit();?> |
||||
|
-1 |
||||
@ -0,0 +1,4 @@ |
|||||
|
<?php |
||||
|
//000000000000 |
||||
|
exit();?> |
||||
|
a:6:{s:2:"id";i:2;s:10:"config_key";s:23:"START_UP_PAGE_DIY_INDEX";s:5:"value";a:6:{s:4:"type";s:9:"DIY_INDEX";s:4:"name";s:10:"SHOP_INDEX";s:6:"parent";s:9:"SHOP_LINK";s:4:"page";s:23:"/addon/shop/pages/index";s:5:"title";s:24:"dict_diy.shop_link_index";s:6:"action";s:8:"decorate";}s:6:"status";i:1;s:11:"create_time";s:19:"2025-03-04 09:13:36";s:11:"update_time";s:19:"2025-03-04 09:13:36";} |
||||
@ -1,4 +0,0 @@ |
|||||
<?php |
|
||||
//000000000000 |
|
||||
exit();?> |
|
||||
a:3:{i:0;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\8d\4b55ce338f9b3a100a33ac8e8243e8.php";i:1;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\20\336a007c8134208722cf4e14270f07.php";i:2;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\32\0a87fb2f5a7e9725055a2c99bd1e51.php";} |
|
||||
@ -0,0 +1,4 @@ |
|||||
|
<?php |
||||
|
//000000000300 |
||||
|
exit();?> |
||||
|
a:2:{s:9:"secretKey";s:16:"hfk4kwq8r6p7ucrq";s:5:"point";O:27:"Fastknife\Domain\Vo\PointVo":2:{s:1:"x";i:179;s:1:"y";i:5;}} |
||||
@ -0,0 +1,4 @@ |
|||||
|
<?php |
||||
|
//000000000300 |
||||
|
exit();?> |
||||
|
a:2:{s:9:"secretKey";s:16:"tzex4hnus36vtgyn";s:5:"point";O:27:"Fastknife\Domain\Vo\PointVo":2:{s:1:"x";i:181;s:1:"y";i:5;}} |
||||
@ -1,4 +1,4 @@ |
|||||
<?php |
<?php |
||||
//000000000000 |
//000000000000 |
||||
exit();?> |
exit();?> |
||||
a:11:{i:0;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\8d\edc20f770c6500c52ce13d6f306b10.php";i:1;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\f0\8f735f0ec7f9eccc3e70aa516d941d.php";i:2;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\47\0f6e29f334d3244ee0749eb9b1a151.php";i:3;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\bd\359a5364051f6c84d65c787bf23f9a.php";i:4;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\f4\073ed8013d62bb2e0f768418925e4d.php";i:5;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\c8\a5c3cd9f43d503a892ec0eaeb9ab08.php";i:6;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\a0\81b1d0c6234fbc717b33719be823a2.php";i:7;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\95\228e6be1c6483f594d6d736cba9335.php";i:8;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\8f\7fdd2caa2302a943c6b21d4b077526.php";i:9;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\21\6e30a16639b283b1efa6b98838867b.php";i:10;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\85\c6bb2855c5978a0ab5483ed7646a3b.php";} |
a:16:{i:0;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\8d\edc20f770c6500c52ce13d6f306b10.php";i:1;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\f0\8f735f0ec7f9eccc3e70aa516d941d.php";i:2;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\47\0f6e29f334d3244ee0749eb9b1a151.php";i:3;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\bd\359a5364051f6c84d65c787bf23f9a.php";i:4;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\f4\073ed8013d62bb2e0f768418925e4d.php";i:5;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\c8\a5c3cd9f43d503a892ec0eaeb9ab08.php";i:6;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\a0\81b1d0c6234fbc717b33719be823a2.php";i:7;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\95\228e6be1c6483f594d6d736cba9335.php";i:8;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\8f\7fdd2caa2302a943c6b21d4b077526.php";i:9;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\21\6e30a16639b283b1efa6b98838867b.php";i:10;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\85\c6bb2855c5978a0ab5483ed7646a3b.php";i:11;s:121:"D:\DeveloperApp\phpstudy_pro\WWW\NiuCloud_ZhiHuiJiaoWu_Admin\niucloud\runtime\cache\01\c8e2ef1c2c504601d752e81c5f8f57.php";i:12;s:121:"D:\DeveloperApp\phpstudy_pro\WWW\NiuCloud_ZhiHuiJiaoWu_Admin\niucloud\runtime\cache\04\a77b15025b2c34a79ecfe786681739.php";i:13;s:121:"D:\DeveloperApp\phpstudy_pro\WWW\NiuCloud_ZhiHuiJiaoWu_Admin\niucloud\runtime\cache\ed\b6cf0da7ae6bc4e108cde29a092460.php";i:14;s:121:"D:\DeveloperApp\phpstudy_pro\WWW\NiuCloud_ZhiHuiJiaoWu_Admin\niucloud\runtime\cache\c0\2ea470914e2a1b4422c74fb53b3fb7.php";i:15;s:121:"D:\DeveloperApp\phpstudy_pro\WWW\NiuCloud_ZhiHuiJiaoWu_Admin\niucloud\runtime\cache\96\42cea4fc1e386bb972a5e5dec82fda.php";} |
||||
@ -1,4 +0,0 @@ |
|||||
<?php |
|
||||
//000000000000 |
|
||||
exit();?> |
|
||||
a:1:{i:0;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\11\96761b4a84f0e988872166216d02cd.php";} |
|
||||
@ -1,4 +1,4 @@ |
|||||
<?php |
<?php |
||||
//000000000000 |
//000000000000 |
||||
exit();?> |
exit();?> |
||||
a:3:{i:0;s:264:"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJpc3MiOiJzaG9wYWRtaW4uY2MiLCJhdWQiOiJzaG9wYWRtaW4uY2MiLCJpYXQiOjE3NDEwMDc3MDIsIm5iZiI6MTc0MTAwNzcwMiwiZXhwIjoxNzQxNjEyNTAyLCJqdGkiOiIxX2FkbWluIn0.ARwVjIYCdNUSwEmmm1KhrY1zKEWhRME0j26HM_GUNc4";i:1;s:264:"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJpc3MiOiJzaG9wYWRtaW4uY2MiLCJhdWQiOiJzaG9wYWRtaW4uY2MiLCJpYXQiOjE3NDEwNTA4MTYsIm5iZiI6MTc0MTA1MDgxNiwiZXhwIjoxNzQxNjU1NjE2LCJqdGkiOiIxX2FkbWluIn0.aZLpv4RBIlBCbnnqV9oXk9kC5AgdKHTUYiS3CCBSAqw";i:2;s:264:"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJpc3MiOiJzaG9wYWRtaW4uY2MiLCJhdWQiOiJzaG9wYWRtaW4uY2MiLCJpYXQiOjE3NDEwNTE0MTksIm5iZiI6MTc0MTA1MTQxOSwiZXhwIjoxNzQxNjU2MjE5LCJqdGkiOiIxX2FkbWluIn0.znLMCEfxdiRUIwiCBFah2d4cMlkJAazGbfAPU2C4bfo";} |
a:4:{i:0;s:264:"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJpc3MiOiJzaG9wYWRtaW4uY2MiLCJhdWQiOiJzaG9wYWRtaW4uY2MiLCJpYXQiOjE3NDEwMDc3MDIsIm5iZiI6MTc0MTAwNzcwMiwiZXhwIjoxNzQxNjEyNTAyLCJqdGkiOiIxX2FkbWluIn0.ARwVjIYCdNUSwEmmm1KhrY1zKEWhRME0j26HM_GUNc4";i:1;s:264:"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJpc3MiOiJzaG9wYWRtaW4uY2MiLCJhdWQiOiJzaG9wYWRtaW4uY2MiLCJpYXQiOjE3NDEwNTA4MTYsIm5iZiI6MTc0MTA1MDgxNiwiZXhwIjoxNzQxNjU1NjE2LCJqdGkiOiIxX2FkbWluIn0.aZLpv4RBIlBCbnnqV9oXk9kC5AgdKHTUYiS3CCBSAqw";i:2;s:264:"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJpc3MiOiJzaG9wYWRtaW4uY2MiLCJhdWQiOiJzaG9wYWRtaW4uY2MiLCJpYXQiOjE3NDEwNTE0MTksIm5iZiI6MTc0MTA1MTQxOSwiZXhwIjoxNzQxNjU2MjE5LCJqdGkiOiIxX2FkbWluIn0.znLMCEfxdiRUIwiCBFah2d4cMlkJAazGbfAPU2C4bfo";i:3;s:251:"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJpc3MiOiJ6aGp3LmNjIiwiYXVkIjoiemhqdy5jYyIsImlhdCI6MTc0MTE1NjA1NSwibmJmIjoxNzQxMTU2MDU1LCJleHAiOjE3NDE3NjA4NTUsImp0aSI6IjFfYWRtaW4ifQ.Wt0Rfttva_ld-or-b7Ya5UI6TIJsTpu7ZyBIjXNJAsw";} |
||||
@ -0,0 +1,4 @@ |
|||||
|
<?php |
||||
|
//000000000000 |
||||
|
exit();?> |
||||
|
-1 |
||||
@ -1,4 +1,4 @@ |
|||||
<?php |
<?php |
||||
//000000000000 |
//000000000000 |
||||
exit();?> |
exit();?> |
||||
a:1:{i:0;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\8d\fdacbfbb7ae58ba988054e909e2307.php";} |
a:2:{i:0;s:88:"D:\phpstudy_pro\WWW\niushop\niucloud\runtime\cache\8d\fdacbfbb7ae58ba988054e909e2307.php";i:1;s:121:"D:\DeveloperApp\phpstudy_pro\WWW\NiuCloud_ZhiHuiJiaoWu_Admin\niucloud\runtime\cache\8d\fdacbfbb7ae58ba988054e909e2307.php";} |
||||
@ -0,0 +1,4 @@ |
|||||
|
<?php |
||||
|
//000000000000 |
||||
|
exit();?> |
||||
|
-1 |
||||
@ -0,0 +1,4 @@ |
|||||
|
<?php |
||||
|
//000000000000 |
||||
|
exit();?> |
||||
|
-1 |
||||
@ -0,0 +1,3 @@ |
|||||
|
{ |
||||
|
"presets": ["@babel/preset-env"] |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
NODE_ENV = 'development' |
||||
|
|
||||
|
# api请求地址 |
||||
|
VITE_APP_BASE_URL='' |
||||
|
|
||||
|
# 图片服务器地址 |
||||
|
VITE_IMG_DOMAIN='' |
||||
|
|
||||
|
# 本地存储时token的参数名 |
||||
|
VITE_REQUEST_STORAGE_TOKEN_KEY='wapToken' |
||||
|
|
||||
|
# 请求时header中token的参数名 |
||||
|
VITE_REQUEST_HEADER_TOKEN_KEY='token' |
||||
|
|
||||
|
# 请求时header中来源场景的参数名 |
||||
|
VITE_REQUEST_HEADER_CHANNEL_KEY='channel' |
||||
|
|
||||
|
# 应用版本 |
||||
|
VITE_APP_VERSION='1.0.1' |
||||
@ -0,0 +1,19 @@ |
|||||
|
NODE_ENV = 'production' |
||||
|
|
||||
|
# api请求地址 |
||||
|
VITE_APP_BASE_URL='' |
||||
|
|
||||
|
# 图片服务器地址 |
||||
|
VITE_IMG_DOMAIN='' |
||||
|
|
||||
|
# 本地存储时token的参数名 |
||||
|
VITE_REQUEST_STORAGE_TOKEN_KEY='wapToken' |
||||
|
|
||||
|
# 请求时header中token的参数名 |
||||
|
VITE_REQUEST_HEADER_TOKEN_KEY='token' |
||||
|
|
||||
|
# 请求时header中来源场景的参数名 |
||||
|
VITE_REQUEST_HEADER_CHANNEL_KEY='channel' |
||||
|
|
||||
|
# 应用版本 |
||||
|
VITE_APP_VERSION='1.0.1' |
||||
@ -0,0 +1,21 @@ |
|||||
|
# Logs |
||||
|
logs |
||||
|
*.log |
||||
|
npm-debug.log* |
||||
|
yarn-debug.log* |
||||
|
yarn-error.log* |
||||
|
pnpm-debug.log* |
||||
|
lerna-debug.log* |
||||
|
|
||||
|
node_modules |
||||
|
.DS_Store |
||||
|
dist |
||||
|
*.local |
||||
|
|
||||
|
# Editor directories and files |
||||
|
.idea |
||||
|
*.suo |
||||
|
*.ntvs* |
||||
|
*.njsproj |
||||
|
*.sln |
||||
|
*.sw? |
||||
@ -0,0 +1,20 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="zh-cn"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8" /> |
||||
|
<script> |
||||
|
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || |
||||
|
CSS.supports('top: constant(a)')) |
||||
|
document.write( |
||||
|
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + |
||||
|
(coverSupport ? ', viewport-fit=cover' : '') + '" />') |
||||
|
</script> |
||||
|
<title></title> |
||||
|
<!--preload-links--> |
||||
|
<!--app-context--> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div id="app"><!--app-html--></div> |
||||
|
<script type="module" src="/src/main.js"></script> |
||||
|
</body> |
||||
|
</html> |
||||
@ -0,0 +1,86 @@ |
|||||
|
{ |
||||
|
"name": "uni-preset-vue", |
||||
|
"version": "0.0.0", |
||||
|
"scripts": { |
||||
|
"dev:app": "uni -p app", |
||||
|
"dev:app-android": "uni -p app-android", |
||||
|
"dev:app-ios": "uni -p app-ios", |
||||
|
"dev:custom": "uni -p", |
||||
|
"dev:h5": "uni", |
||||
|
"dev:h5:ssr": "uni --ssr", |
||||
|
"dev:mp-alipay": "uni -p mp-alipay", |
||||
|
"dev:mp-baidu": "uni -p mp-baidu", |
||||
|
"dev:mp-jd": "uni -p mp-jd", |
||||
|
"dev:mp-kuaishou": "uni -p mp-kuaishou", |
||||
|
"dev:mp-lark": "uni -p mp-lark", |
||||
|
"dev:mp-qq": "uni -p mp-qq", |
||||
|
"dev:mp-toutiao": "uni -p mp-toutiao", |
||||
|
"dev:mp-weixin": "node publish.cjs mp-weixin dev", |
||||
|
"dev:niu-mp-weixin": "uni -p mp-weixin", |
||||
|
"dev:quickapp-webview": "uni -p quickapp-webview", |
||||
|
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei", |
||||
|
"dev:quickapp-webview-union": "uni -p quickapp-webview-union", |
||||
|
"build:app": "uni build -p app", |
||||
|
"build:app-android": "uni build -p app-android", |
||||
|
"build:app-ios": "uni build -p app-ios", |
||||
|
"build:custom": "uni build -p", |
||||
|
"build:h5": "uni build && node publish.cjs h5 build", |
||||
|
"build:h5:ssr": "uni build --ssr", |
||||
|
"build:mp-alipay": "uni build -p mp-alipay", |
||||
|
"build:mp-baidu": "uni build -p mp-baidu", |
||||
|
"build:mp-jd": "uni build -p mp-jd", |
||||
|
"build:mp-kuaishou": "uni build -p mp-kuaishou", |
||||
|
"build:mp-lark": "uni build -p mp-lark", |
||||
|
"build:mp-qq": "uni build -p mp-qq", |
||||
|
"build:mp-toutiao": "uni build -p mp-toutiao", |
||||
|
"build:mp-weixin": "uni build -p mp-weixin && node publish.cjs mp-weixin build", |
||||
|
"build:quickapp-webview": "uni build -p quickapp-webview", |
||||
|
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei", |
||||
|
"build:quickapp-webview-union": "uni build -p quickapp-webview-union", |
||||
|
"type-check": "vue-tsc --noEmit" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"@dcloudio/uni-app": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/uni-app-plus": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/uni-components": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/uni-h5": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/uni-mp-alipay": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/uni-mp-baidu": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/uni-mp-jd": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/uni-mp-kuaishou": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/uni-mp-lark": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/uni-mp-qq": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/uni-mp-toutiao": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/uni-mp-weixin": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/uni-quickapp-webview": "3.0.0-3080720230703001", |
||||
|
"html2canvas": "^1.4.1", |
||||
|
"image-tools": "^1.4.0", |
||||
|
"lodash-es": "^4.17.21", |
||||
|
"pinia": "2.0.36", |
||||
|
"qrcode": "^1.5.1", |
||||
|
"qs": "6.7.0", |
||||
|
"sortablejs": "^1.15.0", |
||||
|
"uview-plus": "^3.1.29", |
||||
|
"vue": "^3.3.0", |
||||
|
"vue-i18n": "^9.2.2", |
||||
|
"weixin-js-sdk": "^1.6.5" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"@dcasia/mini-program-tailwind-webpack-plugin": "^1.5.6", |
||||
|
"@dcloudio/types": "^3.3.2", |
||||
|
"@dcloudio/uni-automator": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/uni-cli-shared": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/uni-stacktracey": "3.0.0-3080720230703001", |
||||
|
"@dcloudio/vite-plugin-uni": "3.0.0-3080720230703001", |
||||
|
"@rollup/plugin-commonjs": "^24.0.1", |
||||
|
"@types/qrcode": "^1.5.0", |
||||
|
"@types/sortablejs": "^1.15.0", |
||||
|
"@vue/tsconfig": "^0.1.3", |
||||
|
"sass": "^1.54.5", |
||||
|
"typescript": "^4.9.4", |
||||
|
"vite": "4.0.4", |
||||
|
"vite-plugin-windicss": "^1.8.10", |
||||
|
"vue-tsc": "^1.0.24", |
||||
|
"windicss": "^3.5.6" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,120 @@ |
|||||
|
const fs = require('fs') |
||||
|
const { spawn } = require('child_process'); |
||||
|
const path = require('path'); |
||||
|
|
||||
|
const main = () => { |
||||
|
const params = process.argv.slice(2) || [] |
||||
|
const port = params[0] || '' |
||||
|
const mode = params[1] || '' |
||||
|
|
||||
|
switch (port) { |
||||
|
case 'h5': |
||||
|
publish() |
||||
|
break; |
||||
|
case 'mp-weixin': |
||||
|
if (mode == 'build') { |
||||
|
handleWeappAddonComponents(mode) |
||||
|
handleWeappLanguage(mode) |
||||
|
} else if (mode == 'dev') { |
||||
|
listenWeappRunDev() |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const publish = () => { |
||||
|
const src = './dist/build/h5' |
||||
|
const dest = '../niucloud/public/wap' |
||||
|
|
||||
|
solve() |
||||
|
|
||||
|
// 目标目录不存在停止复制 |
||||
|
try { |
||||
|
const dir = fs.readdirSync(dest) |
||||
|
} catch (e) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 删除目标目录下文件 |
||||
|
fs.rm(dest, { recursive: true }, err => { |
||||
|
if(err) { |
||||
|
console.log(err) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
fs.cp(src, dest, { recursive: true }, (err) => { |
||||
|
if (err) { |
||||
|
console.error(err) |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const solve = () => { |
||||
|
const src = './dist/build/h5/assets' |
||||
|
const filemaps = fs.readdirSync(src) |
||||
|
|
||||
|
filemaps.forEach(file => { |
||||
|
if (/^(index-)(\w{8})(.js)$/.test(file)) { |
||||
|
const path = `${src}/${file}` |
||||
|
let content = fs.readFileSync(path, 'utf-8') |
||||
|
const first = 'const match = location.href.match(/\\/wap\\/(\\d*)\\//);' |
||||
|
|
||||
|
if (content.indexOf(first) == -1) { |
||||
|
content = first + content |
||||
|
const replace = 'router:{mode:"history",base: match ? `/wap/${match[1]}/` : "/wap/",assets:"assets",routerBase: match ? `/wap/${match[1]}/` : "/wap/"},darkmode' |
||||
|
content = content.replace(/router:{(.*?)},darkmode/s, replace) |
||||
|
fs.writeFileSync(path, content, 'utf8') |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const handleWeappAddonComponents = (mode) => { |
||||
|
const src = `./dist/${mode}/mp-weixin/addon/components/diy/group/index.json` |
||||
|
|
||||
|
try { |
||||
|
const data = JSON.parse(fs.readFileSync(src, 'utf8')); |
||||
|
data.componentPlaceholder = {}; |
||||
|
|
||||
|
Object.keys(data.usingComponents).map(key => { |
||||
|
data.componentPlaceholder[key] = "view"; |
||||
|
}) |
||||
|
fs.writeFileSync(src, JSON.stringify(data)) |
||||
|
} catch (err) { |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const handleWeappLanguage = (mode) => { |
||||
|
const src = `./dist/${mode}/mp-weixin/locale/language.js` |
||||
|
|
||||
|
try { |
||||
|
let content = fs.readFileSync(src, 'utf8'); |
||||
|
content = content.replace(/Promise\.resolve\(require\(("[^"]+")\)\)/g, 'require.async($1)') |
||||
|
fs.writeFileSync(src, content) |
||||
|
} catch (err) { |
||||
|
console.log(err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const listenWeappRunDev = () => { |
||||
|
const devProcess = spawn('npm', ['run', 'dev:niu-mp-weixin'], { |
||||
|
stdio: ['pipe', 'pipe', 'pipe'], |
||||
|
shell: true |
||||
|
}); |
||||
|
|
||||
|
let serverReady = false; |
||||
|
|
||||
|
// 监听 stdout 输出 |
||||
|
devProcess.stdout.on('data', (data) => { |
||||
|
const message = data.toString(); |
||||
|
console.log(message) |
||||
|
if (!serverReady && message.includes('DONE Build complete')) { |
||||
|
serverReady = true; |
||||
|
handleWeappAddonComponents('dev') |
||||
|
handleWeappLanguage('dev') |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
main() |
||||
@ -0,0 +1,205 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app' |
||||
|
import { launchInterceptor } from '@/utils/interceptor' |
||||
|
import { getToken, isWeixinBrowser, currRoute, deepClone, setThemeColor } from '@/utils/common' |
||||
|
import useMemberStore from '@/stores/member' |
||||
|
import useConfigStore from '@/stores/config' |
||||
|
import useSystemStore from '@/stores/system' |
||||
|
import { useLogin } from '@/hooks/useLogin' |
||||
|
import { useShare } from '@/hooks/useShare' |
||||
|
|
||||
|
onLaunch((data: any) => { |
||||
|
|
||||
|
// 添加初始化拦截器 |
||||
|
launchInterceptor() |
||||
|
|
||||
|
// #ifdef H5 |
||||
|
uni.getSystemInfoSync().platform == 'ios' && (uni.setStorageSync('initUrl', location.href)) |
||||
|
|
||||
|
// 传输给后台数据 |
||||
|
window.parent.postMessage(JSON.stringify({ |
||||
|
type: 'appOnLaunch', |
||||
|
message: '初始化加载完成' |
||||
|
}), '*'); |
||||
|
|
||||
|
// 监听父页面发来的消息 |
||||
|
window.addEventListener('message', event => { |
||||
|
try { |
||||
|
let data = { |
||||
|
type: '' |
||||
|
}; |
||||
|
if (typeof event.data == 'string') { |
||||
|
data = JSON.parse(event.data) |
||||
|
} else if (typeof event.data == 'object') { |
||||
|
data = event.data |
||||
|
} |
||||
|
if (data.type && data.type == 'appOnReady') { |
||||
|
window.parent.postMessage(JSON.stringify({ |
||||
|
type: 'appOnReady', |
||||
|
message: '加载完成' |
||||
|
}), '*'); |
||||
|
} |
||||
|
} catch (e) { |
||||
|
console.log('uni-app App.vue 接受数据错误', e) |
||||
|
} |
||||
|
}, false); |
||||
|
|
||||
|
const { wechatInit } = useShare() |
||||
|
wechatInit() |
||||
|
// #endif |
||||
|
|
||||
|
// #ifdef MP |
||||
|
const updateManager = uni.getUpdateManager(); |
||||
|
updateManager.onCheckForUpdate(function (res) { |
||||
|
// 请求完新版本信息的回调 |
||||
|
}); |
||||
|
|
||||
|
updateManager.onUpdateReady(function (res) { |
||||
|
uni.showModal({ |
||||
|
title: '更新提示', |
||||
|
content: '新版本已经准备好,是否重启应用?', |
||||
|
success(res) { |
||||
|
if (res.confirm) { |
||||
|
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启 |
||||
|
updateManager.applyUpdate(); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
updateManager.onUpdateFailed(function (res) { |
||||
|
// 新的版本下载失败 |
||||
|
}); |
||||
|
// #endif |
||||
|
|
||||
|
// 获取初始化数据信息 |
||||
|
useSystemStore().getInitFn(async() => { |
||||
|
|
||||
|
const configStore = useConfigStore() |
||||
|
|
||||
|
let loginConfig = uni.getStorageSync('login_config') |
||||
|
if (!loginConfig) { |
||||
|
loginConfig = deepClone(configStore.login) |
||||
|
} |
||||
|
|
||||
|
let url: any = currRoute() |
||||
|
// 设置主色调 |
||||
|
setThemeColor(url) |
||||
|
|
||||
|
// 判断在登录注册页面账号锁定后不进行请求三方登录注册 |
||||
|
if ((['app/pages/auth/index', 'app/pages/auth/login', 'app/pages/auth/register', 'app/pages/auth/resetpwd'].indexOf(url) != -1) && |
||||
|
(loginConfig.is_username || loginConfig.is_mobile || loginConfig.is_bind_mobile)) { |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
// 判断是否已登录 |
||||
|
if (getToken()) { |
||||
|
const memberStore: any = useMemberStore() |
||||
|
|
||||
|
await memberStore.setToken(getToken(), () => { |
||||
|
if (!uni.getStorageSync('openid')) { |
||||
|
const memberInfo = useMemberStore().info |
||||
|
const login = useLogin() |
||||
|
|
||||
|
// #ifdef MP-WEIXIN |
||||
|
if (memberInfo.mobile) uni.setStorageSync('wap_member_mobile', memberInfo.mobile) // 存储会员手机号,防止重复请求微信获取手机号接口 |
||||
|
if (memberInfo && memberInfo.weapp_openid) { |
||||
|
uni.setStorageSync('openid', memberInfo.weapp_openid) |
||||
|
} else { |
||||
|
login.getAuthCode({ updateFlag: true }) // 更新oppenid |
||||
|
} |
||||
|
// #endif |
||||
|
|
||||
|
// #ifdef H5 |
||||
|
if (isWeixinBrowser()) { |
||||
|
if (memberInfo && memberInfo.wx_openid) { |
||||
|
uni.setStorageSync('openid', memberInfo.wx_openid) |
||||
|
} else { |
||||
|
if (data.query.code) { |
||||
|
// 检测身份是否合法(当前登录的账号是不是我的),openid有效后才能更新登录 |
||||
|
login.updateOpenid(data.query.code, () => { |
||||
|
login.authLogin({ code: data.query.code }) |
||||
|
}) |
||||
|
} else { |
||||
|
if (loginConfig.is_force_access_user_info) { |
||||
|
// 强制获取用户信息 |
||||
|
login.getAuthCode({ scopes: 'snsapi_userinfo' }) |
||||
|
} else { |
||||
|
// 静默获取 |
||||
|
login.getAuthCode({ scopes: 'snsapi_base' }) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// #endif |
||||
|
|
||||
|
} |
||||
|
|
||||
|
// 开启强制绑定手机号 |
||||
|
if (uni.getStorageSync('isbindmobile')) { |
||||
|
uni.removeStorageSync('isbindmobile'); |
||||
|
} |
||||
|
|
||||
|
if (loginConfig.is_bind_mobile && !memberStore.info.mobile) { |
||||
|
// 强制绑定手机号 |
||||
|
uni.setStorageSync('isbindmobile', true) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
} |
||||
|
|
||||
|
if (!getToken()) { |
||||
|
|
||||
|
// #ifdef MP |
||||
|
// 小程序 会员退出后不会自动登录 |
||||
|
if (uni.getStorageSync('autoLoginLock')) return; |
||||
|
// #endif |
||||
|
|
||||
|
const login = useLogin() |
||||
|
|
||||
|
// #ifdef MP |
||||
|
// 判断是否开启第三方自动注册登录 |
||||
|
if (loginConfig.is_auth_register) { |
||||
|
// 第三方平台自动登录 |
||||
|
login.getAuthCode() |
||||
|
} |
||||
|
// #endif |
||||
|
|
||||
|
// #ifdef H5 |
||||
|
if (isWeixinBrowser()) { |
||||
|
if (uni.getStorageSync('autoLoginLock') && !uni.getStorageSync('wechat_login_back')) return; |
||||
|
// 开启自动注册的情况下才能执行 |
||||
|
if (loginConfig.is_auth_register || uni.getStorageSync('wechat_login_back')) { |
||||
|
uni.removeStorageSync('wechat_login_back') // 删除微信公众号手动授权登录回调标识 |
||||
|
if (data.query.code) { |
||||
|
login.authLogin({ code: data.query.code }) |
||||
|
} else { |
||||
|
if (loginConfig.is_force_access_user_info) { |
||||
|
// 强制获取用户信息 |
||||
|
login.getAuthCode({ scopes: 'snsapi_userinfo' }) |
||||
|
} else { |
||||
|
// 静默获取 |
||||
|
login.getAuthCode({ scopes: 'snsapi_base' }) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// #endif |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
}) |
||||
|
|
||||
|
onShow(() => { |
||||
|
|
||||
|
}) |
||||
|
|
||||
|
onHide(() => { |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
uni-page-head { |
||||
|
display: none !important; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,59 @@ |
|||||
|
<template> |
||||
|
<view :style="themeColor()"> |
||||
|
<view v-show="!loading" class="diy-template-wrap"> |
||||
|
<diy-group ref="diyGroupRef" :data="diyFormData" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
<script lang="ts" setup> |
||||
|
import { ref, reactive, onMounted } from 'vue'; |
||||
|
import diyGroup from '@/addon/components/diy/group/index.vue' |
||||
|
import { getFormRecord } from '@/app/api/diy_form'; |
||||
|
|
||||
|
const props = defineProps(['record_id','completeLayout']); |
||||
|
const emits = defineEmits(['callback']) |
||||
|
|
||||
|
const loading = ref(true); |
||||
|
|
||||
|
const diyFormData: any = reactive({ |
||||
|
global: {}, |
||||
|
value: [] |
||||
|
}) |
||||
|
|
||||
|
onMounted(() => { |
||||
|
getFormRecord({ |
||||
|
record_id: props.record_id |
||||
|
}).then((res: any) => { |
||||
|
diyFormData.global.completeLayout = props.completeLayout || 'style-1'; |
||||
|
if (res.data.recordsFieldList) { |
||||
|
|
||||
|
res.data.recordsFieldList.forEach((item: any) => { |
||||
|
let comp = { |
||||
|
id: item.field_key, |
||||
|
componentName: item.field_type, |
||||
|
pageStyle: '', |
||||
|
viewFormDetail: true, // 查看表单详情标识 |
||||
|
field: { |
||||
|
name: item.field_name, |
||||
|
value: item.handle_field_value, |
||||
|
required: item.field_required, |
||||
|
unique: item.field_unique, |
||||
|
privacyProtection: item.privacy_protection, |
||||
|
}, |
||||
|
margin: { |
||||
|
top: 0, |
||||
|
bottom: 0, |
||||
|
both: 0 |
||||
|
} |
||||
|
}; |
||||
|
diyFormData.value.push(comp); |
||||
|
}) |
||||
|
} |
||||
|
emits('callback', res.data.recordsFieldList) |
||||
|
loading.value = false; |
||||
|
}).catch(() => { |
||||
|
loading.value = false; |
||||
|
emits('callback', []) |
||||
|
}) |
||||
|
}) |
||||
|
</script> |
||||
@ -0,0 +1,191 @@ |
|||||
|
<template> |
||||
|
<view :style="themeColor()"> |
||||
|
<!-- 自定义组件渲染 --> |
||||
|
<view v-show="requestData.status == 1 && !diy.getLoading()" class="diy-template-wrap"> |
||||
|
<diy-group ref="diyGroupRef" :data="diyFormData" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
<script lang="ts" setup> |
||||
|
import { ref, reactive, computed, onMounted, watch } from 'vue'; |
||||
|
import { useDiyForm } from '@/hooks/useDiyForm' |
||||
|
import { deepClone,getValidTime } from '@/utils/common' |
||||
|
import diyGroup from '@/addon/components/diy/group/index.vue' |
||||
|
|
||||
|
const props = defineProps(['form_id', 'relate_id', 'storage_name', 'form_border']); |
||||
|
|
||||
|
const diy = useDiyForm({ |
||||
|
form_id: props.form_id, |
||||
|
needLogin: false // 不检测登录,调用业务自行处理 |
||||
|
}) |
||||
|
|
||||
|
const diyGroupRef = ref(null) |
||||
|
|
||||
|
const requestData = computed(() => { |
||||
|
return diy.requestData; |
||||
|
}) |
||||
|
|
||||
|
const diyFormData: any = reactive({}) |
||||
|
|
||||
|
onMounted(() => { |
||||
|
diy.getData(() => { |
||||
|
diyFormData.status = diy.data.status; |
||||
|
if (diyFormData.status) { |
||||
|
diyFormData.title = diy.data.title; |
||||
|
diyFormData.global = diy.data.global; |
||||
|
if (diyFormData.global) { |
||||
|
diyFormData.global.topStatusBar.isShow = false; // 顶部导航栏强制隐藏 |
||||
|
diyFormData.global.bottomTabBarSwitch = false; // 底部导航强制隐藏 |
||||
|
} |
||||
|
let value: any = []; |
||||
|
if(props.form_border == 'none'){ |
||||
|
diyFormData.global.borderControl = false; |
||||
|
} |
||||
|
// 需要过滤 组件类型,筛选出来表单,排除表单提交组件 |
||||
|
diy.data.value.forEach((item: any) => { |
||||
|
if (item.componentType == 'diy_form' && item.componentName != 'FormSubmit') { |
||||
|
value.push(item); |
||||
|
} |
||||
|
}) |
||||
|
diyFormData.value = value; |
||||
|
diyFormData.componentRefs = null; |
||||
|
diyGroupRef.value?.refresh(); |
||||
|
watchFormData(); |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
const watchFormData = () => { |
||||
|
watch( |
||||
|
() => diyFormData.value, |
||||
|
(newValue, oldValue) => { |
||||
|
if (newValue) { |
||||
|
let formData: any = { |
||||
|
validTime: getValidTime(5), // 缓存数据有效期为5分钟 |
||||
|
components: [] |
||||
|
}; |
||||
|
newValue.forEach((item: any) => { |
||||
|
// 只存表单组件 |
||||
|
if (item.componentType == 'diy_form' && item.componentName != 'FormSubmit') { |
||||
|
// 只存储表单数据,压缩存储空间 |
||||
|
let field = deepClone(item.field); |
||||
|
// 移除不需要存储的数据 |
||||
|
delete field.remark; // 字段说明 |
||||
|
delete field.detailComponent; // 用于详情展示 |
||||
|
delete field.default; // 默认值 |
||||
|
formData.components.push({ |
||||
|
id: item.id, |
||||
|
componentName: item.componentName, |
||||
|
componentType: item.componentType, |
||||
|
componentTitle: item.componentTitle, |
||||
|
isHidden: item.isHidden, |
||||
|
field: field |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
if (formData.components.length) { |
||||
|
uni.setStorageSync('diyFormStorage_' + props.form_id, formData) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
{ deep: true } |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
const verify = () => { |
||||
|
if(!diyFormData.status) return true; |
||||
|
if(!diyFormData.value) return true; |
||||
|
let allPass = true; // 是否全部通过验证 |
||||
|
|
||||
|
let componentRefs = diyGroupRef.value.getFormRef().componentRefs; |
||||
|
|
||||
|
// 需要过滤 组件类型,筛选出来表单 |
||||
|
for (let i = 0; i < diyFormData.value.length; i++) { |
||||
|
let item = diyFormData.value[i]; |
||||
|
if (item.field.required || item.field.value) { |
||||
|
let refKey = `diy${ item.componentName }Ref`; |
||||
|
let isBreak = false; |
||||
|
if (componentRefs[refKey]) { |
||||
|
for (let k = 0; k < componentRefs[refKey].length; k++) { |
||||
|
let compRef = componentRefs[refKey][k]; |
||||
|
let verify = compRef.verify(); // 验证表单组件数据 |
||||
|
if (verify && !verify.code) { |
||||
|
isBreak = true; |
||||
|
uni.showToast({ |
||||
|
title: verify.message, |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
if (isBreak) { |
||||
|
allPass = false; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!allPass) return false; |
||||
|
|
||||
|
const data = { |
||||
|
form_id: props.form_id, |
||||
|
value: uni.getStorageSync('diyFormStorage_' + props.form_id), |
||||
|
relate_id: props.relate_id || 0 // 关联业务id |
||||
|
} |
||||
|
if (props.storage_name) { |
||||
|
uni.setStorageSync(props.storage_name, data) |
||||
|
} |
||||
|
return allPass; |
||||
|
} |
||||
|
|
||||
|
// 获取数据 |
||||
|
const getData = ()=> { |
||||
|
return { |
||||
|
form_id: props.form_id, |
||||
|
value: diyFormData.value, |
||||
|
relate_id: props.relate_id || 0 // 关联业务id |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const clearStorage = (keys: any=[]) => { |
||||
|
uni.removeStorageSync('diyFormStorage_' + props.form_id) |
||||
|
if (props.storage_name) uni.removeStorageSync(props.storage_name) |
||||
|
if(keys) { |
||||
|
keys.forEach((key: any) => { |
||||
|
uni.removeStorageSync(key) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 监听页面隐藏 |
||||
|
diy.onHide(); |
||||
|
|
||||
|
// 监听页面卸载 |
||||
|
diy.onUnload(); |
||||
|
|
||||
|
// 监听滚动事件 |
||||
|
// diy.onPageScroll() |
||||
|
|
||||
|
defineExpose({ |
||||
|
verify, |
||||
|
getData, |
||||
|
clearStorage |
||||
|
}) |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
.diy-template-wrap { |
||||
|
/* #ifdef MP */ |
||||
|
.child-diy-template-wrap { |
||||
|
::v-deep .diy-group { |
||||
|
> .draggable-element.top-fixed-diy { |
||||
|
display: block !important; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
/* #endif */ |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,29 @@ |
|||||
|
.ignore-draggable-element, .draggable-element { |
||||
|
&.decorate { |
||||
|
&:hover:before { |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
border: 4rpx dotted $u-primary; |
||||
|
z-index: 10; |
||||
|
pointer-events: none; |
||||
|
cursor: move; |
||||
|
} |
||||
|
|
||||
|
&.selected:before { |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
border: 4rpx solid $u-primary; |
||||
|
z-index: 10; |
||||
|
pointer-events: none; |
||||
|
cursor: move; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,212 @@ |
|||||
|
<template> |
||||
|
<view class="diy-group" id="componentList"> |
||||
|
<top-tabbar :scrollBool="diyGroup.componentsScrollBool.TopTabbar" v-if="data.global && Object.keys(data.global).length && data.global.topStatusBar && data.global.topStatusBar.isShow" ref="topTabbarRef" :data="data.global" /> |
||||
|
<view v-for="(component, index) in data.value" :key="component.id" |
||||
|
@click="diyStore.changeCurrentIndex(index, component)" |
||||
|
:class="diyGroup.getComponentClass(index,component)" :style="component.pageStyle"> |
||||
|
<view class="relative" :style="{ marginTop : component.margin.top < 0 ? (component.margin.top * 2) + 'rpx' : '0' }"> |
||||
|
<!-- 装修模式下,设置负上边距后超出的内容,禁止选中设置 --> |
||||
|
<view v-if="diyGroup.isShowPlaceHolder(index,component)" class="absolute w-full z-1" :style="{ height : (component.margin.top * 2 * -1) + 'rpx' }" @click.stop="diyGroup.placeholderEvent"></view> |
||||
|
<template v-if="component.componentName == 'ActiveCube'"> |
||||
|
<diy-active-cube ref="diyActiveCubeRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.ActiveCube" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'CarouselSearch'"> |
||||
|
<diy-carousel-search ref="diyCarouselSearchRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.CarouselSearch" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FloatBtn'"> |
||||
|
<diy-float-btn ref="diyFloatBtnRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FloatBtn" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormAddress'"> |
||||
|
<diy-form-address ref="diyFormAddressRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormAddress" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormCheckbox'"> |
||||
|
<diy-form-checkbox ref="diyFormCheckboxRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormCheckbox" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormDate'"> |
||||
|
<diy-form-date ref="diyFormDateRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormDate" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormDateScope'"> |
||||
|
<diy-form-date-scope ref="diyFormDateScopeRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormDateScope" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormEmail'"> |
||||
|
<diy-form-email ref="diyFormEmailRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormEmail" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormFile'"> |
||||
|
<diy-form-file ref="diyFormFileRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormFile" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormIdentity'"> |
||||
|
<diy-form-identity ref="diyFormIdentityRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormIdentity" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormIdentityPrivacy'"> |
||||
|
<diy-form-identity-privacy ref="diyFormIdentityPrivacyRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormIdentityPrivacy" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormImage'"> |
||||
|
<diy-form-image ref="diyFormImageRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormImage" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormInput'"> |
||||
|
<diy-form-input ref="diyFormInputRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormInput" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormLocation'"> |
||||
|
<diy-form-location ref="diyFormLocationRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormLocation" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormMobile'"> |
||||
|
<diy-form-mobile ref="diyFormMobileRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormMobile" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormNumber'"> |
||||
|
<diy-form-number ref="diyFormNumberRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormNumber" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormPrivacy'"> |
||||
|
<diy-form-privacy ref="diyFormPrivacyRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormPrivacy" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormPrivacyPop'"> |
||||
|
<diy-form-privacy-pop ref="diyFormPrivacyPopRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormPrivacyPop" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormRadio'"> |
||||
|
<diy-form-radio ref="diyFormRadioRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormRadio" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormSubmit'"> |
||||
|
<diy-form-submit ref="diyFormSubmitRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormSubmit" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormTable'"> |
||||
|
<diy-form-table ref="diyFormTableRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormTable" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormTextarea'"> |
||||
|
<diy-form-textarea ref="diyFormTextareaRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormTextarea" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormTime'"> |
||||
|
<diy-form-time ref="diyFormTimeRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormTime" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormTimeScope'"> |
||||
|
<diy-form-time-scope ref="diyFormTimeScopeRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormTimeScope" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormVideo'"> |
||||
|
<diy-form-video ref="diyFormVideoRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormVideo" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'FormWechatName'"> |
||||
|
<diy-form-wechat-name ref="diyFormWechatNameRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.FormWechatName" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'GraphicNav'"> |
||||
|
<diy-graphic-nav ref="diyGraphicNavRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.GraphicNav" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'HorzBlank'"> |
||||
|
<diy-horz-blank ref="diyHorzBlankRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.HorzBlank" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'HorzLine'"> |
||||
|
<diy-horz-line ref="diyHorzLineRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.HorzLine" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'HotArea'"> |
||||
|
<diy-hot-area ref="diyHotAreaRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.HotArea" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'ImageAds'"> |
||||
|
<diy-image-ads ref="diyImageAdsRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.ImageAds" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'MemberInfo'"> |
||||
|
<diy-member-info ref="diyMemberInfoRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.MemberInfo" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'MemberLevel'"> |
||||
|
<diy-member-level ref="diyMemberLevelRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.MemberLevel" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'Notice'"> |
||||
|
<diy-notice ref="diyNoticeRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.Notice" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'PictureShow'"> |
||||
|
<diy-picture-show ref="diyPictureShowRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.PictureShow" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'RichText'"> |
||||
|
<diy-rich-text ref="diyRichTextRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.RichText" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'RubikCube'"> |
||||
|
<diy-rubik-cube ref="diyRubikCubeRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.RubikCube" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'Text'"> |
||||
|
<diy-text ref="diyTextRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.Text" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'GoodsCoupon'"> |
||||
|
<diy-goods-coupon ref="diyGoodsCouponRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.GoodsCoupon" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'GoodsList'"> |
||||
|
<diy-goods-list ref="diyGoodsListRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.GoodsList" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'ManyGoodsList'"> |
||||
|
<diy-many-goods-list ref="diyManyGoodsListRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.ManyGoodsList" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'ShopExchangeGoods'"> |
||||
|
<diy-shop-exchange-goods ref="diyShopExchangeGoodsRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.ShopExchangeGoods" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'ShopExchangeInfo'"> |
||||
|
<diy-shop-exchange-info ref="diyShopExchangeInfoRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.ShopExchangeInfo" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'ShopGoodsRanking'"> |
||||
|
<diy-shop-goods-ranking ref="diyShopGoodsRankingRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.ShopGoodsRanking" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'ShopGoodsRecommend'"> |
||||
|
<diy-shop-goods-recommend ref="diyShopGoodsRecommendRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.ShopGoodsRecommend" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'ShopMemberInfo'"> |
||||
|
<diy-shop-member-info ref="diyShopMemberInfoRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.ShopMemberInfo" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'ShopNewcomer'"> |
||||
|
<diy-shop-newcomer ref="diyShopNewcomerRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.ShopNewcomer" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'ShopOrderInfo'"> |
||||
|
<diy-shop-order-info ref="diyShopOrderInfoRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.ShopOrderInfo" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'ShopSearch'"> |
||||
|
<diy-shop-search ref="diyShopSearchRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.ShopSearch" /> |
||||
|
</template> |
||||
|
<template v-if="component.componentName == 'SingleRecommend'"> |
||||
|
<diy-single-recommend ref="diySingleRecommendRef" :component="component" :global="data.global" :index="index" :scrollBool="diyGroup.componentsScrollBool.SingleRecommend" /> |
||||
|
</template> |
||||
|
</view> |
||||
|
</view> |
||||
|
<template v-if="diyStore.mode == '' && data.global && data.global.bottomTabBarSwitch"> |
||||
|
<view class="pt-[20rpx]"></view> |
||||
|
<tabbar /> |
||||
|
</template> |
||||
|
</view> |
||||
|
</template> |
||||
|
<script lang="ts" setup> |
||||
|
import diyGoodsCoupon from '@/addon/shop/components/diy/goods-coupon/index.vue'; |
||||
|
import diyGoodsList from '@/addon/shop/components/diy/goods-list/index.vue'; |
||||
|
import diyManyGoodsList from '@/addon/shop/components/diy/many-goods-list/index.vue'; |
||||
|
import diyShopExchangeGoods from '@/addon/shop/components/diy/shop-exchange-goods/index.vue'; |
||||
|
import diyShopExchangeInfo from '@/addon/shop/components/diy/shop-exchange-info/index.vue'; |
||||
|
import diyShopGoodsRanking from '@/addon/shop/components/diy/shop-goods-ranking/index.vue'; |
||||
|
import diyShopGoodsRecommend from '@/addon/shop/components/diy/shop-goods-recommend/index.vue'; |
||||
|
import diyShopMemberInfo from '@/addon/shop/components/diy/shop-member-info/index.vue'; |
||||
|
import diyShopNewcomer from '@/addon/shop/components/diy/shop-newcomer/index.vue'; |
||||
|
import diyShopOrderInfo from '@/addon/shop/components/diy/shop-order-info/index.vue'; |
||||
|
import diyShopSearch from '@/addon/shop/components/diy/shop-search/index.vue'; |
||||
|
import diySingleRecommend from '@/addon/shop/components/diy/single-recommend/index.vue'; |
||||
|
import topTabbar from '@/components/top-tabbar/top-tabbar.vue' |
||||
|
import useDiyStore from '@/app/stores/diy'; |
||||
|
import { useDiyGroup } from './useDiyGroup'; |
||||
|
import { ref,getCurrentInstance } from 'vue'; |
||||
|
|
||||
|
const props = defineProps(['data']); |
||||
|
const instance: any = getCurrentInstance(); |
||||
|
const getFormRef = () => { |
||||
|
return { |
||||
|
componentRefs: instance.refs |
||||
|
} |
||||
|
} |
||||
|
const diyStore = useDiyStore(); |
||||
|
const diyGroup = useDiyGroup({ |
||||
|
...props, |
||||
|
getFormRef |
||||
|
}); |
||||
|
const data = ref(diyGroup.data); |
||||
|
|
||||
|
// 监听页面加载完成 |
||||
|
diyGroup.onMounted(); |
||||
|
|
||||
|
// 监听滚动事件 |
||||
|
diyGroup.onPageScroll(); |
||||
|
defineExpose({ |
||||
|
refresh: diyGroup.refresh, |
||||
|
getFormRef |
||||
|
}) |
||||
|
</script> |
||||
|
<style lang="scss" scoped> |
||||
|
@import './index.scss'; |
||||
|
</style> |
||||
@ -0,0 +1,185 @@ |
|||||
|
import { ref, onMounted, nextTick, computed } from 'vue'; |
||||
|
import Sortable from 'sortablejs'; |
||||
|
import { range } from 'lodash-es'; |
||||
|
import { onPageScroll, onHide, onShow } from '@dcloudio/uni-app'; |
||||
|
import useDiyStore from '@/app/stores/diy'; |
||||
|
|
||||
|
export function useDiyGroup(params: any = {}) { |
||||
|
|
||||
|
let scrollVal: any = ""; //组件滚动值集合
|
||||
|
const componentsScrollBool: any = ref({}); //组件是否根据滚动进行相应改变
|
||||
|
const diyStore = useDiyStore(); |
||||
|
|
||||
|
const positionFixed = ref(['fixed', 'top_fixed', 'right_fixed', 'bottom_fixed', 'left_fixed']); |
||||
|
|
||||
|
const data = computed(() => { |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
return diyStore; |
||||
|
} else { |
||||
|
return params.data; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const getComponentClass = (index: any, component: any) => { |
||||
|
let obj: any = { |
||||
|
relative: true, |
||||
|
selected: diyStore.currentIndex == index, |
||||
|
decorate: diyStore.mode == 'decorate' |
||||
|
} |
||||
|
obj['top-fixed-' + diyStore.topFixedStatus] = true; |
||||
|
|
||||
|
if (component.position && positionFixed.value.indexOf(component.position) != -1) { |
||||
|
// 找出置顶组件,设置禁止拖动
|
||||
|
obj['ignore-draggable-element'] = true; |
||||
|
} else { |
||||
|
obj['draggable-element'] = true; |
||||
|
} |
||||
|
if (component.componentName == 'ImageAds') { |
||||
|
obj['overflow-hidden'] = true |
||||
|
} |
||||
|
return obj; |
||||
|
} |
||||
|
|
||||
|
// 是否显示占位区域,用于禁止选中负上边距的内容
|
||||
|
const isShowPlaceHolder = (index: any, component: any) => { |
||||
|
// #ifdef H5
|
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
let el: any = document.getElementById('componentList'); |
||||
|
if (el && el.children.length && el.children[index]) { |
||||
|
let height = el.children[index].offsetHeight; |
||||
|
let top = 0; |
||||
|
if (component.margin.top < 0) { |
||||
|
top = component.margin.top * 2 * -1; |
||||
|
// 若负上边距大于组件的高度,则允许选中进行装修
|
||||
|
if (top > height) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
// #endif
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 监听页面加载完成
|
||||
|
const onMountedLifeCycle = () => { |
||||
|
onMounted(() => { |
||||
|
// #ifdef H5
|
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
var el: any = document.getElementById('componentList'); |
||||
|
const sortable = Sortable.create(el, { |
||||
|
draggable: '.draggable-element', |
||||
|
animation: 200, |
||||
|
// 结束拖拽
|
||||
|
onEnd: event => { |
||||
|
let temp = diyStore.value[event.oldIndex!]; |
||||
|
diyStore.value.splice(event.oldIndex!, 1); |
||||
|
diyStore.value.splice(event.newIndex!, 0, temp); |
||||
|
|
||||
|
nextTick(() => { |
||||
|
sortable.sort(range(diyStore.value.length).map((value: any) => { |
||||
|
return value.toString(); |
||||
|
})); |
||||
|
|
||||
|
diyStore.postMessage(event.newIndex, diyStore.value[event.newIndex]); |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
// #endif
|
||||
|
|
||||
|
nextTick(() => { |
||||
|
setTimeout(() => { |
||||
|
|
||||
|
// 初始化组件滚动值
|
||||
|
scrollVal = uni.getStorageSync('componentsScrollValGroup'); |
||||
|
if (scrollVal) { |
||||
|
for (let key in scrollVal) { |
||||
|
componentsScrollBool.value[key] = -1; |
||||
|
} |
||||
|
} |
||||
|
}, 500) |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// 页面onShow调用时,也会触发改方法
|
||||
|
const refresh = () => { |
||||
|
nextTick(() => { |
||||
|
let time: any = null; |
||||
|
let fn = () => { |
||||
|
diyStore.componentRefs = params.getFormRef().componentRefs; |
||||
|
data.value.componentRefs = params.getFormRef().componentRefs; |
||||
|
params.getFormRef().componentRefs.topTabbarRef?.refresh(); |
||||
|
if (time) clearInterval(time); |
||||
|
} |
||||
|
|
||||
|
let getPass = () => { |
||||
|
let pass = false; |
||||
|
try { |
||||
|
params.getFormRef() |
||||
|
pass = true; |
||||
|
} catch (e) { |
||||
|
pass = false; |
||||
|
} |
||||
|
if (pass) { |
||||
|
fn(); |
||||
|
} |
||||
|
return pass; |
||||
|
} |
||||
|
|
||||
|
if (!getPass()) { |
||||
|
time = setInterval(() => { |
||||
|
getPass() |
||||
|
}, 100) |
||||
|
} |
||||
|
|
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 空函数,禁止选中
|
||||
|
const placeholderEvent = () => { |
||||
|
} |
||||
|
|
||||
|
const isPagesHide = ref(false) |
||||
|
onShow(() => { |
||||
|
isPagesHide.value = false; |
||||
|
}) |
||||
|
onHide(() => { |
||||
|
isPagesHide.value = true; |
||||
|
}) |
||||
|
|
||||
|
// 监听滚动事件
|
||||
|
const scrollValStr = ref() |
||||
|
const onPageScrollLifeCycle = () => { |
||||
|
onPageScroll((e) => { |
||||
|
if (scrollVal && !isPagesHide.value) { |
||||
|
for (let key in scrollVal) { |
||||
|
if (e.scrollTop <= 0) { |
||||
|
// -1 表示页面滚动值小于零,组件随页面下拉而下来
|
||||
|
componentsScrollBool.value[key] = -1; |
||||
|
} else if (e.scrollTop > scrollVal[key]) { |
||||
|
// 1 表示页面滚动值大于传入滚动值,组件随页面上拉背景、文字颜色等采用滚动后的变量
|
||||
|
componentsScrollBool.value[key] = 1; |
||||
|
} else { |
||||
|
// 2 表示页面滚动值小于传入滚动值,组件随页面下拉背景、文字颜色等采用滚动前的变量
|
||||
|
componentsScrollBool.value[key] = 2 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
scrollV: scrollValStr.value, |
||||
|
data: data.value, |
||||
|
componentsScrollBool: componentsScrollBool.value, |
||||
|
placeholderEvent, |
||||
|
refresh, |
||||
|
isShowPlaceHolder, |
||||
|
getComponentClass, |
||||
|
onPageScroll: onPageScrollLifeCycle, |
||||
|
onMounted: onMountedLifeCycle |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,59 @@ |
|||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
/** |
||||
|
* 添加购物车 |
||||
|
*/ |
||||
|
export function addCart(data: AnyObject) { |
||||
|
return request.post(`shop/cart`, data) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 编辑购物车数量 |
||||
|
*/ |
||||
|
export function editCart(data: AnyObject) { |
||||
|
return request.put(`shop/cart`, data) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除购物车 |
||||
|
*/ |
||||
|
export function deleteCart(data: AnyObject) { |
||||
|
return request.put(`shop/cart/delete`, data) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 清空购物车 |
||||
|
*/ |
||||
|
export function clearCart() { |
||||
|
return request.delete(`shop/cart/clear`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取购物车列表 |
||||
|
*/ |
||||
|
export function getCartList(params: Record<string, any>) { |
||||
|
return request.get(`shop/cart`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取购物车商品列表 |
||||
|
*/ |
||||
|
export function getCartGoodsList(params: Record<string, any>) { |
||||
|
return request.get(`shop/cart/goods`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取购物车数量 |
||||
|
*/ |
||||
|
export function getCartSum(params: Record<string, any>) { |
||||
|
return request.get(`shop/cart/sum`, params) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 购物车计算 |
||||
|
*/ |
||||
|
export function getCartCalculate(params: Record<string, any>) { |
||||
|
return request.get(`shop/cart/calculate`, params) |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
/** |
||||
|
* 获取发票配置 |
||||
|
*/ |
||||
|
export function getInvoiceConfig() { |
||||
|
return request.get(`shop/config/invoice`) |
||||
|
} |
||||
@ -0,0 +1,65 @@ |
|||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
/** |
||||
|
* 优惠券列表 |
||||
|
*/ |
||||
|
export function getShopCouponList(params: Record<string, any>) { |
||||
|
return request.get(`shop/coupon`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 优惠券详情 |
||||
|
*/ |
||||
|
export function getShopCouponInfo(id: number) { |
||||
|
return request.get(`shop/coupon/${ id }`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 优惠券二维码 |
||||
|
*/ |
||||
|
export function getShopCouponQrocde(id: number) { |
||||
|
return request.get(`shop/coupon/qrcode/${ id }`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 领取优惠券 |
||||
|
*/ |
||||
|
export function getCoupon(params: Record<string, any>) { |
||||
|
return request.post(`shop/coupon`, params, { showSuccessMessage: true }) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取我的优惠券 |
||||
|
*/ |
||||
|
export function getMyCouponList(params: Record<string, any>) { |
||||
|
return request.get(`shop/member/coupon`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取优惠券列表供组件调用 |
||||
|
*/ |
||||
|
export function getShopCouponComponents(params: Record<string, any>) { |
||||
|
return request.get(`shop/coupon/components`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取我的优惠券数量 |
||||
|
* status 1:待使用,2:已使用,3:已过期,4:已失效 |
||||
|
*/ |
||||
|
export function getMyCouponCount(params: Record<string, any>) { |
||||
|
return request.get(`shop/member/coupon/count`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取我的优惠券类型 |
||||
|
*/ |
||||
|
export function getMyCouponType() { |
||||
|
return request.get(`shop/coupon_type`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取我的优惠数量 |
||||
|
*/ |
||||
|
export function getMyCouponStatusCount() { |
||||
|
return request.get(`shop/member/coupon/status_count`) |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
/** |
||||
|
* 获取限时折扣轮播 |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function getActiveDiscountConfig() { |
||||
|
return request.get(`shop/discount/config`); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取限时折扣列表 |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function getActiveDiscountList(params: Record<string, any>) { |
||||
|
return request.get(`shop/discount`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取限时折扣商品列表 |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function getActiveDiscountGoodsList(params: Record<string, any>) { |
||||
|
return request.get(`shop/discount/goods`, params) |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
/** |
||||
|
* 获取商品评价 |
||||
|
*/ |
||||
|
export function getEvaluateList(params: Record<string, any>) { |
||||
|
return request.get(`shop/goods/evaluate`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取评价详情 |
||||
|
*/ |
||||
|
export function getEvaluateInfo(id: any) { |
||||
|
return request.get(`shop/goods/evaluate/${ id }`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 提交评论 |
||||
|
*/ |
||||
|
export function setOrderEvaluate(params: Record<string, any>) { |
||||
|
return request.post('shop/goods/evaluate', params, { showSuccessMessage: true }) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取订单评价 |
||||
|
*/ |
||||
|
export function getOrderEvaluate(id: any) { |
||||
|
return request.get(`shop/order/evaluate/${ id }`) |
||||
|
} |
||||
@ -0,0 +1,107 @@ |
|||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
/** |
||||
|
* 获取商品分类模板配置 |
||||
|
*/ |
||||
|
export function getGoodsCategoryConfig() { |
||||
|
return request.get(`shop/goods/category/config`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取商品分类树结构 |
||||
|
*/ |
||||
|
export function getGoodsCategoryTree() { |
||||
|
return request.get(`shop/goods/category/tree`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取商品分类列表 |
||||
|
*/ |
||||
|
export function getGoodsCategoryList(params: Record<string, any>) { |
||||
|
return request.get(`shop/goods/category/list`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取商品列表 |
||||
|
*/ |
||||
|
export function getGoodsPages(params: Record<string, any>) { |
||||
|
return request.get(`shop/goods/pages`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取商品详情 |
||||
|
*/ |
||||
|
export function getGoodsDetail(params: Record<string, any>) { |
||||
|
return request.get(`shop/goods/detail`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取商品规格 |
||||
|
*/ |
||||
|
export function getGoodsSku(sku_id: any) { |
||||
|
return request.get(`shop/goods/sku/${ sku_id }`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 收藏列表 |
||||
|
*/ |
||||
|
export function getCollectList(params: Record<string, any>) { |
||||
|
return request.get(`shop/goods/collect`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 取消收藏 |
||||
|
*/ |
||||
|
export function cancelCollect(params: Record<string, any>) { |
||||
|
return request.put(`shop/goods/collect`, params, { showSuccessMessage: true }) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 收藏 |
||||
|
*/ |
||||
|
export function collect(goods_id: any) { |
||||
|
return request.post(`shop/goods/collect/${ goods_id }`) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 获取评价 |
||||
|
*/ |
||||
|
export function getEvaluateList(goods_id: any) { |
||||
|
return request.get(`shop/goods/evaluate/list`, { goods_id }) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取商品列表供组件调用 |
||||
|
*/ |
||||
|
export function getGoodsComponents(params: Record<string, any>) { |
||||
|
return request.get(`shop/goods/components`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取商品满减信息 |
||||
|
*/ |
||||
|
export function getManjian(params: Record<string, any>) { |
||||
|
return request.get(`shop/manjian/info`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 商品足迹添加 |
||||
|
*/ |
||||
|
export function browse(params: Record<string, any>) { |
||||
|
return request.post(`shop/goods/browse`, params, { showSuccessMessage: false }) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 商品足迹列表 |
||||
|
*/ |
||||
|
export function getBrowse(params: Record<string, any>) { |
||||
|
return request.get(`shop/goods/browse`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 商品足迹删除 |
||||
|
*/ |
||||
|
export function delBrowse(params: Record<string, any>) { |
||||
|
return request.delete(`shop/goods/browse`, params) |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
// 分享专区列表
|
||||
|
export function getNewcomerGoodsList(params: Record<string, any>) { |
||||
|
return request.get(`shop/newcomer/goods`, params) |
||||
|
} |
||||
|
|
||||
|
export function getNewcomersConfig() { |
||||
|
return request.get(`shop/newcomer/config`) |
||||
|
} |
||||
|
|
||||
|
// 首页新人专享列表
|
||||
|
export function getNewcomersComponentsList(params: Record<string, any>) { |
||||
|
return request.get(`shop/newcomer/goods/components`, params) |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,86 @@ |
|||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
/***************************************************** 订单列表 ****************************************************/ |
||||
|
/** |
||||
|
* 获取订单设置 |
||||
|
*/ |
||||
|
export function getShopOrderConfig() { |
||||
|
return request.get(`shop/order/config`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取订单状态列表 |
||||
|
*/ |
||||
|
export function getShopOrderStatus() { |
||||
|
return request.get(`shop/order/status`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取订单列表 |
||||
|
*/ |
||||
|
export function getShopOrder(params: Record<string, any>) { |
||||
|
return request.get(`shop/order`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取订单角标数据 |
||||
|
*/ |
||||
|
export function getShopOrderNum() { |
||||
|
return request.get(`shop/order/num`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取订单详情 |
||||
|
*/ |
||||
|
export function getShopOrderDetail(order_id: any) { |
||||
|
return request.get(`shop/order/${ order_id }`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 关闭订单 |
||||
|
*/ |
||||
|
export function orderClose(order_id: number) { |
||||
|
return request.put(`shop/order/close/${ order_id }`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 订单完成 |
||||
|
*/ |
||||
|
export function orderFinish(order_id: number) { |
||||
|
return request.put(`shop/order/finish/${ order_id }`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 订单创建计算 |
||||
|
*/ |
||||
|
export function orderCreateCalculate(params: Record<string, any>) { |
||||
|
return request.get('shop/order_create/calculate', params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 订单创建 |
||||
|
*/ |
||||
|
export function orderCreate(params: Record<string, any>) { |
||||
|
return request.post('shop/order_create/create', params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 查询订单可用优惠券 |
||||
|
*/ |
||||
|
export function orderCoupon(params: Record<string, any>) { |
||||
|
return request.get('shop/order_create/coupon', params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 查询自提点 |
||||
|
*/ |
||||
|
export function getStoreList(params: Record<string, any>) { |
||||
|
return request.get('shop/order_create/store', params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 查询物流信息 |
||||
|
*/ |
||||
|
export function getMaterialflowList(params: Record<string, any>) { |
||||
|
return request.get('shop/order/logistics', params) |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
/** |
||||
|
* 获取个人积分信息 |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function getExchangePoint() { |
||||
|
return request.get(`shop/exchange/point`); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取积分商城推荐列表 |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function getExchangeComponentsList(params: Record<string, any>) { |
||||
|
return request.get(`shop/exchange/components`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取积分商城列表 |
||||
|
* @returns |
||||
|
*/ |
||||
|
export function getExchangeGoodsList(params: Record<string, any>) { |
||||
|
return request.get(`shop/exchange`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取积分商品详情 |
||||
|
*/ |
||||
|
export function getExchangeGoodsDetail(id: any) { |
||||
|
return request.get(`shop/exchange/goods/${ id }`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 订单创建计算 |
||||
|
*/ |
||||
|
export function orderCreateCalculate(params: Record<string, any>) { |
||||
|
return request.get('shop/exchange_order/calculate', params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 订单创建 |
||||
|
*/ |
||||
|
export function orderCreate(params: Record<string, any>) { |
||||
|
return request.post('shop/exchange_order/create', params) |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,29 @@ |
|||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
// 榜单分类列表
|
||||
|
export function getRankList() { |
||||
|
return request.get(`shop/rank`) |
||||
|
} |
||||
|
|
||||
|
// 榜单商品列表
|
||||
|
export function getRankGoodsList(params: Record<string, any>) { |
||||
|
return request.get(`shop/rank/goods`, params) |
||||
|
} |
||||
|
|
||||
|
// 获取排行榜配置
|
||||
|
export function getRankConfig() { |
||||
|
return request.get(`shop/rank/getRankConfig`) |
||||
|
} |
||||
|
|
||||
|
// 榜单组件商品列表
|
||||
|
export function getRankComponentsGoodsList(params: Record<string, any>) { |
||||
|
return request.get(`shop/rank/components`, params) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,71 @@ |
|||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
/** |
||||
|
* 申请退款 |
||||
|
*/ |
||||
|
export function applyRefund(params: Record<string, any>) { |
||||
|
return request.post(`shop/refund/apply`, params, { showSuccessMessage: true }) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 修改退款申请 |
||||
|
*/ |
||||
|
export function editRefund(params: Record<string, any>) { |
||||
|
return request.put(`shop/refund/${ params.order_refund_no }`, params, { showSuccessMessage: true }) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 申请退款退货 |
||||
|
*/ |
||||
|
export function refundDelivery(params: Record<string, any>) { |
||||
|
return request.post(`shop/refund/delivery/${ params.order_refund_no }`, params, { showSuccessMessage: true }) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 修改退款申请 |
||||
|
*/ |
||||
|
export function editRefundDelivery(params: Record<string, any>) { |
||||
|
return request.put(`shop/refund/delivery/${ params.order_refund_no }`, params, { showSuccessMessage: true }) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取退款原因 |
||||
|
*/ |
||||
|
export function getRefundReason() { |
||||
|
return request.get('shop/refund/reason') |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取退款列表 |
||||
|
*/ |
||||
|
export function getRefundList() { |
||||
|
return request.get('shop/order/refund') |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取退款详情 |
||||
|
*/ |
||||
|
export function getRefundDetail(orderRefundNo: string) { |
||||
|
return request.get(`shop/order/refund/${ orderRefundNo }`) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取退款金额 |
||||
|
*/ |
||||
|
export function getRefundMoney(params: Record<string, any>) { |
||||
|
return request.get(`shop/refund/refund_data`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 编辑退款金额 |
||||
|
*/ |
||||
|
export function getRefundMoneyAgain(params: Record<string, any>) { |
||||
|
return request.get(`shop/refund/refund_data_by_no`, params) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 取消维权 |
||||
|
*/ |
||||
|
export function closeRefund(orderRefundNo: string) { |
||||
|
return request.put(`shop/refund/close/${ orderRefundNo }`, {}, { showSuccessMessage: true }) |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
import request from '@/utils/request' |
||||
|
|
||||
|
/** |
||||
|
* 获取评论设置 |
||||
|
*/ |
||||
|
export function getEvaluateConfig() { |
||||
|
return request.get(`shop/goods/evaluate/config`) |
||||
|
} |
||||
@ -0,0 +1,382 @@ |
|||||
|
<template> |
||||
|
<x-skeleton :type="skeleton.type" :loading="skeleton.loading" :config="skeleton.config" v-if="couponList && Object.keys(couponList).length > 0"> |
||||
|
<view :style="warpCss" class="overflow-hidden"> |
||||
|
|
||||
|
<view v-if="diyComponent.style == 'style-1'" class="coupon-wrap style-1 relative"> |
||||
|
<scroll-view scroll-x="true" class="coupon-list" :style="{'background-image':'url(' + img('addon/shop/diy/goods_coupon/style1_bg2.png') + ')','background-size':'100%','background-repeat':'no-repeat'}"> |
||||
|
<view class="coupon-class"> |
||||
|
<block v-if="couponList.length > 1"> |
||||
|
<view v-for="(item,index) in couponList" :key="index" class="rounded-[16rpx] box-border pt-[14rpx] inline-flex flex-col items-center relative w-[150rpx] h-[130rpx]" :class="{'mr-[20rpx]': index != couponList.length-1}" :style="{'background-image':'url(' + img('addon/shop/diy/goods_coupon/coupon_item_bg.png') + ')','background-size':'100%','background-repeat':'no-repeat'}" @click="couponItemLink(item)"> |
||||
|
<view class="truncate w-full flex items-baseline justify-center price-font text-[var(--price-text-color)]"> |
||||
|
<text class="text-[26rpx] font-500">¥</text> |
||||
|
<text class="text-[36rpx] truncate font-500">{{ parseFloat(item.price) }}</text> |
||||
|
</view> |
||||
|
<view class="text-[#303133] text-[20rpx] mt-[12rpx]">{{ item.min_condition_money == '0.00' ? '无门槛' : ('满'+parseFloat(item.min_condition_money)+'元可用') }}</view> |
||||
|
<view class="mt-[auto] rounded-b-[12rpx] text-[#f2333c] text-[20rpx] w-[100%] h-[36rpx] flex items-center justify-center bg-[#fff5f2]">{{item.type_name}}</view> |
||||
|
</view> |
||||
|
</block> |
||||
|
<block v-else> |
||||
|
<view v-for="(item,index) in couponList" :key="index" class="rounded-[16rpx] box-border pt-[14rpx] pl-[44rpx] pr-[44rpx] inline-flex items-center justify-between relative w-[100%] h-[130rpx]" :style="{'background-image':'url(' + img('addon/shop/diy/goods_coupon/style1_bg4.png') + ')','background-size':'100%','background-repeat':'no-repeat'}" @click="couponItemLink(item)"> |
||||
|
<view class="flex price-font text-[var(--price-text-color)] items-baseline"> |
||||
|
<text class="text-[36rpx] mt-[16rpx] mr-[4rpx]">¥</text> |
||||
|
<text class="text-[85rpx] font-500 max-w-[170rpx] truncate">{{parseFloat(item.price)}}</text> |
||||
|
</view> |
||||
|
<view class="border-0 border-dashed border-r-[2rpx] border-[#FF323C] absolute left-[40%] top-[46rpx] bottom-0 w-[2rpx]"></view> |
||||
|
<view class="w-[270rpx]"> |
||||
|
<view class="flex items-center mt-[auto]"> |
||||
|
<text class="rounded-[4rpx] bg-[#fff3f0] text-[#f2333c] border-[2rpx] border-solid border-[#f2333c] text-[22rpx] px-[6rpx] pb-[4rpx] pt-[6rpx] flex items-center justify-center whitespace-nowrap">{{item.type_name}}</text> |
||||
|
<text class="ml-[4rpx] text-[#f2333c] max-w-[184rpx] truncate">{{item.title}}</text> |
||||
|
</view> |
||||
|
<view class="text-[#f2333c] text-[30rpx] font-500 mt-[10rpx] w-[270rpx] truncate">{{item.min_condition_money == '0.00' ? '无门槛' : ('消费满'+parseFloat(item.min_condition_money)+'元可用') }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</block> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
<view class="w-[100%] h-[130rpx] pt-[24rpx] px-[26rpx] box-border flex items-center justify-between absolute left-0 right-0 bottom-0" :style="{'background-image':'url(' + img('addon/shop/diy/goods_coupon/style1_bg.png') + ')','background-size':'100% 130rpx','background-repeat':'no-repeat'}"> |
||||
|
<view class="flex flex-col"> |
||||
|
<text class="text-[30rpx] text-[#fff] font-400">{{ diyComponent.couponTitle }}</text> |
||||
|
<text class="text-[20rpx] text-[rgba(255,255,255,.8)] mt-[10rpx]">{{ diyComponent.couponSubTitle }}</text> |
||||
|
</view> |
||||
|
<text v-if="diyComponent.btnText" @click="toLink('/addon/shop/pages/coupon/list')" class="bg-[#fff] flex items-center justify-center text-[#FF4142] text-[22rpx] min-w-[100rpx] px-[24rpx] box-border h-[50rpx] coupon-buy-btn">{{diyComponent.btnText}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view v-else-if="diyComponent.style == 'style-2'" class="coupon-wrap style-2 relative"> |
||||
|
<scroll-view scroll-x="true" class="coupon-list"> |
||||
|
<view v-for="(item,index) in couponList" :key="index" class="box-border pt-[14rpx] inline-flex flex-col items-center relative w-[140rpx] h-[130rpx] rounded-[10rpx]" :class="{'mr-[20rpx]': index != couponList.length-1, 'mr-[290rpx]': index == couponList.length-1}" :style="{'background-image':'url(' + img('addon/shop/diy/goods_coupon/coupon_item_bg.png') + ')','background-size':'100%','background-repeat':'no-repeat'}" @click="couponItemLink(item)"> |
||||
|
<view class="flex items-baseline justify-center w-full truncate price-font text-[var(--price-text-color)]"> |
||||
|
<text class="text-[24rpx]">¥</text> |
||||
|
<text class="text-[38rpx] font-bold truncate">{{parseFloat(item.price)}}</text> |
||||
|
</view> |
||||
|
<view class="text-[#303133] text-[20rpx] truncate max-w-[120rpx] mt-[12rpx]">{{item.min_condition_money == '0.00' ? '无门槛' : ('满'+parseFloat(item.min_condition_money)+'元可用') }}</view> |
||||
|
<view class="mt-[auto] rounded-b-[12rpx] text-[#f2333c] text-[20rpx] w-[100%] h-[36rpx] flex items-center justify-center bg-[#fff5f2]">{{item.type_name}}</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
<view class="w-[290rpx] h-[170rpx] py-[20rpx] pl-[30rpx] box-border flex flex-col items-center justify-between absolute right-0 bottom-0" :style="{'background-image':'url(' + img('addon/shop/diy/goods_coupon/style2_bg.png') + ')','background-size':'290rpx 170rpx','background-repeat':'no-repeat'}"> |
||||
|
<text class="text-[30rpx] text-[#fff] font-500">{{ diyComponent.couponTitle }}</text> |
||||
|
<text class="text-[20rpx] text-[rgba(255,255,255,.8)] mt-[14rpx]">{{ diyComponent.couponSubTitle }}</text> |
||||
|
<text v-if="diyComponent.btnText" @click="toLink('/addon/shop/pages/coupon/list')" class="bg-[#fff] text-[#FF4142] text-[22rpx] min-w-[100rpx] px-[24rpx] box-border h-[50rpx] leading-[50rpx] text-center coupon-buy-btn mt-auto">{{diyComponent.btnText}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view v-else-if="diyComponent.style == 'style-3'" class="coupon-wrap style-3 relative" :style="{'background-image':'url(' + img('addon/shop/diy/goods_coupon/style3_bg.jpg') + ')','background-size':'100% 204rpx','background-repeat':'no-repeat'}"> |
||||
|
<view class="desc flex flex-col"> |
||||
|
<text class="text-[30rpx] text-[#fff] font-500">{{ diyComponent.couponTitle }}</text> |
||||
|
<text class="text-[22rpx] text-[rgba(255,255,255,.8)] mt-[10rpx]">{{ diyComponent.couponSubTitle }}</text> |
||||
|
<text v-if="diyComponent.btnText" @click="toLink('/addon/shop/pages/coupon/list')" class="bg-[#fff] text-[#FF4142] text-[24rpx] w-[140rpx] box-border h-[50rpx] leading-[50rpx] text-center coupon-buy-btn mt-auto">{{diyComponent.btnText}}</text> |
||||
|
</view> |
||||
|
<scroll-view scroll-x="true" class="coupon-list" v-if="couponList.length > 1"> |
||||
|
<view v-for="(item,index) in couponList" :key="index" class="bg-[#fff] box-border p-[8rpx] pb-[12rpx] inline-flex flex-col items-center relative rounded-[20rpx] ml-[12rpx]" @click="couponItemLink(item)"> |
||||
|
<view class="coupon-item-content"> |
||||
|
<view class="text-[20rpx] text-[#fff]">{{item.type_name}}</view> |
||||
|
<view class="mt-[auto] flex items-baseline justify-center w-full truncate price-font text-[#fff]"> |
||||
|
<text class="text-[24rpx]">¥</text> |
||||
|
<text class="text-[44rpx] font-bold truncate">{{parseFloat(item.price)}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="text-[#303133] text-[22rpx] truncate max-w-[120rpx] mt-[12rpx]">{{item.min_condition_money == '0.00' ? '无门槛' : ('满'+parseFloat(item.min_condition_money)+'元可用') }}</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
<view v-if="couponList.length == 1" class="bg-[#fff] box-border p-[10rpx] relative rounded-[20rpx] single-coupon flex-1" @click="couponItemLink(couponList[0])"> |
||||
|
<view class="flex items-center coupon-item-content"> |
||||
|
<view class="coupon-left flex items-center justify-center text-[#fff] w-[156rpx] mr-[30rpx]"> |
||||
|
<text class="text-[24rpx]">¥</text> |
||||
|
<text class="text-[44rpx] font-[500]">{{parseFloat(couponList[0].price)}}</text> |
||||
|
</view> |
||||
|
<view class="flex flex-col"> |
||||
|
<view class="text-[#fff] text-[28rpx] mb-[14rpx]">{{couponList[0].min_condition_money == '0.00' ? '无门槛' : ('满'+parseFloat(couponList[0].min_condition_money)+'元可用') }}</view> |
||||
|
<view class="flex items-center"> |
||||
|
<text class="bg-[#fff] mr-[10rpx] text-[red] text-[20rpx] px-[10rpx] py-[8rpx] rounded-[20rpx]">{{couponList[0].type_name}}</text> |
||||
|
<text class="text-[#fff] text-[24rpx]">店铺优惠券</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view v-else-if="diyComponent.style == 'style-4'" class="coupon-wrap style-4 relative" :style="couponStyle4Css"> |
||||
|
<view class="desc flex items-center pt-[6rpx] pb-[26rpx]" @click="toLink('/addon/shop/pages/coupon/list')"> |
||||
|
<text class="text-[32rpx] text-[#fff] font-500" :style="{color: diyComponent.titleColor}">{{ diyComponent.couponTitle }}</text> |
||||
|
<text class="text-[22rpx] text-[rgba(255,255,255,.8)] ml-[10rpx]" :style="{color: diyComponent.subTitleColor}">{{ diyComponent.couponSubTitle }}</text> |
||||
|
</view> |
||||
|
<scroll-view scroll-x="true" class="coupon-list"> |
||||
|
<view v-for="(item,index) in couponList" :key="index" class="px-[10rpx] h-[120rpx] inline-flex items-center relative mr-[12rpx] coupon-item box-border min-w-[310rpx]" :style="{'background-color': diyComponent.couponItem.bgColor, 'border-radius': ((diyComponent.couponItem.aroundRadius*2)+'rpx')}" @click="couponItemLink(item)"> |
||||
|
<view class="flex min-w-[110rpx] max-w-[120rpx] items-baseline justify-center truncate price-font mr-[10rpx]" :style="{'color': diyComponent.couponItem.moneyColor}"> |
||||
|
<text class="text-[26rpx]">¥</text> |
||||
|
<text class="text-[46rpx] font-bold truncate">{{parseFloat(item.price)}}</text> |
||||
|
</view> |
||||
|
<view class="flex flex-col"> |
||||
|
<view class="text-[28rpx] font-500" :style="{'color': diyComponent.couponItem.textColor}">{{item.type_name}}</view> |
||||
|
<view class="text-[#666] text-[24rpx] truncate max-w-[180rpx] mt-[12rpx]" :style="{'color': diyComponent.couponItem.subTextColor}">{{item.min_condition_money == '0.00' ? '无门槛' : ('满'+parseFloat(item.min_condition_money)+'元可用') }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</x-skeleton> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
// 优惠券组件 |
||||
|
import { ref, reactive, computed, watch, onMounted } from 'vue'; |
||||
|
import { img, redirect } from '@/utils/common'; |
||||
|
import useDiyStore from '@/app/stores/diy'; |
||||
|
import { useLogin } from '@/hooks/useLogin'; |
||||
|
import useMemberStore from '@/stores/member' |
||||
|
import { getShopCouponComponents,getCoupon } from '@/addon/shop/api/coupon'; |
||||
|
|
||||
|
const props = defineProps(['component', 'index']); |
||||
|
const diyStore = useDiyStore(); |
||||
|
const memberStore = useMemberStore() |
||||
|
const userInfo = computed(() => memberStore.info) |
||||
|
const skeleton = reactive({ |
||||
|
type: 'banner', |
||||
|
loading: false, |
||||
|
config: { |
||||
|
gridRows: 1, |
||||
|
gridRowsGap: '0rpx', |
||||
|
headHeight: '170rpx' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const diyComponent = computed(() => { |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
return diyStore.value[props.index]; |
||||
|
} else { |
||||
|
return props.component; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const warpCss = computed(() => { |
||||
|
var style = ''; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const couponStyle4Css = computed(()=>{ |
||||
|
var style = ''; |
||||
|
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`; |
||||
|
else style += 'background-color:' + (diyComponent.value.componentStartBgColor || diyComponent.value.componentEndBgColor) + ';'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const toLink = (url: any)=>{ |
||||
|
if (diyStore.mode == 'decorate') return; |
||||
|
redirect({ url }) |
||||
|
} |
||||
|
|
||||
|
const couponList: any = ref([]) |
||||
|
const getShopCouponListFn = () => { |
||||
|
let data: object = { |
||||
|
num: diyComponent.value.source == 'all' ? diyComponent.value.num : '', |
||||
|
coupon_ids: diyComponent.value.source == 'custom' ? diyComponent.value.couponIds : '', |
||||
|
}; |
||||
|
getShopCouponComponents(data).then((res: any) => { |
||||
|
couponList.value = res.data |
||||
|
skeleton.loading = false; |
||||
|
|
||||
|
// 数据为空时隐藏整个组件 |
||||
|
// if(couponList.value.length == 0) { |
||||
|
// diyComponent.value.pageStyle = ''; |
||||
|
// } |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const couponItemLink = (data:any)=> { |
||||
|
// redirect({ url: '/addon/shop/pages/coupon/detail', param: { coupon_id: data.id } }) |
||||
|
collecting(data.id) |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
refresh(); |
||||
|
}); |
||||
|
|
||||
|
const refresh = () => { |
||||
|
|
||||
|
// 装修模式下设置默认图 |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
let obj = { |
||||
|
title: '满减券', |
||||
|
type_name: '通用券', |
||||
|
price: 100, |
||||
|
min_condition_money: 0, |
||||
|
}; |
||||
|
for (let i = 0; i < 4; i++) { |
||||
|
couponList.value.push(obj); |
||||
|
} |
||||
|
} else { |
||||
|
getShopCouponListFn(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const collecting = (coupon_id: any) => { |
||||
|
if (diyStore.mode == 'decorate') return; |
||||
|
if (!userInfo.value) { |
||||
|
useLogin().setLoginBack({ url: '/addon/shop/pages/coupon/list' }) |
||||
|
return false |
||||
|
} |
||||
|
getCoupon({ coupon_id, number: 1 }).then(res => { |
||||
|
// detail.value.btnType = 'using' |
||||
|
}) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.coupon-wrap{ |
||||
|
&.style-1{ |
||||
|
.coupon-list{ |
||||
|
position: relative; |
||||
|
height: 270rpx; |
||||
|
width: 100%; |
||||
|
white-space: nowrap; |
||||
|
padding: 30rpx 40rpx 0; |
||||
|
box-sizing: border-box; |
||||
|
&::before{ |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 20rpx; |
||||
|
right: 20rpx; |
||||
|
bottom: 0; |
||||
|
background: linear-gradient(#FEF9EC, #FCD9A5); |
||||
|
border-radius: 24rpx; |
||||
|
} |
||||
|
} |
||||
|
.coupon-buy-btn{ |
||||
|
border-radius: 50rpx; |
||||
|
} |
||||
|
} |
||||
|
&.style-2{ |
||||
|
.coupon-list{ |
||||
|
position: relative; |
||||
|
height: 170rpx; |
||||
|
width: 100%; |
||||
|
white-space: nowrap; |
||||
|
padding: 20rpx 0 20rpx 20rpx; |
||||
|
box-sizing: border-box; |
||||
|
overflow: hidden; |
||||
|
background: linear-gradient(#EE3928, #EF3F30); |
||||
|
} |
||||
|
.coupon-buy-btn{ |
||||
|
border-radius: 50rpx; |
||||
|
} |
||||
|
} |
||||
|
&.style-3{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding: 20rpx; |
||||
|
.desc{ |
||||
|
padding-top: 12rpx; |
||||
|
height: 164rpx; |
||||
|
margin-right: 10rpx; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
.coupon-list{ |
||||
|
flex: 1; |
||||
|
position: relative; |
||||
|
white-space: nowrap; |
||||
|
box-sizing: border-box; |
||||
|
overflow: hidden; |
||||
|
.coupon-item-content{ |
||||
|
position: relative; |
||||
|
background: linear-gradient( 160deg, #FD5F2F 0%, #F6370F 100%); |
||||
|
width: 146rpx; |
||||
|
height: 108rpx; |
||||
|
border-radius: 12rpx; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
padding: 12rpx 0 14rpx; |
||||
|
box-sizing: border-box; |
||||
|
&::after{ |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
border: 12rpx solid #fff; |
||||
|
border-right-color: transparent; |
||||
|
border-top-color: transparent; |
||||
|
border-radius: 50%; |
||||
|
top: 50%; |
||||
|
transform: rotate(45deg) translateY(-50%); |
||||
|
right: -5rpx; |
||||
|
} |
||||
|
&::before{ |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
border: 12rpx solid #fff; |
||||
|
border-left-color: transparent; |
||||
|
border-bottom-color: transparent; |
||||
|
border-radius: 50%; |
||||
|
top: 50%; |
||||
|
transform: rotate(45deg) translateY(-50%); |
||||
|
left: -22rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.single-coupon{ |
||||
|
.coupon-item-content{ |
||||
|
position: relative; |
||||
|
background: linear-gradient( 160deg, #FD5F2F 0%, #F6370F 100%); |
||||
|
height: 144rpx; |
||||
|
border-radius: 12rpx; |
||||
|
padding: 12rpx 0 14rpx; |
||||
|
box-sizing: border-box; |
||||
|
&::after{ |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
border: 14rpx solid #fff; |
||||
|
border-right-color: transparent; |
||||
|
border-top-color: transparent; |
||||
|
border-radius: 50%; |
||||
|
left: 139rpx; |
||||
|
transform: rotate(-45deg); |
||||
|
top: -18rpx; |
||||
|
} |
||||
|
&::before{ |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
border: 14rpx solid #fff; |
||||
|
border-left-color: transparent; |
||||
|
border-bottom-color: transparent; |
||||
|
border-radius: 50%; |
||||
|
left: 139rpx; |
||||
|
transform: rotate(-45deg); |
||||
|
bottom: -16rpx; |
||||
|
} |
||||
|
.coupon-left{ |
||||
|
position: relative; |
||||
|
&::after{ |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
top: 50%; |
||||
|
right: 0; |
||||
|
transform: translateY(-50%); |
||||
|
border-right: 2rpx dashed #fff; |
||||
|
width: 2rpx; |
||||
|
height: 88rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.coupon-buy-btn{ |
||||
|
border-radius: 50rpx; |
||||
|
} |
||||
|
} |
||||
|
&.style-4{ |
||||
|
padding: 20rpx 24rpx; |
||||
|
.coupon-list{ |
||||
|
flex: 1; |
||||
|
position: relative; |
||||
|
white-space: nowrap; |
||||
|
box-sizing: border-box; |
||||
|
overflow: hidden; |
||||
|
.coupon-item{ |
||||
|
width: calc(50% - 6rpx); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,434 @@ |
|||||
|
<template> |
||||
|
<x-skeleton :type="skeleton.type" :loading="skeleton.loading" :config="skeleton.config"> |
||||
|
<view :style="warpCss" class="overflow-hidden"> |
||||
|
<view :style="maskLayer"></view> |
||||
|
<view :class="{'diy-shop-goods-list relative flex flex-wrap justify-between': diyComponent.style != 'style-2', 'biserial-goods-list': diyComponent.style == 'style-2'}"> |
||||
|
<block v-if="diyComponent.style == 'style-1'"> |
||||
|
<view class="bg-white w-full flex p-[20rpx] overflow-hidden" :class="{ 'mt-[20rpx]': index > 0 }" :style="itemCss" v-for="(item,index) in goodsList" :key="item.goods_id" @click="toLink(item)"> |
||||
|
<u--image radius="var(--goods-rounded-big)" width="200rpx" height="200rpx" :src="img(item.goods_cover_thumb_mid || '')" model="aspectFill"> |
||||
|
<template #error> |
||||
|
<image class="w-[200rpx] h-[200rpx] rounded-[var(--goods-rounded-big)] overflow-hidden" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image> |
||||
|
</template> |
||||
|
</u--image> |
||||
|
<view class="flex-1 flex flex-col ml-[20rpx] py-[6rpx] relative"> |
||||
|
<view class="text-[28rpx] leading-[40rpx] text-[#303133] multi-hidden mb-[10rpx]" :style="{ color : diyComponent.goodsNameStyle.color, fontWeight : diyComponent.goodsNameStyle.fontWeight }" v-if="diyComponent.goodsNameStyle.control"> |
||||
|
<view class="brand-tag" v-if="item.goods_brand"> |
||||
|
{{item.goods_brand.brand_name}} |
||||
|
</view> |
||||
|
{{item.goods_name}} |
||||
|
</view> |
||||
|
<view v-if="item.goods_label_name && item.goods_label_name.length && diyComponent.labelStyle.control" class="flex flex-wrap mb-[10rpx]"> |
||||
|
<template v-for="(tagItem, tagIndex) in item.goods_label_name"> |
||||
|
<image class="img-tag" v-if="tagItem.style_type == 'icon' && tagItem.icon" :src="img(tagItem.icon)" mode="heightFix" @error="diyGoods.error(tagItem,'icon')"></image> |
||||
|
<view class="base-tag" v-else-if="tagItem.style_type == 'diy' || !tagItem.icon" :style="diyGoods.baseTagStyle(tagItem)"> |
||||
|
{{tagItem.label_name}} |
||||
|
</view> |
||||
|
</template> |
||||
|
</view> |
||||
|
<view class="mt-auto flex justify-between items-center"> |
||||
|
<view class="flex flex-col"> |
||||
|
<view class="flex items-baseline leading-[1]" v-if="diyComponent.priceStyle.control"> |
||||
|
<view class="font-bold text-[var(--price-text-color)] price-font block truncate max-w-[350rpx]" :style="{ color : diyComponent.priceStyle.color }"> |
||||
|
<text class="text-[24rpx] font-500 mr-[4rpx]">¥</text> |
||||
|
<text class="text-[40rpx] font-500">{{ parseFloat(diyGoods.goodsPrice(item)).toFixed(2)}}</text> |
||||
|
</view> |
||||
|
<image v-if="diyGoods.priceType(item) == 'member_price'" class="max-w-[50rpx] h-[28rpx] ml-[6rpx]" :src="img('addon/shop/VIP.png')" mode="heightFix" /> |
||||
|
</view> |
||||
|
<text v-if="diyComponent.saleStyle.control" class="mt-[8rpx] text-[22rpx] text-[var(--text-color-light9)]" :style="{ color : diyComponent.saleStyle.color }">已售{{item.sale_num}}{{item.unit || '件'}}</text> |
||||
|
</view> |
||||
|
<view v-if="diyComponent.btnStyle.control" class="absolute right-[0] bottom-[0]"> |
||||
|
<view v-if="diyComponent.btnStyle.style == 'button'" :style="goodsBtnCss" class="px-[18rpx] min-w-[100rpx] box-border h-[48rpx] flex items-center justify-center"> |
||||
|
<text class="text-[20rpx]">{{diyComponent.btnStyle.text}}</text> |
||||
|
</view> |
||||
|
<view v-else :style="goodsBtnCss" class="w-[50rpx] h-[50rpx] rounded-[50%] flex items-center justify-center"> |
||||
|
<text :class="[diyComponent.btnStyle.style]" class="nc-iconfont text-[30rpx]"></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</block> |
||||
|
<block v-if="diyComponent.style == 'style-2'"> |
||||
|
<view> |
||||
|
<template v-for="(item,index) in goodsList"> |
||||
|
<view v-if="(index%2) == 0" class="flex flex-col bg-[#fff] box-border overflow-hidden" :class="{'mt-[24rpx]': index > 1}" :style="itemCss" @click="toLink(item)"> |
||||
|
<u--image :width="style2Width" :height="style2Width" :src="img(item.goods_cover_thumb_mid || '')" model="aspectFill"> |
||||
|
<template #error> |
||||
|
<image :style="{'width': style2Width,'height': style2Width}" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image> |
||||
|
</template> |
||||
|
</u--image> |
||||
|
<view class="relative min-h-[44rpx] px-[16rpx] flex-1 pt-[16rpx] pb-[20rpx] flex flex-col justify-between"> |
||||
|
<view class="text-[#303133] leading-[40rpx] text-[28rpx] multi-hidden" :style="{ color : diyComponent.goodsNameStyle.color, fontWeight : diyComponent.goodsNameStyle.fontWeight }" v-if="diyComponent.goodsNameStyle.control"> |
||||
|
<view class="brand-tag" v-if="item.goods_brand"> |
||||
|
{{item.goods_brand.brand_name}} |
||||
|
</view> |
||||
|
{{item.goods_name}} |
||||
|
</view> |
||||
|
<view v-if="item.goods_label_name && item.goods_label_name.length && diyComponent.labelStyle.control" class="flex flex-wrap"> |
||||
|
<template v-for="(tagItem, tagIndex) in item.goods_label_name"> |
||||
|
<image class="img-tag" v-if="tagItem.style_type == 'icon' && tagItem.icon" :src="img(tagItem.icon)" mode="heightFix" @error="diyGoods.error(tagItem,'icon')"></image> |
||||
|
<view class="base-tag" v-else-if="tagItem.style_type == 'diy' || !tagItem.icon" :style="diyGoods.baseTagStyle(tagItem)"> |
||||
|
{{tagItem.label_name}} |
||||
|
</view> |
||||
|
</template> |
||||
|
</view> |
||||
|
<view class="flex justify-between flex-wrap items-center mt-[20rpx]"> |
||||
|
<view class="flex flex-col"> |
||||
|
<view class="flex items-baseline leading-[1]" v-if="diyComponent.priceStyle.control"> |
||||
|
<view class="text-[var(--price-text-color)] price-font block truncate max-w-[270rpx]" :style="{ color : diyComponent.priceStyle.color }"> |
||||
|
<text class="text-[24rpx] font-400">¥</text> |
||||
|
<text class="text-[40rpx] font-500">{{ parseFloat(diyGoods.goodsPrice(item)).toFixed(2).split('.')[0] }}</text> |
||||
|
<text class="text-[24rpx] font-500">.{{ parseFloat(diyGoods.goodsPrice(item)).toFixed(2).split('.')[1] }}</text> |
||||
|
</view> |
||||
|
<image v-if="diyGoods.priceType(item) == 'member_price'" class="max-w-[50rpx] h-[28rpx] ml-[6rpx]" :src="img('addon/shop/VIP.png')" mode="heightFix" /> |
||||
|
</view> |
||||
|
<text v-if="diyComponent.saleStyle.control" class="text-[22rpx] mt-[8rpx] text-[var(--text-color-light9)]" :style="{ color : diyComponent.saleStyle.color }">已售{{item.sale_num}}{{item.unit || '件'}}</text> |
||||
|
</view> |
||||
|
<view class="absolute right-[16rpx] bottom-[16rpx]" v-if="diyComponent.btnStyle.control"> |
||||
|
<view v-if="diyComponent.btnStyle.style == 'button'" :style="goodsBtnCss" class="px-[18rpx] h-[48rpx] flex items-center justify-center"> |
||||
|
<text class="text-[20rpx]">{{diyComponent.btnStyle.text}}</text> |
||||
|
</view> |
||||
|
<view v-else :style="goodsBtnCss" class="w-[46rpx] h-[46rpx] rounded-[50%] flex items-center justify-center"> |
||||
|
<text :class="[diyComponent.btnStyle.style]" class="nc-iconfont text-[30rpx]"></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
</view> |
||||
|
<view> |
||||
|
<template v-for="(item,index) in goodsList"> |
||||
|
<view v-if="(index%2) == 1" class="flex flex-col bg-[#fff] box-border overflow-hidden" :class="{'mt-[24rpx]': index > 1}" :style="itemCss" @click="toLink(item)"> |
||||
|
<u--image :width="style2Width" :height="style2Width" :src="img(item.goods_cover_thumb_mid || '')" model="aspectFill"> |
||||
|
<template #error> |
||||
|
<image :style="{'width': style2Width,'height': style2Width}" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image> |
||||
|
</template> |
||||
|
</u--image> |
||||
|
<view class="relative min-h-[44rpx] px-[16rpx] flex-1 pt-[16rpx] pb-[20rpx] flex flex-col justify-between"> |
||||
|
<view class="text-[#303133] leading-[40rpx] text-[28rpx] multi-hidden" :style="{ color : diyComponent.goodsNameStyle.color, fontWeight : diyComponent.goodsNameStyle.fontWeight }" v-if="diyComponent.goodsNameStyle.control"> |
||||
|
<view class="brand-tag" v-if="item.goods_brand"> |
||||
|
{{item.goods_brand.brand_name}} |
||||
|
</view> |
||||
|
{{item.goods_name}} |
||||
|
</view> |
||||
|
<view v-if="item.goods_label_name && item.goods_label_name.length && diyComponent.labelStyle.control" class="flex flex-wrap"> |
||||
|
<template v-for="(tagItem, tagIndex) in item.goods_label_name"> |
||||
|
<image class="img-tag" v-if="tagItem.style_type == 'icon' && tagItem.icon" :src="img(tagItem.icon)" mode="heightFix" @error="diyGoods.error(tagItem,'icon')"></image> |
||||
|
<view class="base-tag" v-else-if="tagItem.style_type == 'diy' || !tagItem.icon" :style="diyGoods.baseTagStyle(tagItem)"> |
||||
|
{{tagItem.label_name}} |
||||
|
</view> |
||||
|
</template> |
||||
|
</view> |
||||
|
<view class="flex justify-between flex-wrap items-center mt-[20rpx]"> |
||||
|
<view class="flex flex-col"> |
||||
|
<view class="flex items-baseline leading-[1]" v-if="diyComponent.priceStyle.control"> |
||||
|
<view class="text-[var(--price-text-color)] price-font block truncate max-w-[270rpx]" :style="{ color : diyComponent.priceStyle.color }"> |
||||
|
<text class="text-[24rpx] font-400">¥</text> |
||||
|
<text class="text-[40rpx] font-500">{{ parseFloat(diyGoods.goodsPrice(item)).toFixed(2).split('.')[0] }}</text> |
||||
|
<text class="text-[24rpx] font-500">.{{ parseFloat(diyGoods.goodsPrice(item)).toFixed(2).split('.')[1] }}</text> |
||||
|
</view> |
||||
|
<image v-if="diyGoods.priceType(item) == 'member_price'" class="max-w-[50rpx] h-[28rpx] ml-[6rpx]" :src="img('addon/shop/VIP.png')" mode="heightFix" /> |
||||
|
</view> |
||||
|
<text v-if="diyComponent.saleStyle.control" class="text-[22rpx] mt-[8rpx] text-[var(--text-color-light9)]" :style="{ color : diyComponent.saleStyle.color }">已售{{item.sale_num}}{{item.unit || '件'}}</text> |
||||
|
</view> |
||||
|
<view class="absolute right-[16rpx] bottom-[16rpx]" v-if="diyComponent.btnStyle.control"> |
||||
|
<view v-if="diyComponent.btnStyle.style == 'button'" :style="goodsBtnCss" class="px-[18rpx] h-[48rpx] flex items-center justify-center"> |
||||
|
<text class="text-[20rpx]">{{diyComponent.btnStyle.text}}</text> |
||||
|
</view> |
||||
|
<view v-else :style="goodsBtnCss" class="w-[46rpx] h-[46rpx] rounded-[50%] flex items-center justify-center"> |
||||
|
<text :class="[diyComponent.btnStyle.style]" class="nc-iconfont text-[30rpx]"></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
</view> |
||||
|
</block> |
||||
|
<block v-if="diyComponent.style == 'style-3'"> |
||||
|
<view :style="style3Css" v-if="goodsList.length"> |
||||
|
<scroll-view :id="'warpStyle3-'+diyComponent.id" class="whitespace-nowrap min-h-[290rpx]" :scroll-x="true"> |
||||
|
<view :id="'item'+index+diyComponent.id" class="w-[214rpx] mb-[6rpx] inline-block bg-[#fff] box-border overflow-hidden" :class="{'!mr-[0rpx]' : index == (goodsList.length-1)}" :style="itemCss+itemStyle3" v-for="(item,index) in goodsList" :key="item.goods_id" @click="toLink(item)"> |
||||
|
<u--image width="214rpx" height="160rpx" :src="img(item.goods_cover_thumb_mid || '')" model="aspectFill"> |
||||
|
<template #error> |
||||
|
<image class="w-[214rpx] h-[160rpx]" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image> |
||||
|
</template> |
||||
|
</u--image> |
||||
|
<view class="relative min-h-[40rpx] px-[10rpx] pt-[16rpx] pb-[10rpx]"> |
||||
|
<view class="text-[26rpx] text-[#303133] truncate" :style="{ color : diyComponent.goodsNameStyle.color, fontWeight : diyComponent.goodsNameStyle.fontWeight }" v-if="diyComponent.goodsNameStyle.control">{{item.goods_name}}</view> |
||||
|
<view class="text-[var(--price-text-color)] pt-[16rpx] pb-[6rpx] font-bold price-font block truncate max-w-[160rpx] leading-[1] overflow-hidden" :style="{ color : diyComponent.priceStyle.color }" v-if="diyComponent.priceStyle.control"> |
||||
|
<text class="text-[20rpx] font-400 mr-[2rpx]">¥</text> |
||||
|
<text class="text-[36rpx] font-500">{{ parseFloat(diyGoods.goodsPrice(item)).toFixed(2)}}</text> |
||||
|
</view> |
||||
|
<view v-if="diyComponent.btnStyle.control" class="absolute right-[10rpx] bottom-[12rpx]"> |
||||
|
<view v-if="diyComponent.btnStyle.style != 'button'" :style="goodsBtnCss" class="w-[40rpx] h-[40rpx] rounded-[50%] flex items-center justify-center"> |
||||
|
<text :class="[diyComponent.btnStyle.style]" class="nc-iconfont text-[28rpx]"></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
|
||||
|
</block> |
||||
|
</view> |
||||
|
</view> |
||||
|
</x-skeleton> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
// 商品列表 |
||||
|
import { ref,reactive,computed, watch, onMounted, nextTick,getCurrentInstance } from 'vue'; |
||||
|
import { redirect, img } from '@/utils/common'; |
||||
|
import useDiyStore from '@/app/stores/diy'; |
||||
|
import { getGoodsComponents } from '@/addon/shop/api/goods'; |
||||
|
import {useGoods} from '@/addon/shop/hooks/useGoods' |
||||
|
|
||||
|
const diyGoods = useGoods(); |
||||
|
const props = defineProps(['component', 'index','value']); |
||||
|
const diyStore = useDiyStore(); |
||||
|
const emits = defineEmits(['loadingFn']); //商品数据加载完成之后触发 |
||||
|
|
||||
|
const skeleton = reactive({ |
||||
|
type: '', |
||||
|
loading: diyStore.mode == 'decorate' ? false : true, |
||||
|
config: {} |
||||
|
}) |
||||
|
|
||||
|
const goodsList = ref<Array<any>>([]); |
||||
|
|
||||
|
const diyComponent = computed(() => { |
||||
|
if(props.value) { |
||||
|
return props.value; |
||||
|
}else if (diyStore.mode == 'decorate') { |
||||
|
return diyStore.value[props.index]; |
||||
|
} else { |
||||
|
return props.component; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const warpCss = computed(() => { |
||||
|
var style = ''; |
||||
|
style += 'position:relative;'; |
||||
|
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`; |
||||
|
else style += 'background-color:' + (diyComponent.value.componentStartBgColor || diyComponent.value.componentEndBgColor) + ';'; |
||||
|
|
||||
|
if(diyComponent.value.componentBgUrl) { |
||||
|
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`; |
||||
|
style += 'background-size: cover;background-repeat: no-repeat;'; |
||||
|
} |
||||
|
|
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
// 背景图加遮罩层 |
||||
|
const maskLayer = computed(()=>{ |
||||
|
var style = ''; |
||||
|
if(diyComponent.value.componentBgUrl) { |
||||
|
style += 'position:absolute;top:0;width:100%;'; |
||||
|
style += `background: rgba(0,0,0,${diyComponent.value.componentBgAlpha / 10});`; |
||||
|
style += `height:${height.value}px;`; |
||||
|
|
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
} |
||||
|
|
||||
|
return style; |
||||
|
}); |
||||
|
|
||||
|
const itemCss = computed(() => { |
||||
|
var style = ''; |
||||
|
if (diyComponent.value.elementBgColor) style += 'background-color:' + diyComponent.value.elementBgColor + ';'; |
||||
|
if (diyComponent.value.topElementRounded) style += 'border-top-left-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topElementRounded) style += 'border-top-right-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;'; |
||||
|
if(diyComponent.value.style == 'style-2'){ |
||||
|
if(diyComponent.value.margin && diyComponent.value.margin.both) style += 'width: calc((100vw - ' + (diyComponent.value.margin.both*4) + 'rpx - 20rpx) / 2);' |
||||
|
else style += 'width: calc((100vw - 20rpx) / 2 );' |
||||
|
} |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const goodsBtnCss = computed(() => { |
||||
|
var style = ''; |
||||
|
if (diyComponent.value.btnStyle.style == 'button' && diyComponent.value.btnStyle.aroundRadius) style += 'border-radius:' + diyComponent.value.btnStyle.aroundRadius * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.btnStyle.startBgColor && diyComponent.value.btnStyle.endBgColor){ |
||||
|
style += `background:linear-gradient(${diyComponent.value.btnStyle.startBgColor},${diyComponent.value.btnStyle.endBgColor});`; |
||||
|
}else{ |
||||
|
style += 'background-color:' + (diyComponent.value.btnStyle.startBgColor || diyComponent.value.btnStyle.endBgColor) + ';'; |
||||
|
} |
||||
|
if (diyComponent.value.btnStyle.textColor) style += 'color:' + diyComponent.value.btnStyle.textColor + ';'; |
||||
|
if (diyComponent.value.btnStyle.style == 'button' && diyComponent.value.btnStyle.fontWeight) style += 'font-weight: bold;'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const style2Width = computed(() => { |
||||
|
var style = ''; |
||||
|
if(diyComponent.value.margin && diyComponent.value.margin.both) style += 'calc((100vw - ' + (diyComponent.value.margin.both*4) + 'rpx - 20rpx) / 2)' |
||||
|
else style += 'calc((100vw - 20rpx) / 2 )' |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const style3Css = computed(() => { |
||||
|
var style = ''; |
||||
|
style += 'padding:0 20rpx;'; |
||||
|
if (diyComponent.value.margin && diyComponent.value.margin.both){style += 'width: calc(100vw - ' + ((diyComponent.value.margin.both * 4) + 40) + 'rpx);'} |
||||
|
else{style += 'box-sizing: border-box; width: 100vw;';} |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
//商品样式三 |
||||
|
const itemStyle3 = ref(''); |
||||
|
const setItemStyle3 = ()=>{ |
||||
|
// #ifdef MP-WEIXIN |
||||
|
uni.createSelectorQuery().in(instance).select('#warpStyle3-'+diyComponent.value.id).boundingClientRect((res:any) => { |
||||
|
uni.createSelectorQuery().in(instance).select('#item0'+diyComponent.value.id).boundingClientRect((data:any) => { |
||||
|
itemStyle3.value = `margin-right:${(res.width - data.width*3)/2}px;` |
||||
|
}).exec() |
||||
|
}).exec() |
||||
|
// #endif |
||||
|
// #ifdef H5 |
||||
|
itemStyle3.value= 'margin-right:14rpx;' |
||||
|
// #endif |
||||
|
} |
||||
|
|
||||
|
const getGoodsListFn = () => { |
||||
|
let data = { |
||||
|
num: (diyComponent.value.source == 'all' || diyComponent.value.source == 'category') ? diyComponent.value.num : '', |
||||
|
goods_ids: diyComponent.value.source == 'custom' ? diyComponent.value.goods_ids : '', |
||||
|
goods_category: diyComponent.value.source == 'category' ? diyComponent.value.goods_category : '', |
||||
|
order: diyComponent.value.sortWay |
||||
|
} |
||||
|
getGoodsComponents(data).then((res) => { |
||||
|
goodsList.value = res.data; |
||||
|
|
||||
|
// 数据为空时隐藏整个组件 |
||||
|
// if(goodsList.value.length == 0 && diyComponent.value.pageStyle) { |
||||
|
// diyComponent.value.pageStyle = ''; |
||||
|
// } |
||||
|
|
||||
|
skeleton.loading = false; |
||||
|
emits('loadingFn', res.data) |
||||
|
if(diyComponent.value.componentBgUrl) { |
||||
|
setTimeout(() => { |
||||
|
const query = uni.createSelectorQuery().in(instance); |
||||
|
query.select('.diy-shop-goods-list').boundingClientRect((data: any) => { |
||||
|
if(data) height.value = data.height; |
||||
|
}).exec(); |
||||
|
}, 1000) |
||||
|
} |
||||
|
nextTick(()=>{ |
||||
|
setTimeout(()=>{ |
||||
|
if(diyComponent.value.style == 'style-3') setItemStyle3() |
||||
|
},500) |
||||
|
}) |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const initSkeleton = ()=> { |
||||
|
if (diyComponent.value.style == 'style-1') { |
||||
|
|
||||
|
// 单列 风格 |
||||
|
skeleton.type = 'list' |
||||
|
skeleton.type = 'list' |
||||
|
skeleton.config = { |
||||
|
textRows: 2 |
||||
|
}; |
||||
|
} else if (diyComponent.value.style == 'style-2') { |
||||
|
|
||||
|
// 两列 风格 |
||||
|
skeleton.type = 'waterfall' |
||||
|
skeleton.config = { |
||||
|
headHeight: '320rpx', |
||||
|
gridRows: 1, |
||||
|
textRows: 2, |
||||
|
textWidth: ['100%', '80%'] |
||||
|
}; |
||||
|
} else if (diyComponent.value.style == 'style-3') { |
||||
|
|
||||
|
// 横向滑动 风格 |
||||
|
skeleton.type = 'waterfall' |
||||
|
skeleton.config = { |
||||
|
gridRows: 1, |
||||
|
gridColumns: 3, |
||||
|
headHeight: '200rpx', |
||||
|
textRows: 2, |
||||
|
textWidth: ['100%', '80%'] |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const instance = getCurrentInstance(); |
||||
|
const height = ref(0) |
||||
|
|
||||
|
onMounted(() => { |
||||
|
refresh(); |
||||
|
// 装修模式下刷新 |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
watch( |
||||
|
() => diyComponent.value, |
||||
|
(newValue, oldValue) => { |
||||
|
if (newValue && newValue.componentName == 'GoodsList') { |
||||
|
nextTick(() => { |
||||
|
const query = uni.createSelectorQuery().in(instance); |
||||
|
query.select('.diy-shop-goods-list').boundingClientRect((data: any) => { |
||||
|
if(data) height.value = data.height; |
||||
|
}).exec(); |
||||
|
if(diyComponent.value.style == 'style-3') setItemStyle3() |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
) |
||||
|
}else{ |
||||
|
watch( |
||||
|
() => diyComponent.value, |
||||
|
(newValue, oldValue) => { |
||||
|
refresh(); |
||||
|
}, |
||||
|
{deep: true} |
||||
|
) |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
const refresh = () => { |
||||
|
// 装修模式下设置默认图 |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
let obj = { |
||||
|
goods_cover_thumb_mid: "", |
||||
|
goods_name: "商品名称", |
||||
|
sale_num: "100", |
||||
|
unit: "件", |
||||
|
goodsSku:{price:100} |
||||
|
}; |
||||
|
goodsList.value.push(obj); |
||||
|
goodsList.value.push(obj); |
||||
|
nextTick(()=>{ |
||||
|
if(diyComponent.value.style == 'style-3') setItemStyle3() |
||||
|
}) |
||||
|
}else{ |
||||
|
initSkeleton(); |
||||
|
getGoodsListFn(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
const toLink = (data: any) => { |
||||
|
redirect({ url: '/addon/shop/pages/goods/detail', param: { goods_id: data.goods_id } }) |
||||
|
} |
||||
|
</script> |
||||
|
<style lang="scss" scoped> |
||||
|
@import '@/addon/shop/styles/common.scss'; |
||||
|
.biserial-goods-list{ |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
grid-gap: 10px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,340 @@ |
|||||
|
<template> |
||||
|
<view class="overflow-hidden"> |
||||
|
<scroll-view scroll-x="true" class="many-goods-list-head" :class="diyComponent.headStyle" :scroll-into-view="'a' + cateIndex" :style="warpCss"> |
||||
|
<template v-if="diyComponent.headStyle == 'style-3'"> |
||||
|
<template v-if="diyComponent.source == 'custom'"> |
||||
|
<view v-for="(item,index) in diyComponent.list" :key="index" :class="['flex-col inline-flex items-center justify-center', { 'pr-[40rpx]': (index != diyComponent.list.length-1) }]" @click="changeCateIndex(item,index)"> |
||||
|
<image :style="{ borderRadius : (diyComponent.aroundRadius * 2) + 'rpx' }" :class="['w-[90rpx] h-[90rpx] overflow-hidden border-[2rpx] border-solid border-transparent', {'border-[var(--primary-color)]': index == cateIndex }]" v-if="item.imageUrl" :src="img(item.imageUrl)" mode="aspectFit"/> |
||||
|
<image :style="{ borderRadius : (diyComponent.aroundRadius * 2) + 'rpx' }" :class="['w-[90rpx] h-[90rpx] overflow-hidden border-[2rpx] border-solid border-transparent', {'border-[var(--primary-color)]': index == cateIndex }]" v-else :src="img('static/resource/images/diy/figure.png')" mode="scaleToFill" /> |
||||
|
<text :class="['text-[28rpx] mt-[16rpx]', {'font-500 text-[var(--primary-color)]' : index == cateIndex }]">{{ item.title }}</text> |
||||
|
</view> |
||||
|
</template> |
||||
|
<template v-else-if="diyComponent.source == 'goods_category'"> |
||||
|
<view class="pr-[40rpx] inline-flex flex-col items-center justify-center" @click="changeGoodsCategory({ category_id : 0 })"> |
||||
|
<image :style="{ borderRadius : (diyComponent.aroundRadius * 2) + 'rpx' }" :class="['w-[90rpx] h-[90rpx] overflow-hidden border-[2rpx] border-solid border-transparent', {'border-[var(--primary-color)]': currentCategoryId == 0}]" :src="img('static/resource/images/diy/figure.png')" mode="scaleToFill" /> |
||||
|
<text :class="['text-[28rpx] mt-[16rpx]', {'font-500 text-[var(--primary-color)]': currentCategoryId == 0}]">全部</text> |
||||
|
</view> |
||||
|
<view v-for="(item,index) in goodsCategoryListData" :key="index" :class="['flex-col inline-flex items-center justify-center', { 'pr-[40rpx]': (index != goodsCategoryListData.length-1) }]" @click="changeGoodsCategory(item)"> |
||||
|
<image :style="{ borderRadius : (diyComponent.aroundRadius * 2) + 'rpx' }" :class="['w-[90rpx] h-[90rpx] overflow-hidden border-[2rpx] border-solid border-transparent', {'border-[var(--primary-color)]': currentCategoryId == item.category_id}]" v-if="item.image" :src="img(item.image)" mode="aspectFit"/> |
||||
|
<image :style="{ borderRadius : (diyComponent.aroundRadius * 2) + 'rpx' }" :class="['w-[90rpx] h-[90rpx] overflow-hidden border-[2rpx] border-solid border-transparent', {'border-[var(--primary-color)]': currentCategoryId == item.category_id}]" v-else :src="img('static/resource/images/diy/figure.png')" mode="scaleToFill" /> |
||||
|
<text :class="['text-[28rpx] mt-[16rpx]', {'font-500 text-[var(--primary-color)]' : currentCategoryId == item.category_id}]">{{ item.category_name }}</text> |
||||
|
</view> |
||||
|
</template> |
||||
|
</template> |
||||
|
<template v-else> |
||||
|
<view v-for="(item, index) in diyComponent.list" class="scroll-item" :class="[diyComponent.headStyle, { active: index == cateIndex }]" :id="'a' + index" :key="index" @click="changeCateIndex(item, index)"> |
||||
|
<view class="cate" v-if="diyComponent.headStyle == 'style-1'"> |
||||
|
<view class="name">{{ item.title }}</view> |
||||
|
<view class="desc" :v-if="item.desc">{{ item.desc }}</view> |
||||
|
</view> |
||||
|
<view v-if="diyComponent.headStyle == 'style-2'" class="cate"> |
||||
|
<view class="name">{{ item.title }}</view> |
||||
|
<text class="nc-iconfont nc-icon-xiaolian-2 !text-[40rpx] text-[var(--primary-color)] transform" v-if="index == cateIndex"></text> |
||||
|
</view> |
||||
|
<view v-if="diyComponent.headStyle == 'style-4'" class="cate"> |
||||
|
<view class="name">{{ item.title }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
</scroll-view> |
||||
|
|
||||
|
<diy-goods-list class="many-goods-list-body" v-if="goodsValue" :value="goodsValue"></diy-goods-list> |
||||
|
|
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
// 多商品组 组件 |
||||
|
import { ref, computed, watch, onMounted } from 'vue'; |
||||
|
import { img } from '@/utils/common'; |
||||
|
import useDiyStore from '@/app/stores/diy'; |
||||
|
import diyGoodsList from '@/addon/shop/components/diy/goods-list/index.vue'; |
||||
|
import { getGoodsCategoryList } from '@/addon/shop/api/goods'; |
||||
|
|
||||
|
const props = defineProps(['component', 'index']); |
||||
|
const diyStore = useDiyStore(); |
||||
|
|
||||
|
const diyComponent = computed(() => { |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
return diyStore.value[props.index]; |
||||
|
} else { |
||||
|
return props.component; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const warpCss = computed(() => { |
||||
|
var style = ''; |
||||
|
if(diyComponent.value.componentStartBgColor) { |
||||
|
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`; |
||||
|
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';'; |
||||
|
} |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
onMounted(() => { |
||||
|
refresh(); |
||||
|
// 装修模式下刷新 |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
watch( |
||||
|
() => diyComponent.value, |
||||
|
(newValue, oldValue) => { |
||||
|
if (newValue && newValue.componentName == 'ManyGoodsList') { |
||||
|
refresh(); |
||||
|
} |
||||
|
} |
||||
|
) |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
const refresh = () => { |
||||
|
// 装修模式下设置默认图 |
||||
|
if(diyComponent.value.headStyle == 'style-3' && diyComponent.value.source == 'goods_category' && diyComponent.value.goods_category){ |
||||
|
getGoodsCategoryFn(diyComponent.value.goods_category); |
||||
|
}else{ |
||||
|
changeCateIndex(diyComponent.value.list[0], 0,true) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const cateIndex = ref(0) // 当前选中的分类id |
||||
|
|
||||
|
const goodsValue:any = ref(null); |
||||
|
|
||||
|
const changeCateIndex = (item:any, index:any, isFirst:any = false)=> { |
||||
|
// 装修模式禁止跳转 |
||||
|
if (diyStore.mode == 'decorate' && !isFirst) return; |
||||
|
|
||||
|
cateIndex.value = index; |
||||
|
refreshGoodsList({ |
||||
|
source: item.source, |
||||
|
goods_category: item.goods_category, |
||||
|
goods_ids: item.goods_ids |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const goodsCategoryListData = ref([]) |
||||
|
const currentCategoryId = ref(0) // 当前选中的分类id |
||||
|
|
||||
|
// 获取商品分类列表 |
||||
|
const getGoodsCategoryFn = (pid:any)=> { |
||||
|
getGoodsCategoryList({ |
||||
|
pid |
||||
|
}).then(res => { |
||||
|
if (res.code == 1) { |
||||
|
goodsCategoryListData.value = res.data; |
||||
|
if (goodsCategoryListData.value) { |
||||
|
refreshGoodsList({ |
||||
|
source: 'category', |
||||
|
goods_category: '' |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 切换商品分类,查询商品列表数据 |
||||
|
const changeGoodsCategory = (item :any)=> { |
||||
|
// 装修模式禁止跳转 |
||||
|
if (diyStore.mode == 'decorate') return; |
||||
|
currentCategoryId.value = item.category_id |
||||
|
refreshGoodsList({ |
||||
|
source: 'category', |
||||
|
goods_category: currentCategoryId.value |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const refreshGoodsList = (obj:any)=> { |
||||
|
goodsValue.value = { |
||||
|
style: diyComponent.value.style, |
||||
|
margin: diyComponent.value.margin, |
||||
|
source: obj.source, |
||||
|
|
||||
|
num: diyComponent.value.num, |
||||
|
sortWay: diyComponent.value.sortWay, |
||||
|
|
||||
|
goodsNameStyle: diyComponent.value.goodsNameStyle, |
||||
|
priceStyle: diyComponent.value.priceStyle, |
||||
|
saleStyle: diyComponent.value.saleStyle, |
||||
|
btnStyle: diyComponent.value.btnStyle, |
||||
|
labelStyle: diyComponent.value.labelStyle, |
||||
|
|
||||
|
elementBgColor: diyComponent.value.elementBgColor, |
||||
|
topElementRounded: diyComponent.value.topElementRounded, |
||||
|
bottomElementRounded: diyComponent.value.bottomElementRounded |
||||
|
}; |
||||
|
|
||||
|
if (obj.goods_category) { |
||||
|
goodsValue.value.goods_category = obj.goods_category |
||||
|
} |
||||
|
if (obj.goods_ids && obj.goods_ids.length) { |
||||
|
goodsValue.value.goods_ids = obj.goods_ids |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.many-goods-list-head { |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 5; |
||||
|
width: 100%; |
||||
|
white-space: nowrap; |
||||
|
box-sizing: border-box; |
||||
|
padding: 20rpx 0 10rpx; |
||||
|
margin-bottom:20rpx; |
||||
|
position: relative; |
||||
|
|
||||
|
&.style-1{ |
||||
|
padding: 26rpx 0 14rpx; |
||||
|
} |
||||
|
|
||||
|
&.style-2{ |
||||
|
height: 100rpx; |
||||
|
padding: 20rpx 0 16rpx; |
||||
|
} |
||||
|
|
||||
|
&.style-3{ |
||||
|
padding: 26rpx 20rpx; |
||||
|
background-color: #fff; |
||||
|
margin-bottom: 20rpx; |
||||
|
width: 100%; |
||||
|
white-space: nowrap; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
|
||||
|
&.style-4{ |
||||
|
padding-bottom: 0; |
||||
|
} |
||||
|
|
||||
|
.scroll-item { |
||||
|
display: inline-block; |
||||
|
text-align: center; |
||||
|
width: auto; |
||||
|
padding: 0 20rpx; |
||||
|
|
||||
|
&.style-1{ |
||||
|
&:first-child { |
||||
|
width: calc(25% - 20rpx); |
||||
|
padding-left: 0; |
||||
|
} |
||||
|
|
||||
|
&.active { |
||||
|
.name{ |
||||
|
color: var(--primary-color); |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
.desc { |
||||
|
color: #ffffff; |
||||
|
border-radius: 20rpx; |
||||
|
background-color: var(--primary-color); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.name { |
||||
|
font-size: 30rpx; |
||||
|
color: #333; |
||||
|
line-height: 1; |
||||
|
} |
||||
|
|
||||
|
.cate { |
||||
|
display: inline-block; |
||||
|
} |
||||
|
|
||||
|
.desc { |
||||
|
font-size: 22rpx; |
||||
|
color: #999; |
||||
|
height: 38rpx; |
||||
|
line-height: 38rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
margin-top: 10rpx; |
||||
|
min-width: 110rpx; |
||||
|
max-width: 220rpx; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
padding: 0 10rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&.style-2{ |
||||
|
.cate{ |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
position: relative; |
||||
|
} |
||||
|
.name{ |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
line-height: 32rpx; |
||||
|
} |
||||
|
&.active { |
||||
|
.name{ |
||||
|
color:var(--primary-color); |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
.nc-iconfont{ |
||||
|
position: absolute; |
||||
|
bottom: -35rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
&.style-4{ |
||||
|
padding: 0 10rpx 14rpx; |
||||
|
.cate{ |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
position: relative; |
||||
|
padding: 10rpx 12rpx; |
||||
|
background-color: #F6F6F6; |
||||
|
border-radius: 12rpx; |
||||
|
min-width: 136rpx; |
||||
|
box-sizing: border-box; |
||||
|
border: 2rpx solid #F6F6F6; |
||||
|
} |
||||
|
.name{ |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
line-height: 32rpx; |
||||
|
} |
||||
|
&.active { |
||||
|
.cate{ |
||||
|
background-color: var(--primary-color-light); |
||||
|
border-color: var(--primary-color); |
||||
|
position: relative; |
||||
|
&::after{ |
||||
|
content: ""; |
||||
|
width: 18rpx; |
||||
|
height: 18rpx; |
||||
|
position: absolute; |
||||
|
background-color: var(--primary-color-light); |
||||
|
border: 2rpx solid transparent; |
||||
|
border-bottom-color: var(--primary-color); |
||||
|
border-right-color: var(--primary-color); |
||||
|
bottom: -12rpx; |
||||
|
left: 50%; |
||||
|
transform: translateX(-50%) rotate(45deg); |
||||
|
border-bottom-right-radius: 10rpx; |
||||
|
} |
||||
|
} |
||||
|
.name{ |
||||
|
color: var(--primary-color); |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
.nc-iconfont{ |
||||
|
position: absolute; |
||||
|
bottom: -35rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,234 @@ |
|||||
|
<template> |
||||
|
<x-skeleton :type="skeleton.type" :loading="skeleton.loading" :config="skeleton.config"> |
||||
|
<view class="overflow-hidden" :style="warpCss"> |
||||
|
<view :style="maskLayer"></view> |
||||
|
<view class="diy-shop-exchange-goods-list relative flex flex-wrap justify-between" v-if="goodsList.length"> |
||||
|
<view class="overflow-hidden bg-[#fff] flex flex-col box-border w-[calc(50%-10rpx)] rounded-[var(--rounded-mid)]" :class="{'mt-[20rpx]': index > 1}" :style="itemCss" v-for="(item,index) in goodsList" :key="item.goods_id" @click="toLink(item)"> |
||||
|
<u--image :width="style2Width" :height="style2Width" radius="var(--rounded-mid)" :src="img(item.goods_cover_thumb_mid || '')" model="aspectFill"> |
||||
|
<template #error> |
||||
|
<image class="rounded-[var(--rounded-mid)] overflow-hidden" :style="{'width': style2Width,'height': style2Width}" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image> |
||||
|
</template> |
||||
|
</u--image> |
||||
|
<view class="flex-1 pt-[10rpx] pb-[20rpx] px-[16rpx] flex flex-col justify-between"> |
||||
|
<view class="text-[#333] leading-[40rpx] text-[28rpx] multi-hidden" :style="{ color : diyComponent.goodsNameStyle.color, fontWeight : diyComponent.goodsNameStyle.fontWeight }">{{item.names}}</view> |
||||
|
<view class="text-[22rpx] leading-[28rpx] mt-[10rpx] text-[var(--text-color-light9)]" :style="{ color : diyComponent.saleStyle.color }">已兑{{item.total_exchange_num}}人</view> |
||||
|
<view class="flex justify-between flex-wrap items-center mt-[16rpx]" > |
||||
|
<view class="flex flex-col"> |
||||
|
<view :style="{ color : diyComponent.priceStyle.mainColor }" class="text-[var(--price-text-color)] price-font ml-[2rpx] flex items-baseline"> |
||||
|
<text class="text-[32rpx]">{{ item.point }}</text> |
||||
|
<text class="text-[24rpx] ml-[4rpx]">积分</text> |
||||
|
</view> |
||||
|
<view v-if="item.price&&item.price>0" class="flex items-center mt-[6rpx] price-font"> |
||||
|
<text :style="{ color : diyComponent.priceStyle.mainColor }" class="text-[#333] font-400 text-[32rpx]">+{{ parseFloat(item.price).toFixed(2) }}</text> |
||||
|
<text :style="{ color : diyComponent.priceStyle.mainColor }" class="text-[var(--price-text-color)] font-400 ml-[4rpx] text-[24rpx]">元</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="w-[120rpx] h-[54rpx] text-[22rpx] flex-center !text-[#fff] m-0 rounded-full primary-btn-bg remove-border text-center" shape="circle">去兑换</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view v-else-if="!goodsList.length" class="empty-page"> |
||||
|
<image class="img" :src="img('static/resource/images/system/empty.png')" model="aspectFit" /> |
||||
|
<view class="desc">暂无商品</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</x-skeleton> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
// 积分商品列表 |
||||
|
import { ref,reactive,computed, watch, onMounted, nextTick,getCurrentInstance } from 'vue'; |
||||
|
import { redirect, img } from '@/utils/common'; |
||||
|
import useDiyStore from '@/app/stores/diy'; |
||||
|
import { getExchangeComponentsList } from '@/addon/shop/api/point'; |
||||
|
|
||||
|
const props = defineProps(['component', 'index','value']); |
||||
|
const diyStore = useDiyStore(); |
||||
|
|
||||
|
const skeleton = reactive({ |
||||
|
type: '', |
||||
|
loading: diyStore.mode == 'decorate' ? false : true, |
||||
|
config: {} |
||||
|
}) |
||||
|
|
||||
|
const goodsList = ref<Array<any>>([]); |
||||
|
|
||||
|
const diyComponent = computed(() => { |
||||
|
if(props.value) { |
||||
|
return props.value; |
||||
|
}else if (diyStore.mode == 'decorate') { |
||||
|
return diyStore.value[props.index]; |
||||
|
} else { |
||||
|
return props.component; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const warpCss = computed(() => { |
||||
|
var style = ''; |
||||
|
style += 'position:relative;'; |
||||
|
if(diyComponent.value.componentStartBgColor) { |
||||
|
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`; |
||||
|
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';'; |
||||
|
} |
||||
|
|
||||
|
if(diyComponent.value.componentBgUrl) { |
||||
|
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`; |
||||
|
style += 'background-size: cover;background-repeat: no-repeat;'; |
||||
|
} |
||||
|
|
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
// 背景图加遮罩层 |
||||
|
const maskLayer = computed(()=>{ |
||||
|
var style = ''; |
||||
|
if(diyComponent.value.componentBgUrl) { |
||||
|
style += 'position:absolute;top:0;width:100%;'; |
||||
|
style += `background: rgba(0,0,0,${diyComponent.value.componentBgAlpha / 10});`; |
||||
|
style += `height:${height.value}px;`; |
||||
|
|
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
} |
||||
|
|
||||
|
return style; |
||||
|
}); |
||||
|
|
||||
|
const itemCss = computed(() => { |
||||
|
var style = ''; |
||||
|
if (diyComponent.value.topElementRounded) style += 'border-top-left-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topElementRounded) style += 'border-top-right-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const style2Width = computed(() => { |
||||
|
var style = ''; |
||||
|
if(diyComponent.value.margin && diyComponent.value.margin.both) style += 'calc((100vw - ' + (diyComponent.value.margin.both*4) + 'rpx - 20rpx) / 2)' |
||||
|
else style += 'calc((100vw - 20rpx) / 2 )' |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const getGoodsListFn = () => { |
||||
|
let data = { |
||||
|
order: diyComponent.value.sortWay, |
||||
|
num: diyComponent.value.source == 'all' ? diyComponent.value.num : '', |
||||
|
ids: diyComponent.value.source == 'custom' ? diyComponent.value.goods_ids.join(',') : '', |
||||
|
} |
||||
|
getExchangeComponentsList(data).then((res) => { |
||||
|
goodsList.value = res.data; |
||||
|
skeleton.loading = false; |
||||
|
|
||||
|
// 数据为空时隐藏整个组件 |
||||
|
// if(goodsList.value.length == 0) { |
||||
|
// diyComponent.value.pageStyle = ''; |
||||
|
// } |
||||
|
if(diyComponent.value.componentBgUrl) { |
||||
|
setTimeout(() => { |
||||
|
const query = uni.createSelectorQuery().in(instance); |
||||
|
query.select('.diy-shop-exchange-goods-list').boundingClientRect((data: any) => { |
||||
|
height.value = data.height; |
||||
|
}).exec(); |
||||
|
}, 1000) |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const initSkeleton = ()=> { |
||||
|
if (diyComponent.value.style == 'style-1') { |
||||
|
|
||||
|
// 单列 风格 |
||||
|
skeleton.type = 'list' |
||||
|
skeleton.type = 'list' |
||||
|
skeleton.config = { |
||||
|
textRows: 2 |
||||
|
}; |
||||
|
} else if (diyComponent.value.style == 'style-2') { |
||||
|
|
||||
|
// 两列 风格 |
||||
|
skeleton.type = 'waterfall' |
||||
|
skeleton.config = { |
||||
|
headHeight: '320rpx', |
||||
|
gridRows: 1, |
||||
|
textRows: 2, |
||||
|
textWidth: ['100%', '80%'] |
||||
|
}; |
||||
|
} else if (diyComponent.value.style == 'style-3') { |
||||
|
|
||||
|
// 横向滑动 风格 |
||||
|
skeleton.type = 'waterfall' |
||||
|
skeleton.config = { |
||||
|
gridRows: 1, |
||||
|
gridColumns: 3, |
||||
|
headHeight: '200rpx', |
||||
|
textRows: 2, |
||||
|
textWidth: ['100%', '80%'] |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const instance = getCurrentInstance(); |
||||
|
const height = ref(0) |
||||
|
|
||||
|
onMounted(() => { |
||||
|
refresh(); |
||||
|
// 装修模式下刷新 |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
watch( |
||||
|
() => diyComponent.value, |
||||
|
(newValue, oldValue) => { |
||||
|
if (newValue && newValue.componentName == 'ShopExchangeGoods') { |
||||
|
nextTick(() => { |
||||
|
const query = uni.createSelectorQuery().in(instance); |
||||
|
query.select('.diy-shop-exchange-goods-list').boundingClientRect((data: any) => { |
||||
|
height.value = data.height; |
||||
|
}).exec(); |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
) |
||||
|
}else{ |
||||
|
watch( |
||||
|
() => diyComponent.value, |
||||
|
(newValue, oldValue) => { |
||||
|
refresh(); |
||||
|
}, |
||||
|
{deep: true} |
||||
|
) |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
const refresh = () => { |
||||
|
// 装修模式下设置默认图 |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
let obj = { |
||||
|
image: "", |
||||
|
names: "商品名称", |
||||
|
total_exchange_num: 100, |
||||
|
point: 100, |
||||
|
price:100 |
||||
|
}; |
||||
|
goodsList.value.push(obj); |
||||
|
goodsList.value.push(obj); |
||||
|
}else{ |
||||
|
initSkeleton(); |
||||
|
getGoodsListFn(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const toLink = (data: any) => { |
||||
|
redirect({ url: '/addon/shop/pages/point/detail', param: { id: data.id } }) |
||||
|
} |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
</style> |
||||
@ -0,0 +1,125 @@ |
|||||
|
<template> |
||||
|
<view :style="warpCss" class="shop-exchange-info-wrap"> |
||||
|
<view class="pl-[60rpx] pt-[71rpx] pb-[133rpx] min-h-[382rpx] box-border text-[#fff]"> |
||||
|
<!-- #ifdef MP-WEIXIN --> |
||||
|
<view :style="navbarInnerStyle"></view> |
||||
|
<!-- #endif --> |
||||
|
<template v-if="token"> |
||||
|
<view class="text-[34rpx] leading-[48rpx]">我的积分</view> |
||||
|
<view class="text-[80rpx] font-500 price-font leading-[112rpx]">{{memberPoint.point||0}}</view> |
||||
|
<view class="text-[24rpx] font-400 leading-[34rpx]">今日获得:{{memberPoint.today_point||0}}</view> |
||||
|
</template> |
||||
|
<template v-else> |
||||
|
<view class="pt-[42rpx] title">积分当钱花</view> |
||||
|
<view class="text-[26rpx] leading-[36rpx] text-[#FEF2C0] mt-[10rpx]">做任务可领积分</view> |
||||
|
</template> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { computed, ref, watch,onMounted } from 'vue' |
||||
|
import { getExchangePoint } from '@/addon/shop/api/point'; |
||||
|
import { img, getToken } from '@/utils/common'; |
||||
|
import { t } from '@/locale' |
||||
|
import useDiyStore from '@/app/stores/diy' |
||||
|
|
||||
|
const props = defineProps(['component', 'index','global']); |
||||
|
|
||||
|
const diyStore = useDiyStore(); |
||||
|
const loading = ref(true) |
||||
|
const diyComponent = computed(() => { |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
return diyStore.value[props.index]; |
||||
|
} else { |
||||
|
return props.component; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const warpCss = computed(() => { |
||||
|
var style = ''; |
||||
|
if (diyComponent.value.bgUrl) { |
||||
|
style += 'background-image:url(' + img(diyComponent.value.bgUrl) + ');'; |
||||
|
style += 'background-size: 100%;'; |
||||
|
style += 'backgroundPosition: bottom;'; |
||||
|
style += 'background-repeat: no-repeat;'; |
||||
|
} |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
|
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
onMounted(() => { |
||||
|
refresh(); |
||||
|
}); |
||||
|
|
||||
|
watch( |
||||
|
() => diyComponent.value, |
||||
|
(newValue, oldValue) => { |
||||
|
refresh(); |
||||
|
},{deep: true}) |
||||
|
|
||||
|
const memberPoint :Record<string, any> = ref({}) |
||||
|
|
||||
|
const token = ref(getToken()) |
||||
|
|
||||
|
const refresh = () => { |
||||
|
// 装修模式 |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
memberPoint.value = { |
||||
|
point: 10500, |
||||
|
today_point: 500, |
||||
|
} |
||||
|
} else { |
||||
|
if (!token) { |
||||
|
return; |
||||
|
} |
||||
|
getExchangePointFn() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const getExchangePointFn= async()=> { |
||||
|
const res = await getExchangePoint() |
||||
|
memberPoint.value = res.data |
||||
|
loading.value = false |
||||
|
} |
||||
|
|
||||
|
let menuButtonInfo: any = {}; |
||||
|
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容) |
||||
|
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ |
||||
|
menuButtonInfo = uni.getMenuButtonBoundingClientRect(); |
||||
|
// #endif |
||||
|
|
||||
|
// 导航栏内部盒子的样式 |
||||
|
const navbarInnerStyle = computed(() => { |
||||
|
let style = ''; |
||||
|
// 导航栏宽度,如果在小程序下,导航栏宽度为胶囊的左边到屏幕左边的距离 |
||||
|
// #ifdef MP |
||||
|
if (props.global.topStatusBar.isShow == false) { |
||||
|
style += 'height:' + menuButtonInfo.height + 'px;'; |
||||
|
style += 'padding-top:' + menuButtonInfo.top + 'px;'; |
||||
|
} |
||||
|
// #endif |
||||
|
return style; |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.title{ |
||||
|
width: 240rpx; |
||||
|
height: 58rpx; |
||||
|
font-family: FZLanTingHei-EB-GBK, FZLanTingHei-EB-GBK; |
||||
|
font-weight: 700; |
||||
|
font-size: 48rpx; |
||||
|
line-height: 56rpx; |
||||
|
text-align: left; |
||||
|
color: transparent; |
||||
|
background: linear-gradient(90.00002732075491deg, #FFFDF0 5%, #FEF1B9 88%); |
||||
|
-webkit-background-clip: text; /* 确保背景色可以应用到文字上 */ |
||||
|
background-clip: text; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,190 @@ |
|||||
|
<template> |
||||
|
<view :style="warpCss" class="overflow-hidden"> |
||||
|
<scroll-view scroll-x="true" class="w-[100%] whitespace-nowrap"> |
||||
|
<view class="flex items-start"> |
||||
|
<view class="inline-block" v-for="(item,index) in diyComponent.list" :key="index"> |
||||
|
<view class="w-[460rpx] px-[20rpx] pb-[20rpx] pt-[20rpx] flex flex-col items-start" :class="{'mr-[20rpx]': (diyComponent.list.length-1) != index}" :style="swiperItemCss(item)" v-if="listGoods[item.rankIds[0]] && listGoods[item.rankIds[0]].length > 0"> |
||||
|
<view class="flex items-center h-[50rpx]"> |
||||
|
<image class="w-[30rpx] h-[30rpx] mr-[10rpx]" v-if="item.imgUrl" :src="img(item.imgUrl)" mode="aspectFill"></image> |
||||
|
<view :style="{'color': item.textColor}"> |
||||
|
<text class="text-[30rpx] font-bold" v-if="item.text">{{ item.text }}</text> |
||||
|
<text class="text-[30rpx] font-bold" v-else>{{ listGoodsNames[item.rankIds[0]] }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="flex items-center mt-[6rpx]" :style="{'color': item.subTitle.textColor}" @click="diyStore.toRedirect(item.subTitle.link)"> |
||||
|
<text class="text-[24rpx] font-500" v-if="item.subTitle.text">{{item.subTitle.text}}</text> |
||||
|
<text class="iconfont iconyouV6xx !text-[24rpx]" v-if="item.subTitle.text"></text> |
||||
|
</view> |
||||
|
<view class="mt-[24rpx]"> |
||||
|
<view class="flex bg-[rgba(255,255,255,0.94)] p-[10rpx] rounded-[16rpx] mb-[16rpx]" v-for="(goods, gIndex) in listGoods[item.rankIds[0]]" :class="{'mb-0': gIndex === listGoods[item.rankIds[0]].length - 1}" @click="toDetail(goods.goods_id)"> |
||||
|
<view class="relative w-[130rpx] h-[130rpx] mr-[16rpx]"> |
||||
|
<image class="absolute top-[6rpx] left-[8rpx] w-[30rpx] h-[36rpx]" :style="{ zIndex:2 }" :src="getRankBadge(goods.rank_num)" mode="aspectFill"></image> |
||||
|
<view class="absolute top-[2rpx] left-[-3rpx] flex items-center justify-center w-[50rpx] h-[50rpx]" :style="{ zIndex: 3 }"> |
||||
|
<text class="text-[20rpx] font-bold text-[#fff]">{{ goods.rank_num }}</text> |
||||
|
</view> |
||||
|
<u--image radius="var(--goods-rounded-big)" width="130rpx" height="130rpx" :src="img(goods.goods_cover_thumb_mid || '')" model="aspectFill"> |
||||
|
<template #error> |
||||
|
<image class="w-[130rpx] h-[130rpx] rounded-[var(--goods-rounded-big)] overflow-hidden" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image> |
||||
|
</template> |
||||
|
</u--image> |
||||
|
</view> |
||||
|
<view class="flex flex-col"> |
||||
|
<view class="leading-[1.3] multi-hidden w-[290rpx] text-[28rpx] whitespace-normal">{{goods.goods_name}}</view> |
||||
|
<view class="flex items-center justify-between mt-[auto]"> |
||||
|
<view class="text-[var(--price-text-color)] price-font flex items-baseline"> |
||||
|
<text class="text-[24rpx] font-500">¥</text> |
||||
|
<text class="text-[40rpx] font-500">{{ diyGoods.goodsPrice(goods).toFixed(2).split('.')[0] }}</text> |
||||
|
<text class="text-[24rpx] font-500">.{{ diyGoods.goodsPrice(goods).toFixed(2).split('.')[1] }}</text> |
||||
|
</view> |
||||
|
<view> |
||||
|
<text class="iconfont icongouwuche3 text-[var(--primary-color)] border-[2rpx] border-solid border-[var(--primary-color)] rounded-[50%] text-[22rpx] p-[6rpx]"></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
// 排行榜 |
||||
|
import { ref,reactive,computed, watch, onMounted } from 'vue'; |
||||
|
import useDiyStore from '@/app/stores/diy'; |
||||
|
import { redirect, img } from '@/utils/common'; |
||||
|
import { getRankComponentsGoodsList } from '@/addon/shop/api/rank' |
||||
|
import { useGoods } from '@/addon/shop/hooks/useGoods' |
||||
|
|
||||
|
const props = defineProps(['component', 'index']); |
||||
|
const diyGoods = useGoods(); |
||||
|
const diyStore = useDiyStore(); |
||||
|
|
||||
|
const diyComponent = computed(() => { |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
return diyStore.value[props.index]; |
||||
|
} else { |
||||
|
return props.component; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const warpCss = computed(() => { |
||||
|
var style = ''; |
||||
|
style += 'position:relative;'; |
||||
|
if(diyComponent.value.componentStartBgColor) { |
||||
|
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`; |
||||
|
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';'; |
||||
|
} |
||||
|
|
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const swiperItemCss = (data: any)=> { |
||||
|
var style = ''; |
||||
|
if (data.listFrame.startColor) { |
||||
|
if (data.listFrame.startColor && data.listFrame.endColor) style += 'background: linear-gradient(90deg, ' + data.listFrame.startColor + ', ' + data.listFrame.endColor + ');'; |
||||
|
else style = 'background-color:' + data.listFrame.startColor + ';'; |
||||
|
} |
||||
|
if (data.bgUrl) { |
||||
|
style += 'background-image:' + 'url(' + img(data.bgUrl) + ');'; |
||||
|
style += 'background-size: 100%;'; |
||||
|
style += 'background-repeat: no-repeat;'; |
||||
|
} |
||||
|
|
||||
|
if (diyComponent.value.topElementRounded) style += 'border-top-left-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topElementRounded) style += 'border-top-right-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
} |
||||
|
|
||||
|
watch( |
||||
|
() => diyComponent.value, |
||||
|
(newValue, oldValue) => { |
||||
|
refresh(); |
||||
|
},{deep: true}) |
||||
|
|
||||
|
const refresh = () => { |
||||
|
// 装修模式下刷新 |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
if(diyComponent.value.componentName == 'ShopGoodsRanking') { |
||||
|
const fakeGoods = { |
||||
|
goods_name: '商品名称', |
||||
|
goods_cover_thumb_mid: '', |
||||
|
goodsSku: { |
||||
|
price: 10 |
||||
|
}, |
||||
|
rank_num: 0 // 初始化为 0,后续会递增 |
||||
|
}; |
||||
|
diyComponent.value.list.forEach(item => { |
||||
|
if (!item.goodsList) { |
||||
|
item.goodsList = []; |
||||
|
} |
||||
|
|
||||
|
if (item.goodsList.length === 0) { |
||||
|
const fakeGoodsList = []; |
||||
|
// 生成三个商品,并为每个商品的 rank_num 递增 |
||||
|
for (let i = 0; i < 3; i++) { |
||||
|
const newItem = { ...fakeGoods, rank_num: i + 1 }; // rank_num 从 1 开始递增 |
||||
|
fakeGoodsList.push(newItem); |
||||
|
} |
||||
|
listGoods[item.rankIds[0]] = fakeGoodsList; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} else { |
||||
|
getRankGoodsListFn(); |
||||
|
} |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
const listGoods = reactive({}); |
||||
|
const listGoodsNames = reactive({}); |
||||
|
const getRankGoodsListFn = () => { |
||||
|
diyComponent.value.list.forEach((item) => { |
||||
|
const rank_id = Array.isArray(item.rankIds) ? item.rankIds[0] : 0; |
||||
|
const data = { |
||||
|
rank_id: item.source === 'custom' ? rank_id : 0 |
||||
|
}; |
||||
|
getRankComponentsGoodsList(data).then((res) => { |
||||
|
if (res.data && res.data.goods_list.length > 0) { |
||||
|
listGoods[item.rankIds[0]] = res.data.goods_list; |
||||
|
listGoodsNames[item.rankIds[0]] = res.data.name; |
||||
|
} |
||||
|
}).catch((error) => { |
||||
|
console.error('获取商品数据失败:', error); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
function getRankBadge(sort: any) { |
||||
|
switch (sort) { |
||||
|
case 1: |
||||
|
return img('addon/shop/rank/rank_first.png'); |
||||
|
case 2: |
||||
|
return img('addon/shop/rank/rank_second.png'); |
||||
|
case 3: |
||||
|
return img('addon/shop/rank/rank_third.png'); |
||||
|
default: |
||||
|
return img('addon/shop/rank/rank.png'); |
||||
|
} |
||||
|
} |
||||
|
const toDetail = (id: string | number) => { |
||||
|
redirect({ url: '/addon/shop/pages/goods/detail', param: { goods_id: id }, mode: 'navigateTo' }) |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
refresh(); |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
</style> |
||||
@ -0,0 +1,203 @@ |
|||||
|
<template> |
||||
|
<x-skeleton :type="skeleton.type" :loading="skeleton.loading" :config="skeleton.config"> |
||||
|
<view :style="warpCss" v-if="goodsNum"> |
||||
|
<view class="w-full"> |
||||
|
<scroll-view :id="'warpStyle-'+diyComponent.id" class="whitespace-nowrap h-[341rpx] w-full" :scroll-x="true"> |
||||
|
<block v-for="(item,index) in goodsList" :key="index"> |
||||
|
<view v-if="item.info" :id="'item'+index+diyComponent.id" class="w-[224rpx] h-[341rpx] mr-[20rpx] inline-block bg-[#fff] box-border overflow-hidden" :class="{'!mr-[0rpx]' : index == (goodsList.length-1)}" :style="itemCss+itemStyle" @click="toLink(item)"> |
||||
|
<view class="w-full h-[134rpx]" :style="listFrameStyle(item)"> |
||||
|
<view class="flex pl-[16rpx] pr-[20rpx] justify-between h-[63rpx] items-center"> |
||||
|
<view class="text-[28rpx] leading-[34rpx] flex-1 mr-[8rpx]" :style="{'color':item.title.textColor}">{{item.title.text}}</view> |
||||
|
<view class="w-[68rpx] h-[34rpx] text-[22rpx] text-center leading-[34rpx] text-[#fff] rounded-[17rpx]" :style="moreTitleStyle(item)">{{item.moreTitle.text}}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="mt-[-71rpx] h-[278rpx] w-full px-[20rpx] pt-[18rpx] box-border bg-white" :style="itemCss"> |
||||
|
<view class="flex items-center justify-center w-[184rpx] h-[184rpx]"> |
||||
|
<u--image width="184rpx" height="184rpx" :radius="'var(--goods-rounded-small)'" :src="img(item.info.goods_cover_thumb_mid || '')" model="aspectFill"> |
||||
|
<template #error> |
||||
|
<image class="w-[184rpx] h-[184rpx] rounded-[var(--goods-rounded-small)]" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image> |
||||
|
</template> |
||||
|
</u--image> |
||||
|
</view> |
||||
|
<view class="pt-[12rpx]"> |
||||
|
<view class="h-[44rpx] pl-[4rpx] min-w-[168rpx] box-border flex justify-between items-center mx-auto border-[2rpx] border-solid border-[var(--primary-color)] rounded-[20rpx]" :style="{'border-color':item.button.color}"> |
||||
|
<view class="text-[var(--price-text-color)] font-bold price-font flex items-baseline leading-[40rpx] flex-1 justify-center" > |
||||
|
<view class="leading-[1] max-w-[105rpx] truncate" :style="{ color : diyComponent.priceStyle.mainColor }"> |
||||
|
<text class="text-[18rpx] font-400 mr-[2rpx]">¥</text> |
||||
|
<text class="text-[28rpx] font-500">{{ parseFloat(diyGoods.goodsPrice(item.info.goodsSku)).toFixed(2)}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="w-[70rpx] box-border text-right text-[#fff] pr-[8rpx] text-[22rpx] font-500 leading-[44rpx] rounded-tr-[20rpx] rounded-br-[20rpx] rounded-tl-[24rpx] relative" :style="{'background-color':item.button.color}"> |
||||
|
<text>{{item.button.text}}</text> |
||||
|
<image class="w-[24rpx] h-[44rpx] absolute top-[-2rpx] left-0" :src="img('/addon/shop/Union.png')" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</block> |
||||
|
</scroll-view> |
||||
|
|
||||
|
</view> |
||||
|
</view> |
||||
|
</x-skeleton> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
// 商品推荐 |
||||
|
import { ref,reactive,computed, watch, onMounted, nextTick,getCurrentInstance } from 'vue'; |
||||
|
import { redirect, img, deepClone } from '@/utils/common'; |
||||
|
import useDiyStore from '@/app/stores/diy'; |
||||
|
import { getGoodsComponents } from '@/addon/shop/api/goods'; |
||||
|
import {useGoods} from '@/addon/shop/hooks/useGoods' |
||||
|
|
||||
|
const diyGoods = useGoods(); |
||||
|
const props = defineProps(['component', 'index','value']); |
||||
|
const diyStore = useDiyStore(); |
||||
|
const emits = defineEmits(['loadingFn']); //商品数据加载完成之后触发 |
||||
|
|
||||
|
const goodsNum = ref(0); |
||||
|
|
||||
|
const skeleton = reactive({ |
||||
|
type: '', |
||||
|
loading: false, |
||||
|
config: {} |
||||
|
}) |
||||
|
|
||||
|
const goodsList = ref<Array<any>>([]); |
||||
|
|
||||
|
const diyComponent = computed(() => { |
||||
|
if(props.value) { |
||||
|
return props.value; |
||||
|
}else if (diyStore.mode == 'decorate') { |
||||
|
return diyStore.value[props.index]; |
||||
|
} else { |
||||
|
return props.component; |
||||
|
} |
||||
|
}) |
||||
|
const warpCss = computed(() => { |
||||
|
var style = ''; |
||||
|
style += 'position:relative;'; |
||||
|
if(diyComponent.value.componentStartBgColor) { |
||||
|
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`; |
||||
|
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';'; |
||||
|
} |
||||
|
|
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const itemCss = computed(() => { |
||||
|
var style = ''; |
||||
|
if (diyComponent.value.elementBgColor) style += 'background-color:' + diyComponent.value.elementBgColor + ';'; |
||||
|
if (diyComponent.value.topElementRounded) style += 'border-top-left-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topElementRounded) style += 'border-top-right-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
//listFrame样式 |
||||
|
const listFrameStyle = (item:any)=>{ |
||||
|
var style= ''; |
||||
|
if(item.listFrame.startColor){ |
||||
|
if(item.listFrame.startColor && item.listFrame.endColor) style = `background:linear-gradient( 110deg, ${item.listFrame.startColor} 0%, ${item.listFrame.endColor} 100%);`; |
||||
|
else style = 'background-color:' + item.listFrame.startColor + ';'; |
||||
|
} |
||||
|
return style |
||||
|
} |
||||
|
|
||||
|
//moreTitle样式 |
||||
|
const moreTitleStyle = (item:any)=>{ |
||||
|
var style= ''; |
||||
|
if(item.moreTitle.startColor){ |
||||
|
if(item.moreTitle.startColor && item.moreTitle.endColor) style = `background:linear-gradient( 0deg, ${item.moreTitle.startColor} 0%, ${item.moreTitle.endColor} 100%);`; |
||||
|
else style = 'background-color:' + item.moreTitle.startColor + ';'; |
||||
|
} |
||||
|
return style |
||||
|
} |
||||
|
|
||||
|
//商品样式 |
||||
|
const itemStyle = ref(''); |
||||
|
const setItemStyle = ()=>{ |
||||
|
// #ifdef MP-WEIXIN |
||||
|
// uni.createSelectorQuery().in(instance).select('#warpStyle-'+diyComponent.value.id).boundingClientRect((res:any) => { |
||||
|
// uni.createSelectorQuery().in(instance).select('#item0'+diyComponent.value.id).boundingClientRect((data:any) => { |
||||
|
// itemStyle.value = `margin-right:${(res.width - data.width*3)/2}px;` |
||||
|
// }).exec() |
||||
|
// }).exec() |
||||
|
// #endif |
||||
|
// #ifdef H5 |
||||
|
// itemStyle.value= 'margin-right:19rpx;' |
||||
|
// #endif |
||||
|
if(diyComponent.value.margin && diyComponent.value.margin.both) itemStyle.value = 'width: calc((100vw - ' + (diyComponent.value.margin.both*4) + 'rpx - 40rpx) / 3);' |
||||
|
else itemStyle.value = 'width: calc((100vw - 40rpx) / 3 );' |
||||
|
} |
||||
|
|
||||
|
setItemStyle(); |
||||
|
|
||||
|
const getGoodsListFn = () => { |
||||
|
let data: any = {} |
||||
|
if(diyComponent.value.source == 'all') { |
||||
|
data.num = diyComponent.value.list.length; |
||||
|
}else if(diyComponent.value.source == 'custom') { |
||||
|
data.goods_ids = diyComponent.value.goods_ids; |
||||
|
} |
||||
|
|
||||
|
getGoodsComponents(data).then((res) => { |
||||
|
let goodsObj = res.data; |
||||
|
goodsNum.value = goodsObj.length || 0; |
||||
|
diyComponent.value.list.filter((el:any, index)=>{ |
||||
|
el.info=deepClone(goodsObj[index]) |
||||
|
}); |
||||
|
goodsList.value = deepClone(diyComponent.value.list) |
||||
|
skeleton.loading = false; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const instance = getCurrentInstance(); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
refresh(); |
||||
|
}); |
||||
|
watch( |
||||
|
() => diyComponent.value, |
||||
|
(newValue, oldValue) => { |
||||
|
refresh(); |
||||
|
}, |
||||
|
) |
||||
|
const refresh = () => { |
||||
|
// 装修模式下设置默认图 |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
nextTick(() => { |
||||
|
if(diyComponent.value && diyComponent.value.list){ |
||||
|
goodsList.value = diyComponent.value.list.map((el:any)=>{ |
||||
|
let obj = deepClone(el) |
||||
|
obj.info={ |
||||
|
goods_cover_thumb_mid:'', |
||||
|
goodsSku:{ |
||||
|
price:'10.00' |
||||
|
} |
||||
|
} |
||||
|
return obj |
||||
|
}) |
||||
|
goodsNum.value = 3; |
||||
|
setItemStyle() |
||||
|
} |
||||
|
}) |
||||
|
}else{ |
||||
|
getGoodsListFn(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const toLink = (data: any) => { |
||||
|
redirect({ url: '/addon/shop/pages/goods/detail', param: { goods_id: data.info.goods_id } }) |
||||
|
} |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
</style> |
||||
@ -0,0 +1,262 @@ |
|||||
|
<template> |
||||
|
<view :style="warpCss"> |
||||
|
<view class="px-[30rpx] pt-[30rpx] box-border pb-[30rpx]" :class="{'!pb-[120rpx]':diyComponent.isShowAccount}"> |
||||
|
<!-- #ifdef MP-WEIXIN --> |
||||
|
<view :style="navbarInnerStyle"></view> |
||||
|
<!-- #endif --> |
||||
|
<view v-if="info" class="flex items-center"> |
||||
|
<!-- 唤起获取微信 --> |
||||
|
<u-avatar :src="img(info.headimg)" :size="'110rpx'" leftIcon="none" :default-url="img('static/resource/images/default_headimg.png')" @click="clickAvatar" /> |
||||
|
<view class="ml-[20rpx] flex-1"> |
||||
|
<view class="text-[#ffffff] flex items-baseline flex-wrap" :style="{ color : diyComponent.textColor }"> |
||||
|
<view class="text-[32rpx] truncate max-w-[320rpx] font-500 leading-[38rpx]">{{ info.nickname }}</view> |
||||
|
<view class="text-[26rpx] font-400 leading-[28rpx] ml-[10rpx]" v-if="info.mobile">{{info.mobile.replace(info.mobile.substring(3,7), "****")}}</view> |
||||
|
|
||||
|
<!-- #ifdef H5 --> |
||||
|
<view v-else-if="!info.mobile" @click="bindMobileFn" class="text-[22rpx] ml-[10rpx] px-[6rpx] border-[1rpx] border-solid border-[#E3E4E9] rounded-[8rpx] h-[34rpx] flex-center" :style="diyComponent.textColor ? { boxShadow: '0 0 0 1rpx ' + diyComponent.textColor, border: 'none' } : {}">{{ t('bindMobile') }}</view> |
||||
|
<!-- #endif --> |
||||
|
|
||||
|
<!-- #ifdef MP-WEIXIN --> |
||||
|
<button v-else-if="!info.mobile" class="text-[22rpx] ml-[10rpx] bg-[#fff] px-[6rpx] border-[1rpx] border-solid border-[#E3E4E9] rounded-[8rpx] h-[37rpx] flex-center mr-0" :style="diyComponent.textColor ? { boxShadow: '0 0 0 1rpx ' + diyComponent.textColor, border: 'none' } : {}" open-type="getPhoneNumber" @getphonenumber="memberStore.bindMobile">{{t('bindMobile')}}</button> |
||||
|
<!-- #endif --> |
||||
|
|
||||
|
</view> |
||||
|
<view class="text-[#666] text-[24rpx] font-400 leading-[28rpx] mt-[14rpx]" :style="{ color : diyComponent.uidTextColor }">UID:{{ info.member_no }}</view> |
||||
|
</view> |
||||
|
<text @click="redirect({ url: '/app/pages/setting/index' })" class="nc-iconfont nc-icon-shezhiV6xx1 text-[38rpx] ml-[10rpx]" :style="{ color : diyComponent.textColor }"></text> |
||||
|
</view> |
||||
|
<view v-else class="flex items-center"> |
||||
|
<u-avatar :src="img('static/resource/images/default_headimg.png')" :size="'100rpx'" @click="toLogin" /> |
||||
|
<view class="ml-[20rpx] flex-1" @click="toLogin"> |
||||
|
<view class="text-[32rpx] font-500 leading-[38rpx]" :style="{ color : diyComponent.textColor }"> |
||||
|
{{ t('login') }}/{{ t('register') }} |
||||
|
</view> |
||||
|
</view> |
||||
|
<view @click="redirect({ url: '/app/pages/setting/index' })"> |
||||
|
<text class="nc-iconfont nc-icon-shezhiV6xx1 text-[38rpx] ml-[10rpx]" :style="{ color : diyComponent.textColor }"></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="flex mt-[40rpx] items-center" v-if="diyComponent.isShowAccount"> |
||||
|
<view class="text-center w-[33.333%] flex-shrink-0"> |
||||
|
<view class="text-[36rpx] mb-[20rpx] font-500 price-font"> |
||||
|
<view @click="redirect({url: '/app/pages/member/balance'})" :style="{ color : diyComponent.textColor }">{{ money }}</view> |
||||
|
</view> |
||||
|
<view class="text-[22rpx] font-400"> |
||||
|
<view @click="redirect({url: '/app/pages/member/balance'})" :style="{ color : '#666' }">{{ t('balance') }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="text-center w-[33.333%] flex-shrink-0"> |
||||
|
<view class="text-[36rpx] mb-[20rpx] font-500 price-font"> |
||||
|
<view @click="redirect({url: '/app/pages/member/point'})" :style="{ color : diyComponent.textColor }">{{ parseInt(info?.point) || 0 }}</view> |
||||
|
</view> |
||||
|
<view class="text-[22rpx] font-400"> |
||||
|
<view @click="redirect({url: '/app/pages/member/point'})" :style="{ color : '#666' }">{{ t('point') }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="text-center w-[33.333%] flex-shrink-0" @click="redirect({ url: '/addon/shop/pages/member/my_coupon' })"> |
||||
|
<view class="text-[36rpx] mb-[20rpx] font-500 price-font"> |
||||
|
<view :style="{ color : diyComponent.textColor }">{{ couponCount }}</view> |
||||
|
</view> |
||||
|
<view class="text-[22rpx] font-400"> |
||||
|
<view :style="{ color : '#666' }">{{ t('coupon') }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- #ifdef MP-WEIXIN --> |
||||
|
<information-filling ref="infoFillRef"></information-filling> |
||||
|
<!-- #endif --> |
||||
|
|
||||
|
<!-- 强制绑定手机号 --> |
||||
|
<bind-mobile ref="bindMobileRef" /> |
||||
|
|
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { computed, ref, watch } from 'vue' |
||||
|
import useMemberStore from '@/stores/member' |
||||
|
import { useLogin } from '@/hooks/useLogin' |
||||
|
import { getMyCouponCount } from '@/addon/shop/api/coupon' |
||||
|
import { img, isWeixinBrowser, redirect, urlDeconstruction, moneyFormat } from '@/utils/common' |
||||
|
import { t } from '@/locale' |
||||
|
import { wechatSync } from '@/app/api/system' |
||||
|
import useDiyStore from '@/app/stores/diy' |
||||
|
import useConfigStore from '@/stores/config' |
||||
|
import bindMobile from '@/components/bind-mobile/bind-mobile.vue'; |
||||
|
|
||||
|
const props = defineProps(['component', 'index','global']); |
||||
|
|
||||
|
const configStore = useConfigStore() |
||||
|
const diyStore = useDiyStore(); |
||||
|
|
||||
|
const diyComponent = computed(() => { |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
return diyStore.value[props.index]; |
||||
|
} else { |
||||
|
return props.component; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const warpCss = computed(() => { |
||||
|
var style = ''; |
||||
|
if(diyComponent.value.componentStartBgColor) { |
||||
|
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`; |
||||
|
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';'; |
||||
|
} |
||||
|
if (diyComponent.value.bgUrl) { |
||||
|
style += 'background-image:url(' + img(diyComponent.value.bgUrl) + ');'; |
||||
|
style += 'background-size: 100%;'; |
||||
|
style += 'background-repeat: no-repeat;'; |
||||
|
} |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
|
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const memberStore = useMemberStore() |
||||
|
|
||||
|
// #ifdef H5 |
||||
|
const { query } = urlDeconstruction(location.href) |
||||
|
if (query.code && isWeixinBrowser()) { |
||||
|
setTimeout(() =>{ |
||||
|
wechatSync({ code: query.code }).then(res => { |
||||
|
memberStore.getMemberInfo() |
||||
|
}) |
||||
|
},1500) |
||||
|
} |
||||
|
// #endif |
||||
|
|
||||
|
const info = computed(() => { |
||||
|
// 装修模式 |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
return { |
||||
|
headimg: '', |
||||
|
nickname: '昵称', |
||||
|
member_level_name: '普通会员', |
||||
|
balance: 0, |
||||
|
point: 0, |
||||
|
money: 0, |
||||
|
mobile: '155****0549', |
||||
|
member_no: 'NIU0000021' |
||||
|
} |
||||
|
} else { |
||||
|
getMyCouponCountFn() |
||||
|
return memberStore.info; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const money = computed(() => { |
||||
|
if (info.value) { |
||||
|
let m = parseFloat(info.value.balance) + parseFloat(info.value.money) |
||||
|
return moneyFormat(m.toString()); |
||||
|
} else { |
||||
|
return 0; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const toLogin = () => { |
||||
|
let normalLogin = !configStore.login.is_username && !configStore.login.is_mobile && !configStore.login.is_bind_mobile; // 未开启普通登录 |
||||
|
let authRegisterLogin = !configStore.login.is_auth_register; // 自动注册登录 |
||||
|
|
||||
|
// #ifdef H5 |
||||
|
if (isWeixinBrowser()) { |
||||
|
// 微信浏览器 |
||||
|
if (normalLogin && authRegisterLogin) { |
||||
|
uni.showToast({ title: '商家未开启登录注册', icon: 'none' }) |
||||
|
} else if (configStore.login.is_username || configStore.login.is_mobile || configStore.login.is_bind_mobile) { |
||||
|
useLogin().setLoginBack({ url: '/addon/shop/pages/member/index' }) |
||||
|
} else if (normalLogin && configStore.login.is_auth_register && configStore.login.is_force_access_user_info) { |
||||
|
// 判断是否开启第三方自动注册登录,并且开启强制获取用户信息 |
||||
|
useLogin().getAuthCode({ scopes: 'snsapi_userinfo' }) |
||||
|
} else if (normalLogin && configStore.login.is_auth_register && !configStore.login.is_force_access_user_info) { |
||||
|
// 判断是否开启第三方自动注册登录,并且关闭强制获取用户信息 |
||||
|
useLogin().getAuthCode({ scopes: 'snsapi_base' }) |
||||
|
} |
||||
|
} else { |
||||
|
// 普通浏览器 |
||||
|
if (normalLogin) { |
||||
|
uni.showToast({ title: '商家未开启登录注册', icon: 'none' }) |
||||
|
} else if (configStore.login.is_username || configStore.login.is_mobile || configStore.login.is_bind_mobile) { |
||||
|
useLogin().setLoginBack({ url: '/addon/shop/pages/member/index' }) |
||||
|
} |
||||
|
} |
||||
|
// #endif |
||||
|
|
||||
|
// #ifdef MP |
||||
|
if (normalLogin && authRegisterLogin) { |
||||
|
uni.showToast({ title: '商家未开启登录注册', icon: 'none' }) |
||||
|
} else if (configStore.login.is_username || configStore.login.is_mobile || configStore.login.is_bind_mobile) { |
||||
|
useLogin().setLoginBack({ url: '/addon/shop/pages/member/index' }) |
||||
|
} else if (normalLogin && configStore.login.is_auth_register && !configStore.login.is_force_access_user_info) { |
||||
|
// 判断是否开启第三方自动注册登录 |
||||
|
useLogin().getAuthCode() |
||||
|
} else if (configStore.login.is_auth_register && configStore.login.is_force_access_user_info) { |
||||
|
// 开启了第三方自动注册登录,但是需要强制获取昵称 |
||||
|
useLogin().setLoginBack({ url: '/addon/shop/pages/member/index' }) |
||||
|
} else if (configStore.login.is_auth_register && configStore.login.is_bind_mobile) { |
||||
|
// 开启了第三方自动注册登录,但是需要强制获取手机号 |
||||
|
useLogin().setLoginBack({ url: '/addon/shop/pages/member/index' }) |
||||
|
} |
||||
|
// #endif |
||||
|
|
||||
|
} |
||||
|
|
||||
|
const infoFillRef:any = ref(false) |
||||
|
const clickAvatar = () => { |
||||
|
// #ifdef MP-WEIXIN |
||||
|
infoFillRef.value.show = true |
||||
|
// #endif |
||||
|
|
||||
|
// #ifdef H5 |
||||
|
if (isWeixinBrowser()) { |
||||
|
useLogin().getAuthCode({ scopes: 'snsapi_userinfo' }) |
||||
|
} else { |
||||
|
redirect({ url: '/app/pages/member/personal' }) |
||||
|
} |
||||
|
// #endif |
||||
|
} |
||||
|
|
||||
|
const couponCount = ref(0) |
||||
|
const getMyCouponCountFn= async()=>{ |
||||
|
try { |
||||
|
const res = await getMyCouponCount({status:1}) |
||||
|
couponCount.value = res.data |
||||
|
} catch (e){ |
||||
|
couponCount.value = 0 |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
//强制绑定手机号 |
||||
|
const bindMobileRef: any = ref(null) |
||||
|
const bindMobileFn = () =>{ |
||||
|
bindMobileRef.value.open() |
||||
|
} |
||||
|
|
||||
|
let menuButtonInfo: any = {}; |
||||
|
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容) |
||||
|
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ |
||||
|
menuButtonInfo = uni.getMenuButtonBoundingClientRect(); |
||||
|
// #endif |
||||
|
|
||||
|
// 导航栏内部盒子的样式 |
||||
|
const navbarInnerStyle = computed(() => { |
||||
|
let style = ''; |
||||
|
// 导航栏宽度,如果在小程序下,导航栏宽度为胶囊的左边到屏幕左边的距离 |
||||
|
// #ifdef MP |
||||
|
if (props.global.topStatusBar.isShow == false) { |
||||
|
style += 'height:' + menuButtonInfo.height + 'px;'; |
||||
|
style += 'padding-top:' + menuButtonInfo.top + 'px;'; |
||||
|
} |
||||
|
// #endif |
||||
|
return style; |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
</style> |
||||
@ -0,0 +1,476 @@ |
|||||
|
<template> |
||||
|
<view class="shop-newcomer overflow-hidden" :style="warpCss" v-if="list && Object.keys(list).length"> |
||||
|
<view class="style-1 p-[20rpx]" v-if="diyComponent.style.value == 'style-1'"> |
||||
|
<view class="head flex justify-between items-center mb-[16rpx]" @click="toListFn()"> |
||||
|
<image v-if="diyComponent.textImg" class="h-[34rpx] w-[auto]" :src="img(diyComponent.textImg)" mode="heightFix"></image> |
||||
|
<view class="time-wrap flex items-center ml-[auto]" v-show="timeData && Object.keys(timeData).length"> |
||||
|
<text v-if="!getToken() && diyStore.mode != 'decorate'" class="text-[24rpx] font-500" :style="{color: diyComponent.countDown.otherColor}">活动未开始</text> |
||||
|
<block v-else-if="activeState()"> |
||||
|
<text :style="{color: diyComponent.countDown.otherColor}" class="mr-[10rpx] text-[24rpx]">距结束还有</text> |
||||
|
<up-count-down class="text-[#fff] text-[28rpx]" :time="newcomerTime" format="HH:mm:ss" @change="onChange"> |
||||
|
<view class="flex"> |
||||
|
<view class="text-[24rpx] flex items-center"> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-if="dayTransitionHours()">{{ dayTransitionHours() }}</text> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-else>00</text> |
||||
|
<text :style="{color: diyComponent.countDown.otherColor}" class="text-[20rpx] ml-[6rpx] mr-[7rpx]">:</text> |
||||
|
</view> |
||||
|
<view class="text-[24rpx] flex items-center"> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-if="timeData.minutes">{{ timeData.minutes >= 10?timeData.minutes:'0'+timeData.minutes }}</text> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-else>00</text> |
||||
|
<text :style="{color: diyComponent.countDown.otherColor}" class="text-[20rpx] ml-[6rpx] mr-[7rpx]">:</text> |
||||
|
</view> |
||||
|
<view class="text-[24rpx] flex items-center"> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-if="timeData.seconds">{{ timeData.seconds<10 ? '0'+timeData.seconds : timeData.seconds}}</text> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-else>00</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</up-count-down> |
||||
|
</block> |
||||
|
<text v-else class="text-[28rpx]" :style="{color: diyComponent.countDown.otherColor}">活动已结束</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<scroll-view scroll-x="true" class="content"> |
||||
|
<view class="flex"> |
||||
|
<view class="inline-flex bg-[#fff] p-[16rpx] box-border" :style="commonTempCss()" @click="toDetail(list[0])"> |
||||
|
<view class="w-[150rpx] h-[150rpx] flex items-center justify-center"> |
||||
|
<u--image radius="var(--goods-rounded-big)" width="150rpx" height="150rpx" :src="img(list[0].sku_image || '')" model="aspectFill"> |
||||
|
<template #error> |
||||
|
<image class="w-[150rpx] h-[150rpx] rounded-[var(--goods-rounded-big)] overflow-hidden" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image> |
||||
|
</template> |
||||
|
</u--image> |
||||
|
</view> |
||||
|
<view class="flex flex-col ml-[20rpx] py-[4rpx] flex-1"> |
||||
|
<view class="text-[26rpx] w-[200rpx] whitespace-pre-wrap leading-[1.4] multi-hidden" v-if="list[0].goods">{{list[0].goods.goods_name}}</view> |
||||
|
<view class="flex items-center justify-between mt-[auto]"> |
||||
|
<view class="flex flex-1 items-center"> |
||||
|
<text class="text-[20rpx] text-[#FF0000]">¥</text> |
||||
|
<text class="text-[28rpx] font-500 text-[#FF0000] max-w[120rpx] truncate">{{goodsPrice(list[0])}}</text> |
||||
|
</view> |
||||
|
<text class="italic flex items-center justify-center rounded-[40rpx] w-[60rpx] h-[40rpx] leading-1 text-[#fff] font-bold first-btn-bg">抢</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<block v-for="(item,index) in list" :key="index"> |
||||
|
<view v-if="index > 0" class="ml-[10rpx] inline-flex flex-col items-center p-[16rpx] bg-[#fff] box-border" :style="commonTempCss()" @click="toDetail(item)"> |
||||
|
<view class="w-[110rpx] h-[110rpx] flex items-center justify-center"> |
||||
|
<u--image radius="var(--goods-rounded-big)" width="110rpx" height="110rpx" :src="img(item.sku_image || '')" model="aspectFill"> |
||||
|
<template #error> |
||||
|
<image class="w-[110rpx] h-[110rpx] rounded-[var(--goods-rounded-big)] overflow-hidden" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image> |
||||
|
</template> |
||||
|
</u--image> |
||||
|
</view> |
||||
|
<view class="flex items-center mt-[auto]"> |
||||
|
<text class="text-[24rpx] font-500">¥</text> |
||||
|
<text class="text-[24rpx] font-500 truncate">{{goodsPrice(item)}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</block> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
<view class="style-2 p-[20rpx]" v-if="diyComponent.style.value == 'style-2'"> |
||||
|
<view class="head flex justify-between items-center mb-[16rpx]" @click="toListFn()"> |
||||
|
<image v-if="diyComponent.textImg" class="h-[34rpx] w-[auto]" :src="img(diyComponent.textImg)" mode="heightFix"></image> |
||||
|
<view class="time-wrap flex items-center ml-[auto]" v-show="timeData && Object.keys(timeData).length"> |
||||
|
<text v-if="!getToken() && diyStore.mode != 'decorate'" class="text-[24rpx] font-500" :style="{color: diyComponent.countDown.otherColor}">活动未开始</text> |
||||
|
<block v-else-if="activeState()"> |
||||
|
<text :style="{color: diyComponent.countDown.otherColor}" class="mr-[10rpx] text-[24rpx]">倒计时</text> |
||||
|
<up-count-down class="text-[#fff] text-[28rpx]" :time="newcomerTime" format="DD:HH:mm" @change="onChange"> |
||||
|
<view class="flex"> |
||||
|
<view class="text-[24rpx] flex items-center"> |
||||
|
<text class="time-num" :style="countDownTextCss" v-if="timeData.days">{{ timeData.days }}</text> |
||||
|
<text class="time-num" :style="countDownTextCss" v-else>0</text> |
||||
|
<text :style="{color: diyComponent.countDown.otherColor}" class="text-[22rpx] ml-[6rpx] mr-[7rpx]">天</text> |
||||
|
</view> |
||||
|
<view class="text-[24rpx] flex items-center"> |
||||
|
<text class="time-num" :style="countDownTextCss" v-if="timeData.hours">{{ timeData.hours>=10?timeData.hours:'0'+timeData.hours}}</text> |
||||
|
<text class="time-num" :style="countDownTextCss" v-else>00</text> |
||||
|
<text :style="{color: diyComponent.countDown.otherColor}" class="text-[22rpx] ml-[6rpx] mr-[7rpx]">时</text> |
||||
|
</view> |
||||
|
<view class="text-[24rpx] flex items-center"> |
||||
|
<text class="time-num" :style="countDownTextCss" v-if="timeData.minutes">{{ timeData.minutes >= 10?timeData.minutes:'0'+timeData.minutes }}</text> |
||||
|
<text class="time-num" :style="countDownTextCss" v-else>00</text> |
||||
|
<text :style="{color: diyComponent.countDown.otherColor}" class="text-[22rpx] ml-[6rpx] mr-[7rpx]">分</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</up-count-down> |
||||
|
</block> |
||||
|
<text v-else class="text-[28rpx]" :style="{color: diyComponent.countDown.otherColor}">活动已结束</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<scroll-view scroll-x="true" class="content"> |
||||
|
<view class="flex"> |
||||
|
<view v-for="(item,index) in list" :key="index" class="item-bg mr-[10rpx] inline-flex flex-col items-center p-[6rpx] bg-[#fff] box-border" :style="commonTempCss()" @click="toDetail(item)"> |
||||
|
<view class="flex items-center justify-center w-[146rpx] h-[146rpx]"> |
||||
|
<u--image radius="var(--goods-rounded-small)" width="146rpx" height="146rpx" :src="img(item.sku_image || '')" model="aspectFill"> |
||||
|
<template #error> |
||||
|
<image class="w-[146rpx] h-[146rpx] rounded-[var(--goods-rounded-small)] overflow-hidden" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image> |
||||
|
</template> |
||||
|
</u--image> |
||||
|
</view> |
||||
|
<image class="h-[32rpx] w-[auto] mt-[12rpx] mb-[8rpx]" :src="img('addon/shop/diy/newcomer/style_2_img.png')" mode="heightFix"></image> |
||||
|
<view class="flex items-center text-[#fff] pb-[4rpx]"> |
||||
|
<text class="text-[20rpx] font-500">¥</text> |
||||
|
<text class="text-[30rpx] max-w-[120rpx] font-500 truncate">{{goodsPrice(item)}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
<view class="style-3 pt-[20rpx] pb-[10rpx] px-[10rpx]" v-if="diyComponent.style.value == 'style-3'"> |
||||
|
<view class="head flex mx-[10rpx] items-center mb-[12rpx]" @click="toListFn()"> |
||||
|
<image v-if="diyComponent.textImg" class="h-[34rpx] w-[auto] mr-[16rpx]" :src="img(diyComponent.textImg)" mode="heightFix"></image> |
||||
|
<view class="time-wrap flex items-center" v-show="timeData && Object.keys(timeData).length"> |
||||
|
<text v-if="!getToken() && diyStore.mode != 'decorate'" :style="{color: diyComponent.countDown.otherColor}" class="text-[24rpx] font-500">活动未开始</text> |
||||
|
<up-count-down v-else-if="activeState()" class="text-[#fff] text-[28rpx]" :time="newcomerTime" format="HH:mm:ss" @change="onChange"> |
||||
|
<view class="flex"> |
||||
|
<view class="text-[24rpx] flex items-center"> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-if="dayTransitionHours()">{{ dayTransitionHours() }}</text> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-else>00</text> |
||||
|
<text :style="{color: diyComponent.countDown.otherColor}" class="text-[20rpx] font-bold ml-[6rpx] mr-[7rpx]">:</text> |
||||
|
</view> |
||||
|
<view class="text-[24rpx] flex items-center"> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-if="timeData.minutes">{{ timeData.minutes >= 10?timeData.minutes:'0'+timeData.minutes }}</text> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-else>00</text> |
||||
|
<text :style="{color: diyComponent.countDown.otherColor}" class="text-[20rpx] font-bold ml-[6rpx] mr-[7rpx]">:</text> |
||||
|
</view> |
||||
|
<view class="text-[24rpx] flex items-center"> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-if="timeData.seconds">{{ timeData.seconds<10 ? '0'+timeData.seconds : timeData.seconds}}</text> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-else>00</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</up-count-down> |
||||
|
<text v-else :style="{color: diyComponent.countDown.otherColor}" class="text-[26rpx]">活动已结束</text> |
||||
|
</view> |
||||
|
<view class="ml-[auto] rounded-[20rpx] flex items-baseline pl-[16rpx] pr-[10rpx] pt-[10rpx] pb-[10rpx]" :style="subTitleCss" @click.stop="diyStore.toRedirect(diyComponent.subTitle.link)"> |
||||
|
<text class="text-[22rpx]">{{diyComponent.subTitle.text}}</text> |
||||
|
<text class="iconfont iconarrow-right !text-[18rpx] font-bold"></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<scroll-view scroll-x="true" class="content bg-[#fff] box-border p-[16rpx] rounded-[var(--rounded-small)]"> |
||||
|
<view class="flex"> |
||||
|
<view v-for="(item,index) in list" :key="index" class="item-bg inline-flex flex-col items-center box-border" :class="{'mr-[16rpx]': index != (list.length-1)}" :style="commonTempCss()" @click="toDetail(item)"> |
||||
|
<view class="bg-[#f8f8f8] flex items-center justify-center w-[152rpx] h-[152rpx]"> |
||||
|
<u--image radius="var(--goods-rounded-small)" width="152rpx" height="152rpx" :src="img(item.sku_image || '')" model="aspectFill"> |
||||
|
<template #error> |
||||
|
<image class="w-[152rpx] h-[152rpx] rounded-[var(--goods-rounded-small)] overflow-hidden" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image> |
||||
|
</template> |
||||
|
</u--image> |
||||
|
</view> |
||||
|
<image class="h-[32rpx] w-[auto] mt-[12rpx] mb-[10rpx]" :src="img('addon/shop/diy/newcomer/style_3_img.png')" mode="heightFix"></image> |
||||
|
<view class="flex items-center text-[#FF0E00] pb-[2rpx]"> |
||||
|
<text class="text-[20rpx] font-500">¥</text> |
||||
|
<text class="text-[30rpx] max-w-[120rpx] font-500 truncate">{{goodsPrice(item)}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
<view class="style-4 p-[20rpx] pt-[24rpx]" v-if="diyComponent.style.value == 'style-4'" :style="{ background: 'url(' + img('addon/shop/diy/newcomer/style_4_head.png') + ') no-repeat'}"> |
||||
|
<view class="head flex mx-[10rpx] items-center justify-between mb-[24rpx]"> |
||||
|
<image v-if="diyComponent.textImg" class="h-[34rpx] w-[auto]" :src="img(diyComponent.textImg)" mode="heightFix"></image> |
||||
|
<view class="time-wrap ml-[auto] flex items-center -mt-[8rpx]" v-show="timeData && Object.keys(timeData).length"> |
||||
|
<text v-if="!getToken() && diyStore.mode != 'decorate'" :style="{color: diyComponent.countDown.otherColor}" class="w-[200rpx] text-center text-[24rpx] font-500 pb-[4rpx]">活动未开始</text> |
||||
|
<block v-else-if="activeState()"> |
||||
|
<text :style="{color: diyComponent.countDown.otherColor}" class="mr-[8rpx] text-[24rpx]">本场仅剩</text> |
||||
|
<up-count-down class="text-[#fff] text-[28rpx]" :time="newcomerTime" format="HH:mm:ss" @change="onChange"> |
||||
|
<view class="flex"> |
||||
|
<view class="text-[28rpx] flex items-center"> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-if="dayTransitionHours()">{{ dayTransitionHours() }}</text> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-else>00</text> |
||||
|
<text :style="{color: diyComponent.countDown.otherColor}" class="text-[20rpx] ml-[4rpx] font-bold mr-[5rpx]">:</text> |
||||
|
</view> |
||||
|
<view class="text-[28rpx] flex items-center"> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-if="timeData.minutes">{{ timeData.minutes >= 10?timeData.minutes:'0'+timeData.minutes }}</text> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-else>00</text> |
||||
|
<text :style="{color: diyComponent.countDown.otherColor}" class="text-[20rpx] ml-[4rpx] font-bold mr-[5rpx]">:</text> |
||||
|
</view> |
||||
|
<view class="text-[28rpx] flex items-center"> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-if="timeData.seconds">{{ timeData.seconds<10 ? '0'+timeData.seconds : timeData.seconds}}</text> |
||||
|
<text class="time-num font-500" :style="countDownTextCss" v-else>00</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</up-count-down> |
||||
|
</block> |
||||
|
<text v-else :style="{color: diyComponent.countDown.otherColor}" class="w-[200rpx] text-center text-[24rpx] pb-[4rpx]">活动已结束</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<scroll-view scroll-x="true" class="content"> |
||||
|
<view class="flex"> |
||||
|
<view v-for="(item,index) in list" :key="index" class="item-bg inline-flex flex-col items-center box-border" :class="{'mr-[20rpx]': index != (list.length-1)}" :style="commonTempCss()" @click="toDetail(item)"> |
||||
|
<view class="relative flex items-center justify-center w-[100%] h-[130rpx] pt-[40rpx] mb-[10rpx]"> |
||||
|
<u--image radius="var(--goods-rounded-small)" width="130rpx" height="130rpx" :src="img(item.sku_image || '')" model="aspectFill"> |
||||
|
<template #error> |
||||
|
<image class="w-[130rpx] h-[130rpx] rounded-[var(--goods-rounded-small)] overflow-hidden" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image> |
||||
|
</template> |
||||
|
</u--image> |
||||
|
<view class="content-sign text-[20rpx] text-[#fff]">新人价</view> |
||||
|
</view> |
||||
|
<view class="w-[210rpx] relative -right-[2rpx] -bottom-[2rpx] flex items-center text-[#FF0E00] pb-[2rpx]"> |
||||
|
<view class="flex items-center justify-center flex-1"> |
||||
|
<text class="text-[20rpx] font-500">¥</text> |
||||
|
<text class="text-[36rpx] max-w-[140rpx] font-500 truncate">{{goodsPrice(item)}}</text> |
||||
|
</view> |
||||
|
<text class="btn-bg ml-auto">抢</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
// 新人专享 |
||||
|
import { ref,reactive,computed, onMounted } from 'vue'; |
||||
|
import { redirect, img, getToken } from '@/utils/common'; |
||||
|
import useDiyStore from '@/app/stores/diy'; |
||||
|
import { getNewcomersComponentsList } from '@/addon/shop/api/newcomer' |
||||
|
|
||||
|
const props = defineProps(['component', 'index','value']); |
||||
|
const diyStore = useDiyStore(); |
||||
|
|
||||
|
const diyComponent = computed(() => { |
||||
|
if(props.value) { |
||||
|
return props.value; |
||||
|
}else if (diyStore.mode == 'decorate') { |
||||
|
return diyStore.value[props.index]; |
||||
|
} else { |
||||
|
return props.component; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
/********* 倒计时 - start ***********/ |
||||
|
// 使用 reactive 创建响应式对象 |
||||
|
const timeData = ref({}); |
||||
|
// 定义 onChange 方法 |
||||
|
const onChange = (e) => { |
||||
|
timeData.value = e; |
||||
|
}; |
||||
|
const newcomerTime: any = ref(''); |
||||
|
/********* 倒计时 - end ***********/ |
||||
|
|
||||
|
const goodsPrice = (data: any)=> { |
||||
|
let price: any = 0; |
||||
|
if (data && data.newcomer_price) { |
||||
|
price = Number(data.newcomer_price).toFixed(2); |
||||
|
} |
||||
|
return price; |
||||
|
} |
||||
|
|
||||
|
// 将天转换成时 |
||||
|
const dayTransitionHours = ()=>{ |
||||
|
let num = timeData.value.days * 24 + timeData.value.hours; |
||||
|
num = num ? num : 0; |
||||
|
num = num >=10 ? num : '0' + num; |
||||
|
return num; |
||||
|
} |
||||
|
|
||||
|
// 活动状态 |
||||
|
const activeState = ()=>{ |
||||
|
let bool = true; |
||||
|
if(diyStore.mode != 'decorate' && timeData.value.days <= 0 && timeData.value.hours <= 0 && timeData.value.minutes <= 0 && timeData.value.seconds <= 0 && timeData.value.milliseconds <= 0){ |
||||
|
bool = false; |
||||
|
} |
||||
|
return bool; |
||||
|
} |
||||
|
|
||||
|
const warpCss = computed(() => { |
||||
|
var style = ''; |
||||
|
style += 'position:relative;'; |
||||
|
if(diyComponent.value.componentStartBgColor) { |
||||
|
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`; |
||||
|
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';'; |
||||
|
} |
||||
|
|
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
// 倒计时样式 |
||||
|
const countDownTextCss = computed(() => { |
||||
|
var style = ''; |
||||
|
if(diyComponent.value.countDown && diyComponent.value.countDown.numberBg) { |
||||
|
if (diyComponent.value.countDown.numberBg.startColor && diyComponent.value.countDown.numberBg.endColor) style += `background:linear-gradient(${diyComponent.value.countDown.numberBg.startColor},${diyComponent.value.countDown.numberBg.endColor});`; |
||||
|
else{ |
||||
|
style += 'background-color:' + (diyComponent.value.countDown.numberBg.startColor || diyComponent.value.countDown.numberBg.endColor) + ';'; |
||||
|
} |
||||
|
} |
||||
|
if (diyComponent.value.countDown.numberColor) style += 'color:' + diyComponent.value.countDown.numberColor + ';'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
// 公共模块颜色 |
||||
|
const commonTempCss = ()=>{ |
||||
|
var style = ''; |
||||
|
if (diyComponent.value.topElementRounded) style += 'border-top-left-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topElementRounded) style += 'border-top-right-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
} |
||||
|
|
||||
|
// 副标题样式 |
||||
|
const subTitleCss = computed(() => { |
||||
|
var style = ''; |
||||
|
if(diyComponent.value.subTitle) { |
||||
|
if (diyComponent.value.subTitle.startColor && diyComponent.value.subTitle.endColor) style += `background:linear-gradient(to right, ${diyComponent.value.subTitle.startColor},${diyComponent.value.subTitle.endColor});`; |
||||
|
else{ |
||||
|
style += 'background-color:' + (diyComponent.value.subTitle.startColor || diyComponent.value.subTitle.endColor) + ';'; |
||||
|
} |
||||
|
} |
||||
|
if (diyComponent.value.subTitle.textColor) style += 'color:' + diyComponent.value.subTitle.textColor + ';'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const list: any = ref([]) |
||||
|
const getNewcomerListFn = () => { |
||||
|
let data = { |
||||
|
limit: diyComponent.value.source == 'all' ? diyComponent.value.num : '', |
||||
|
sku_ids: diyComponent.value.source == 'custom' ? diyComponent.value.goods_ids : '' |
||||
|
} |
||||
|
getNewcomersComponentsList(data).then((res: any) => { |
||||
|
newcomerTime.value = res.data.validity_time; |
||||
|
let now = new Date(); |
||||
|
let timestamp: any = now.getTime(); |
||||
|
newcomerTime.value = Number(newcomerTime.value) * 1000 - timestamp; |
||||
|
|
||||
|
list.value = res.data.goods_list |
||||
|
|
||||
|
// 数据为空时隐藏整个组件 |
||||
|
// if(!(list.value.length && (newcomerTime.value > 0 && isJoin.value == 0))) { |
||||
|
// diyComponent.value.pageStyle = ''; |
||||
|
// } |
||||
|
|
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 装修模式下刷新 |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
let obj = { |
||||
|
goods: { |
||||
|
goods_name: "商品名称" |
||||
|
}, |
||||
|
sku_image: "", |
||||
|
newcomer_price: 0.01 |
||||
|
}; |
||||
|
list.value.push(obj); |
||||
|
list.value.push(obj); |
||||
|
list.value.push(obj); |
||||
|
} else { |
||||
|
getNewcomerListFn(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// 跳转专享列表 |
||||
|
const toListFn = ()=>{ |
||||
|
redirect({ url: '/addon/shop/pages/newcomer/list'}) |
||||
|
} |
||||
|
// 跳转详情 |
||||
|
const toDetail = (data: any)=> { |
||||
|
redirect({ url: '/addon/shop/pages/goods/detail', param: { sku_id: data.sku_id, type: 'newcomer_discount' } }) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.shop-newcomer{ |
||||
|
.style-1{ |
||||
|
.content{ |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
.first-btn-bg{ |
||||
|
background: linear-gradient( 140deg, #FF9C24 0%, #FF4837 100%); |
||||
|
} |
||||
|
.time-num{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
min-width: 42rpx; |
||||
|
padding: 0 6rpx; |
||||
|
height: 36rpx; |
||||
|
border-radius: 6rpx; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
} |
||||
|
.style-2{ |
||||
|
.content{ |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
.item-bg{ |
||||
|
background: linear-gradient( 140deg, #FF7A41, #FF2E0A); |
||||
|
} |
||||
|
.time-num{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
min-width: 42rpx; |
||||
|
padding: 0 6rpx; |
||||
|
height: 36rpx; |
||||
|
border-radius: 6rpx; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
} |
||||
|
.style-3{ |
||||
|
.content{ |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
.time-num{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
min-width: 42rpx; |
||||
|
padding: 0 6rpx; |
||||
|
height: 36rpx; |
||||
|
border-radius: 6rpx; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
} |
||||
|
.style-4{ |
||||
|
background-size: 100% 110rpx !important; |
||||
|
.content{ |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
.time-wrap{ |
||||
|
margin-right: -14rpx; |
||||
|
.time-num{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
min-width: 32rpx; |
||||
|
height: 36rpx; |
||||
|
border-radius: 6rpx; |
||||
|
font-size: 26rpx; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
} |
||||
|
.item-bg{ |
||||
|
background: linear-gradient(#FFFFFF 60%, #f7f7f7 100%); |
||||
|
} |
||||
|
.btn-bg{ |
||||
|
background: linear-gradient( 140deg, #FE2B2B 0%, #FF7236 100%); |
||||
|
border-radius: 50%; |
||||
|
border-bottom-left-radius: 0; |
||||
|
font-size: 30rpx; |
||||
|
color: #fff; |
||||
|
width: 50rpx; |
||||
|
height: 50rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
.content-sign{ |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
top: 20rpx; |
||||
|
z-index: 1; |
||||
|
background: linear-gradient( 140deg, #FE2B2B 0%, #FF7236 100%); |
||||
|
padding: 6rpx 8rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,156 @@ |
|||||
|
<template> |
||||
|
<view :style="warpCss"> |
||||
|
<view class="diy-text relative"> |
||||
|
<view class="px-[var(--pad-sidebar-m)] pt-[var(--pad-top-m)] pb-[40rpx] flex items-center justify-between"> |
||||
|
<view @click="diyStore.toRedirect(diyComponent.link)"> |
||||
|
<view class="max-w-[200rpx] truncate leading-[1] text-[30rpx]" :style="{ fontSize: diyComponent.fontSize * 2 + 'rpx', color: diyComponent.textColor, fontWeight: (diyComponent.fontWeight == 'normal' ? 500 : diyComponent.fontWeight) }"> |
||||
|
{{ diyComponent.text }} |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="flex items-center"> |
||||
|
<view @click="redirect({ url: '/addon/shop/pages/order/list'})" class="flex items-center"> |
||||
|
<text class="max-w-[200rpx] truncate text-[24rpx]" :style="{ color: diyComponent.more.color }">{{ diyComponent.more.text }}</text> |
||||
|
<text class="nc-iconfont nc-icon-youV6xx text-[24rpx]" :style="{ color: diyComponent.more.color }"></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="pb-[var(--pad-top-m)] px-[var(--pad-sidebar-m)] flex items-center justify-between text-center"> |
||||
|
<view class="flex flex-col items-center w-[20%] flex-shrink-0" @click="toList(1)"> |
||||
|
<view class="relative w-[44rpx] h-[44rpx]"> |
||||
|
<image class="w-[44rpx] h-[44rpx]" :src="img('addon/shop/diy/member/order1.png')" /> |
||||
|
<view v-if="orderInfo.wait_pay" |
||||
|
:class="['absolute left-[35rpx] top-[-10rpx] rounded-[28rpx] h-[28rpx] min-w-[28rpx] text-center leading-[30rpx] bg-[#FF4646] text-[#fff] text-[20rpx] font-500 box-border', orderInfo.wait_pay > 9 ? 'px-[10rpx]' : '']"> |
||||
|
{{ orderInfo.wait_pay > 99 ? "99+" : orderInfo.wait_pay }} |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="mt-[20rpx] leading-[1]" :style="{ |
||||
|
fontSize: diyComponent.item.fontSize * 2 + 'rpx', |
||||
|
color: diyComponent.item.color, |
||||
|
fontWeight: diyComponent.item.fontWeight |
||||
|
}">待付款</view> |
||||
|
</view> |
||||
|
<view class="flex flex-col items-center w-[20%] flex-shrink-0" @click="toList(2)"> |
||||
|
<view class="relative w-[44rpx] h-[44rpx]"> |
||||
|
<image class="w-[44rpx] h-[44rpx]" :src="img('addon/shop/diy/member/order2.png')" /> |
||||
|
<view v-if="orderInfo.wait_shipping" |
||||
|
:class="['absolute left-[35rpx] top-[-10rpx] rounded-[28rpx] h-[28rpx] min-w-[28rpx] text-center leading-[30rpx] bg-[#FF4646] text-[#fff] text-[20rpx] font-500 box-border', orderInfo.wait_shipping > 9 ? 'px-[10rpx]' : '']"> |
||||
|
{{ orderInfo.wait_shipping > 99 ? "99+" : orderInfo.wait_shipping }} |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="mt-[20rpx] leading-[1]" :style="{ |
||||
|
fontSize: diyComponent.item.fontSize * 2 + 'rpx', |
||||
|
color: diyComponent.item.color, |
||||
|
fontWeight: diyComponent.item.fontWeight |
||||
|
}">待发货</view> |
||||
|
</view> |
||||
|
<view class="flex flex-col items-center w-[20%] flex-shrink-0" @click="toList(3)"> |
||||
|
<view class="relative w-[44rpx] h-[44rpx]"> |
||||
|
<image class="w-[44rpx] h-[44rpx]" :src="img('addon/shop/diy/member/order3.png')" /> |
||||
|
<view v-if="orderInfo.wait_take" |
||||
|
:class="['absolute left-[35rpx] top-[-10rpx] rounded-[28rpx] h-[28rpx] min-w-[28rpx] text-center leading-[30rpx] bg-[#FF4646] text-[#fff] text-[20rpx] font-500 box-border', orderInfo.wait_take > 9 ? 'px-[10rpx]' : '']"> |
||||
|
{{ orderInfo.wait_take > 99 ? "99+" : orderInfo.wait_take }} |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="mt-[20rpx] leading-[1]" :style="{ |
||||
|
fontSize: diyComponent.item.fontSize * 2 + 'rpx', |
||||
|
color: diyComponent.item.color, |
||||
|
fontWeight: diyComponent.item.fontWeight |
||||
|
}">待收货</view> |
||||
|
</view> |
||||
|
<view class="flex flex-col items-center w-[20%] flex-shrink-0" @click="toList(5)"> |
||||
|
<view class="relative w-[44rpx] h-[44rpx]"> |
||||
|
<image class="w-[44rpx] h-[44rpx]" :src="img('addon/shop/diy/member/order4.png')" /> |
||||
|
<view v-if="orderInfo.evaluate" |
||||
|
:class="['absolute left-[35rpx] top-[-10rpx] rounded-[28rpx] h-[28rpx] min-w-[28rpx] text-center leading-[30rpx] bg-[#FF4646] text-[#fff] text-[20rpx] font-500 box-border', orderInfo.evaluate > 9 ? 'px-[10rpx]' : '']"> |
||||
|
{{ orderInfo.evaluate > 99 ? "99+" : orderInfo.evaluate }} |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="mt-[20rpx] leading-[1]" :style="{ |
||||
|
fontSize: diyComponent.item.fontSize * 2 + 'rpx', |
||||
|
color: diyComponent.item.color, |
||||
|
fontWeight: diyComponent.item.fontWeight |
||||
|
}">待评价</view> |
||||
|
</view> |
||||
|
<view class="flex flex-col items-center w-[20%] flex-shrink-0" @click="redirect({ url: '/addon/shop/pages/refund/list'})"> |
||||
|
<view class="relative w-[44rpx] h-[44rpx]"> |
||||
|
<image class="w-[44rpx] h-[44rpx]" :src="img('addon/shop/diy/member/order5.png')" /> |
||||
|
<view v-if="orderInfo.refund" |
||||
|
:class="['absolute left-[35rpx] top-[-10rpx] rounded-[28rpx] h-[28rpx] min-w-[28rpx] text-center leading-[30rpx] bg-[#FF4646] text-[#fff] text-[20rpx] font-500 box-border', orderInfo.refund > 9 ? 'px-[10rpx]' : '']"> |
||||
|
{{ orderInfo.refund > 99 ? "99+" : orderInfo.refund }} |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="mt-[20rpx] leading-[1]" :style="{ |
||||
|
fontSize: diyComponent.item.fontSize * 2 + 'rpx', |
||||
|
color: diyComponent.item.color, |
||||
|
fontWeight: diyComponent.item.fontWeight |
||||
|
}">售后/退款</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { ref, computed, watch,onMounted } from 'vue'; |
||||
|
import useDiyStore from '@/app/stores/diy'; |
||||
|
import { img,redirect } from '@/utils/common'; |
||||
|
import {getShopOrderNum} from '@/addon/shop/api/order'; |
||||
|
|
||||
|
const props = defineProps(['component', 'index']); |
||||
|
const diyStore = useDiyStore(); |
||||
|
const diyComponent = computed(() => { |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
return diyStore.value[props.index]; |
||||
|
} else { |
||||
|
return props.component; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
onMounted(() => { |
||||
|
refresh(); |
||||
|
}); |
||||
|
|
||||
|
watch( |
||||
|
() => diyComponent.value, |
||||
|
(newValue, oldValue) => { |
||||
|
refresh(); |
||||
|
},{deep: true}) |
||||
|
|
||||
|
const refresh = () => { |
||||
|
// 装修模式 |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
orderInfo.value = { |
||||
|
} |
||||
|
} else { |
||||
|
getShopOrderNumFn() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const orderInfo = ref({}) |
||||
|
const warpCss = computed(() => { |
||||
|
var style = ''; |
||||
|
style += 'position:relative;'; |
||||
|
if(diyComponent.value.componentStartBgColor) { |
||||
|
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`; |
||||
|
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';'; |
||||
|
} |
||||
|
|
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const getShopOrderNumFn=()=>{ |
||||
|
getShopOrderNum().then((res:any)=>{ |
||||
|
orderInfo.value = res.data |
||||
|
}) |
||||
|
} |
||||
|
const toList = (status:any) => { |
||||
|
redirect({ url: '/addon/shop/pages/order/list', param: { status } }) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
</style> |
||||
@ -0,0 +1,102 @@ |
|||||
|
<template> |
||||
|
<view :style="warpCss"> |
||||
|
<view :style="maskLayer"></view> |
||||
|
<view class="diy-shop-search relative overflow-hidden flex items-center"> |
||||
|
<image :src="img('addon/shop/diy/search_01.png')" class="w-[40rpx] h-[40rpx]" mode="widthFix" @click="toLink('/addon/shop/pages/goods/category')"></image> |
||||
|
<view class="flex-1 ml-[24rpx] rounded-[32rpx] flex items-center bg-[var(--temp-bg)] opacity-90 py-[10rpx] pl-[38rpx] pr-[32rpx] justify-between h-[60rpx] box-border" @click="toLink('/addon/shop/pages/goods/search')"> |
||||
|
<text class="text-[var(--text-color-light9)] text-[26rpx]">{{ diyComponent.text }}</text> |
||||
|
<text class="nc-iconfont nc-icon-sousuo-duanV6xx1 text-[24rpx]"></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
// 搜索 |
||||
|
import { ref,computed, watch, onMounted, nextTick,getCurrentInstance } from 'vue'; |
||||
|
import { img, redirect } from '@/utils/common'; |
||||
|
import useDiyStore from '@/app/stores/diy'; |
||||
|
|
||||
|
const props = defineProps(['component', 'index']); |
||||
|
const diyStore = useDiyStore(); |
||||
|
|
||||
|
const diyComponent = computed(() => { |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
return diyStore.value[props.index]; |
||||
|
} else { |
||||
|
return props.component; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const warpCss = computed(() => { |
||||
|
var style = ''; |
||||
|
if(diyComponent.value.componentStartBgColor) { |
||||
|
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`; |
||||
|
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';'; |
||||
|
} |
||||
|
|
||||
|
if(diyComponent.value.componentBgUrl) { |
||||
|
style += `background-image:url('${ img(diyComponent.value.componentBgUrl) }');`; |
||||
|
style += 'background-size: cover;background-repeat: no-repeat;'; |
||||
|
} |
||||
|
|
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
// 背景图加遮罩层 |
||||
|
const maskLayer = computed(()=>{ |
||||
|
var style = ''; |
||||
|
if(diyComponent.value.componentBgUrl) { |
||||
|
style += 'position:absolute;top:0;width:100%;'; |
||||
|
style += `background: rgba(0,0,0,${diyComponent.value.componentBgAlpha / 10});`; |
||||
|
style += `height:${height.value}px;`; |
||||
|
|
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
} |
||||
|
|
||||
|
return style; |
||||
|
}); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
refresh(); |
||||
|
// 装修模式下刷新 |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
watch( |
||||
|
() => diyComponent.value, |
||||
|
(newValue, oldValue) => { |
||||
|
if (newValue && newValue.componentName == 'ShopSearch') { |
||||
|
refresh(); |
||||
|
} |
||||
|
} |
||||
|
) |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
const instance = getCurrentInstance(); |
||||
|
const height = ref(0) |
||||
|
|
||||
|
const refresh = ()=> { |
||||
|
nextTick(() => { |
||||
|
const query = uni.createSelectorQuery().in(instance); |
||||
|
query.select('.diy-shop-search').boundingClientRect((data: any) => { |
||||
|
height.value = data.height; |
||||
|
}).exec(); |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const toLink = (url: any)=>{ |
||||
|
if (diyStore.mode == 'decorate') return false; |
||||
|
redirect({ url }) |
||||
|
} |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
</style> |
||||
@ -0,0 +1,255 @@ |
|||||
|
<template> |
||||
|
<view :style="warpCss" class="overflow-hidden" v-if="goodsList && goodsList[0]"> |
||||
|
<view class="flex justify-between items-center mb-[20rpx]" v-if="diyComponent.textImg || diyComponent.subTitle.text"> |
||||
|
<view class="h-[34rpx] flex items-center" v-if="diyComponent.textImg" @click="diyStore.toRedirect(diyComponent.textLink)"> |
||||
|
<image class="h-[100%] w-[auto]" :src="img(diyComponent.textImg)" mode="heightFix" /> |
||||
|
</view> |
||||
|
<view class="flex items-center ml-[auto]" v-if="diyComponent.subTitle.text" @click="diyStore.toRedirect(diyComponent.subTitle.link)" :style="{'color': diyComponent.subTitle.textColor}"> |
||||
|
<text class="text-[24rpx]">{{diyComponent.subTitle.text}}</text> |
||||
|
<text class="text-[22rpx] iconfont iconxiangyoujiantou"></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="flex justify-between"> |
||||
|
<!-- 轮播图 --> |
||||
|
<view class="relative w-[340rpx] overflow-hidden" :style="carouselCss"> |
||||
|
<view v-if="diyComponent.list.length == 1" class="leading-0 overflow-hidden"> |
||||
|
<view @click="diyStore.toRedirect(diyComponent.list[0].link)"> |
||||
|
<image v-if="diyComponent.list[0].imageUrl" :src="img(diyComponent.list[0].imageUrl)" mode="heightFix" class="h-[504rpx] !w-full" :show-menu-by-longpress="true"/> |
||||
|
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="heightFix" class="h-[504rpx] !w-full" :show-menu-by-longpress="true"/> |
||||
|
</view> |
||||
|
</view> |
||||
|
<block v-else> |
||||
|
<swiper class="swiper ns-indicator-dots-three h-[504rpx]" autoplay="true" circular="true" :indicator-dots="isShowDots" @change="swiperChange" |
||||
|
:indicator-color="diyComponent.indicatorColor" :indicator-active-color="diyComponent.indicatorActiveColor"> |
||||
|
<swiper-item class="swiper-item" v-for="(item) in diyComponent.list" :key="item.id"> |
||||
|
<view @click="diyStore.toRedirect(item.link)"> |
||||
|
<view class="item h-[504rpx]"> |
||||
|
<image v-if="item.imageUrl" :src="img(item.imageUrl)" mode="scaleToFill" class="w-full h-full" :show-menu-by-longpress="true"/> |
||||
|
<image v-else :src="img('static/resource/images/diy/figure.png')" mode="scaleToFill" class="w-full h-full" :show-menu-by-longpress="true"/> |
||||
|
</view> |
||||
|
</view> |
||||
|
</swiper-item> |
||||
|
</swiper> |
||||
|
<!-- #ifdef MP-WEIXIN --> |
||||
|
<view v-if="diyComponent.list.length > 1" class="swiper-dot-box straightLineStyle2"> |
||||
|
<view v-for="(numItem, numIndex) in diyComponent.list" :key="numIndex" :class="['swiper-dot', { active: numIndex == swiperIndex }]" :style="[numIndex == swiperIndex ? { backgroundColor: diyComponent.indicatorActiveColor } : { backgroundColor: diyComponent.indicatorColor }]"></view> |
||||
|
</view> |
||||
|
<!-- #endif --> |
||||
|
</block> |
||||
|
</view> |
||||
|
|
||||
|
<view class="w-[340rpx] h-[504rpx] flex flex-col bg-[#fff] box-border overflow-hidden" :style="goodsTempCss" @click="toLink(goodsList[0])"> |
||||
|
<view :style="goodsImgCss" class="w-[346rpx] h-[350rpx] overflow-hidden"> |
||||
|
<u--image width="346rpx" height="350rpx" :src="img(goodsList[0].goods_cover_thumb_mid || '')" model="aspectFill"> |
||||
|
<template #error> |
||||
|
<image class="w-[346rpx] h-[350rpx]" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image> |
||||
|
</template> |
||||
|
</u--image> |
||||
|
</view> |
||||
|
<view class="px-[16rpx] flex-1 pt-[16rpx] pb-[20rpx] flex flex-col justify-between"> |
||||
|
<view class="text-[#303133] leading-[40rpx] text-[28rpx] truncate" :style="{ color : diyComponent.goodsNameStyle.color, fontWeight : diyComponent.goodsNameStyle.fontWeight }"> |
||||
|
{{goodsList[0].goods_name}} |
||||
|
</view> |
||||
|
<view class="flex justify-between flex-wrap items-baseline mt-[28rpx]" > |
||||
|
<view class="flex items-center"> |
||||
|
<view class="text-[var(--price-text-color)] price-font truncate max-w-[200rpx]" :style="{ color : diyComponent.priceStyle.mainColor }"> |
||||
|
<text class="text-[24rpx] font-400">¥</text> |
||||
|
<text class="text-[40rpx] font-500">{{ parseFloat(diyGoods.goodsPrice(goodsList[0])).toFixed(2).split('.')[0] }}</text> |
||||
|
<text class="text-[24rpx] font-500">.{{ parseFloat(diyGoods.goodsPrice(goodsList[0])).toFixed(2).split('.')[1] }}</text> |
||||
|
</view> |
||||
|
<image v-if="diyGoods.priceType(goodsList[0]) == 'member_price'" class="max-w-[50rpx] h-[28rpx] ml-[6rpx]" :src="img('addon/shop/VIP.png')" mode="heightFix" /> |
||||
|
</view> |
||||
|
|
||||
|
<view class="w-[44rpx] h-[44rpx] bg-[red] flex items-center justify-center rounded-[50%]" :style="{ backgroundColor : diyComponent.saleStyle.color }"> |
||||
|
<text class="iconfont iconjia font-500 text-[32rpx] text-[#fff]"></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
// 精选推荐 |
||||
|
import { ref,reactive,computed, watch, onMounted } from 'vue'; |
||||
|
import { redirect, img } from '@/utils/common'; |
||||
|
import useDiyStore from '@/app/stores/diy'; |
||||
|
import { getGoodsComponents } from '@/addon/shop/api/goods'; |
||||
|
import {useGoods} from '@/addon/shop/hooks/useGoods' |
||||
|
|
||||
|
const diyGoods = useGoods(); |
||||
|
const props = defineProps(['component', 'index','value']); |
||||
|
const diyStore = useDiyStore(); |
||||
|
|
||||
|
const goodsList = ref<Array<any>>([]); |
||||
|
|
||||
|
const diyComponent = computed(() => { |
||||
|
if(props.value) { |
||||
|
return props.value; |
||||
|
}else if (diyStore.mode == 'decorate') { |
||||
|
return diyStore.value[props.index]; |
||||
|
} else { |
||||
|
return props.component; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// 轮播指示器 |
||||
|
let isShowDots = ref(true) |
||||
|
// #ifdef H5 |
||||
|
isShowDots.value = true; |
||||
|
// #endif |
||||
|
|
||||
|
// #ifdef MP-WEIXIN |
||||
|
isShowDots.value = false; |
||||
|
// #endif |
||||
|
|
||||
|
const warpCss = computed(() => { |
||||
|
var style = ''; |
||||
|
style += 'position:relative;'; |
||||
|
if(diyComponent.value.componentStartBgColor) { |
||||
|
if (diyComponent.value.componentStartBgColor && diyComponent.value.componentEndBgColor) style += `background:linear-gradient(${diyComponent.value.componentGradientAngle},${diyComponent.value.componentStartBgColor},${diyComponent.value.componentEndBgColor});`; |
||||
|
else style += 'background-color:' + diyComponent.value.componentStartBgColor + ';'; |
||||
|
} |
||||
|
|
||||
|
if (diyComponent.value.topRounded) style += 'border-top-left-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topRounded) style += 'border-top-right-radius:' + diyComponent.value.topRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const goodsTempCss = computed(() => { |
||||
|
var style = ''; |
||||
|
if (diyComponent.value.elementBgColor) style += 'background-color:' + diyComponent.value.elementBgColor + ';'; |
||||
|
if (diyComponent.value.topElementRounded) style += 'border-top-left-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topElementRounded) style += 'border-top-right-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomElementRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomElementRounded * 2 + 'rpx;'; |
||||
|
|
||||
|
if(diyComponent.value.margin && diyComponent.value.margin.both) style += 'width: calc((100vw - ' + (diyComponent.value.margin.both*4) + 'rpx - 20rpx) / 2);' |
||||
|
else style += 'width: calc((100vw - 20rpx) / 2 );' |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const goodsImgCss = computed(() => { |
||||
|
var style = ''; |
||||
|
if (diyComponent.value.topElementRounded) style += 'border-radius:' + diyComponent.value.topElementRounded * 2 + 'rpx;'; |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const carouselCss = computed(() => { |
||||
|
var style = ''; |
||||
|
if (diyComponent.value.topCarouselRounded) style += 'border-top-left-radius:' + diyComponent.value.topCarouselRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.topCarouselRounded) style += 'border-top-right-radius:' + diyComponent.value.topCarouselRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomCarouselRounded) style += 'border-bottom-left-radius:' + diyComponent.value.bottomCarouselRounded * 2 + 'rpx;'; |
||||
|
if (diyComponent.value.bottomCarouselRounded) style += 'border-bottom-right-radius:' + diyComponent.value.bottomCarouselRounded * 2 + 'rpx;'; |
||||
|
|
||||
|
if(diyComponent.value.margin && diyComponent.value.margin.both) style += 'width: calc((100vw - ' + (diyComponent.value.margin.both*4) + 'rpx - 20rpx) / 2);' |
||||
|
else style += 'width: calc((100vw - 20rpx) / 2 );' |
||||
|
return style; |
||||
|
}) |
||||
|
|
||||
|
const getGoodsListFn = () => { |
||||
|
let data = { |
||||
|
num: 1, |
||||
|
goods_ids: diyComponent.value.source == 'custom' ? diyComponent.value.goods_ids : '' |
||||
|
} |
||||
|
getGoodsComponents(data).then((res) => { |
||||
|
goodsList.value = res.data; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
refresh(); |
||||
|
// 装修模式下刷新 |
||||
|
if (diyStore.mode != 'decorate') { |
||||
|
watch( |
||||
|
() => diyComponent.value, |
||||
|
(newValue, oldValue) => { |
||||
|
refresh(); |
||||
|
}, |
||||
|
{deep: true} |
||||
|
) |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
const refresh = () => { |
||||
|
// 装修模式下设置默认图 |
||||
|
if (diyStore.mode == 'decorate') { |
||||
|
let obj = { |
||||
|
goods_cover_thumb_mid: "", |
||||
|
goods_name: "商品名称", |
||||
|
sale_num: "100", |
||||
|
unit: "件", |
||||
|
goodsSku:{price:100} |
||||
|
}; |
||||
|
goodsList.value.push(obj); |
||||
|
}else{ |
||||
|
getGoodsListFn(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const toLink = (data: any) => { |
||||
|
redirect({ url: '/addon/shop/pages/goods/detail', param: { goods_id: data.goods_id } }) |
||||
|
} |
||||
|
|
||||
|
const swiperIndex = ref(0); |
||||
|
const swiperChange = e => { |
||||
|
swiperIndex.value = e.detail.current; |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.swiper.ns-indicator-dots-three :deep(.uni-swiper-dots-horizontal) { |
||||
|
bottom: 12rpx; |
||||
|
} |
||||
|
.swiper.ns-indicator-dots-three :deep(.uni-swiper-dot) { |
||||
|
width: 8rpx; |
||||
|
height: 8rpx; |
||||
|
border-radius: 8rpx; |
||||
|
margin-right: 14rpx; |
||||
|
} |
||||
|
.swiper.ns-indicator-dots-three :deep(.uni-swiper-dot):last-of-type { |
||||
|
margin-right: 0; |
||||
|
} |
||||
|
.swiper.ns-indicator-dots-three :deep(.uni-swiper-dot-active) { |
||||
|
width: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.swiper-dot-box { |
||||
|
position: absolute; |
||||
|
bottom: 4rpx; |
||||
|
width: 100%; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
padding: 0 80rpx 8rpx; |
||||
|
box-sizing: border-box; |
||||
|
|
||||
|
.swiper-dot { |
||||
|
background-color: #b2b2b2; |
||||
|
width: 10rpx; |
||||
|
border-radius: 50%; |
||||
|
height: 10rpx; |
||||
|
margin: 8rpx; |
||||
|
} |
||||
|
|
||||
|
&.straightLineStyle2{ |
||||
|
.swiper-dot { |
||||
|
width: 8rpx; |
||||
|
height: 8rpx; |
||||
|
border-radius: 8rpx; |
||||
|
margin: 0; |
||||
|
margin-right: 14rpx; |
||||
|
&.last-of-type { |
||||
|
margin-right: 0; |
||||
|
} |
||||
|
&.active { |
||||
|
width: 30rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,141 @@ |
|||||
|
<template> |
||||
|
<!-- 满减 --> |
||||
|
<view @touchmove.prevent.stop> |
||||
|
<u-popup class="manjian-popup" :show="manjianShow" @close="manjianShow = false" zIndex="999999"> |
||||
|
<view class="min-h-[480rpx] popup-common" @touchmove.prevent.stop> |
||||
|
<view class="title !pb-[30rpx]">满减送</view> |
||||
|
<scroll-view class="h-[520rpx]" scroll-y="true"> |
||||
|
<view class="px-[var(--popup-sidebar-m)] pt-[30rpx]"> |
||||
|
<view v-for="(item,index) in data.content" :key="index" class="mb-[40rpx]"> |
||||
|
<view class="flex items-center"> |
||||
|
<text class="nc-iconfont nc-icon-qianbaoyueV6xx !text-[28rpx] mr-[10rpx]"></text> |
||||
|
<text class="text-[26rpx] font-500">{{item.limit}}</text> |
||||
|
</view> |
||||
|
<view class="mt-[20rpx]"> |
||||
|
<view v-if="item.goods && item.goods.length" class="flex mt-[20rpx]"> |
||||
|
<view class="w-[100rpx] flex justify-end"> |
||||
|
<view class="bg-[var(--primary-color-light)] text-[var(--primary-color)] rounded-[6rpx] text-[22rpx] flex items-center justify-center px-[12rpx] h-[38rpx] mr-[6rpx]">赠品</view> |
||||
|
</view> |
||||
|
<view class="flex-1 ml-[8rpx]"> |
||||
|
<view class="flex p-[20rpx] bg-[#f8f8f8] rounded-[var(--goods-rounded-big)] overflow-hidden" :class="{'mb-[20rpx]': goodsIndex != (item.goods.length-1)}" v-for="(goodsItem,goodsIndex) in item.goods" :key="goodsIndex" @click="goodsEvent(goodsItem.goods_id)"> |
||||
|
<u--image radius="var(--goods-rounded-mid)" width="120rpx" height="120rpx" :src="img(goodsItem.sku_image)" model="aspectFill"> |
||||
|
<template #error> |
||||
|
<image class="w-[120rpx] h-[120rpx] rounded-[var(--goods-rounded-big)] overflow-hidden" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill"></image> |
||||
|
</template> |
||||
|
</u--image> |
||||
|
<view class="flex flex-1 w-0 flex-col justify-between ml-[20rpx] pt-[6rpx] pb-[10rpx]"> |
||||
|
<view class="truncate text-[#303133] text-[24rpx] leading-[32rpx]"> |
||||
|
{{goodsItem.goods_name}} |
||||
|
</view> |
||||
|
<view class="flex items-baseline"> |
||||
|
<view v-if="goodsItem.sku_name" class="truncate text-[22rpx] mt-[4rpx] text-[#999]"> |
||||
|
{{ goodsItem.sku_name }} |
||||
|
</view> |
||||
|
<view class="font-400 ml-[auto] text-[24rpx] text-[#303133]"> |
||||
|
<text>x</text> |
||||
|
<text>{{goodsItem.num}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<block v-if="item.give && item.give.length"> |
||||
|
<view class="flex items-center mt-[24rpx]" v-for="(giveItem,giveIndex) in item.give" :key="giveIndex"> |
||||
|
<view class="w-[100rpx] flex justify-end"> |
||||
|
<view class="bg-[var(--primary-color-light)] text-[var(--primary-color)] rounded-[6rpx] text-[22rpx] flex items-center justify-center px-[12rpx] h-[38rpx] mr-[6rpx]">{{giveItem.label}}</view> |
||||
|
</view> |
||||
|
<text class="text-[24rpx]">{{giveItem.content}}</text> |
||||
|
</view> |
||||
|
</block> |
||||
|
<view class="flex items-baseline mt-[24rpx]" v-if="item.coupon && item.coupon.length"> |
||||
|
<view class="w-[100rpx] flex justify-end"> |
||||
|
<view class="bg-[var(--primary-color-light)] text-[var(--primary-color)] rounded-[6rpx] text-[22rpx] flex items-center justify-center px-[12rpx] h-[38rpx] mr-[6rpx]">优惠券</view> |
||||
|
</view> |
||||
|
<view class="flex flex-wrap flex-1"> |
||||
|
<text class="flex items-center text-[24rpx] leading-[1.3]" :class="{'mb-[16rpx]': couponIndex != (item.coupon.length-1)}" v-for="(couponItem,couponIndex) in item.coupon" :key="couponIndex"> |
||||
|
{{couponItem.num}}张{{couponItem.coupon_name}}优惠券 |
||||
|
</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
<view class="btn-wrap"> |
||||
|
<button class="primary-btn-bg btn" @click="manjianShow = false">确定</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</u-popup> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { ref } from 'vue'; |
||||
|
import { img, deepClone, redirect } from '@/utils/common' |
||||
|
import { cloneDeep } from 'lodash-es' |
||||
|
import { t } from '@/locale' |
||||
|
|
||||
|
const manjianShow = ref(false); |
||||
|
const data = ref({}); |
||||
|
const open = (parameter:any = {})=>{ |
||||
|
data.value = cloneDeep(parameter); |
||||
|
data.value.content = []; |
||||
|
data.value.rule_json.forEach((item,index)=>{ |
||||
|
if(item.is_show || item.is_show == undefined){ |
||||
|
let obj = {}; |
||||
|
obj.limit = `门槛满${data.value.condition_type == 'over_n_yuan' ? parseFloat(item.limit).toFixed(2) : item.limit }${data.value.condition_type == 'over_n_yuan' ? '元' : '件'}`; |
||||
|
if(item.is_give_goods){ |
||||
|
obj.goods = deepClone(item.goods); |
||||
|
} |
||||
|
obj.give = []; |
||||
|
if(item.is_discount && item.discount_money){ |
||||
|
obj.give.push({ |
||||
|
label: '满减', |
||||
|
content: `订单金额${item.discount_type == 1 ? '减' : '打'}${item.discount_type == 1 ? parseFloat(item.discount_money).toFixed(2) : item.discount_money}${item.discount_type == 1 ? '元' : '折'}` |
||||
|
}) |
||||
|
} |
||||
|
if(item.is_free_shipping){ |
||||
|
obj.give.push({ |
||||
|
label: '包邮', |
||||
|
content: '商品包邮' |
||||
|
}); |
||||
|
} |
||||
|
if(item.is_give_point && item.point){ |
||||
|
obj.give.push({ |
||||
|
label: '积分', |
||||
|
content: `送${item.point}积分` |
||||
|
}); |
||||
|
} |
||||
|
if(item.is_give_balance && item.balance){ |
||||
|
obj.give.push({ |
||||
|
label: '余额', |
||||
|
content: `送${parseFloat(item.balance).toFixed(2)}余额` |
||||
|
}); |
||||
|
} |
||||
|
if(item.is_give_coupon){obj.coupon = item.coupon;} |
||||
|
data.value.content.push(obj); |
||||
|
} |
||||
|
}); |
||||
|
manjianShow.value = true; |
||||
|
} |
||||
|
|
||||
|
const goodsEvent = (id : number) => { |
||||
|
redirect({ |
||||
|
url: '/addon/shop/pages/goods/detail', |
||||
|
param: { |
||||
|
goods_id: id |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
defineExpose({ |
||||
|
open |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
::v-deep .manjian-popup .u-slide-up-enter-to{ |
||||
|
z-index: 999999 !important; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,69 @@ |
|||||
|
<template> |
||||
|
<view class="goods-recommend"> |
||||
|
<view class="mt-[60rpx] flex flex-col items-center sidebar-margin pb-[50rpx]"> |
||||
|
<view class="flex items-center mb-[30rpx]" v-if="goodsList && Object.keys(goodsList).length"> |
||||
|
<image class="w-[38rpx] h-[22rpx]" :src="img('addon/shop_fenxiao/level/title_left.png')" mode="aspectFill"></image> |
||||
|
<text class="text-[30rpx] mx-[18rpx] font-500 text-[#EF000C]">猜你喜欢</text> |
||||
|
<image class="w-[38rpx] h-[22rpx]" :src="img('addon/shop_fenxiao/level/title_right.png')" mode="aspectFill"></image> |
||||
|
</view> |
||||
|
<diy-goods-list @loadingFn="getGoodsListFn" :component="goodsData"/> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { img } from '@/utils/common'; |
||||
|
import { ref } from 'vue' |
||||
|
import diyGoodsList from '@/addon/shop/components/diy/goods-list/index.vue'; |
||||
|
|
||||
|
// 获取分销商品数据 |
||||
|
const goodsList = ref() |
||||
|
const getGoodsListFn = (data: any)=>{ |
||||
|
goodsList.value = data || {} |
||||
|
} |
||||
|
|
||||
|
// 商品列表组件 |
||||
|
const goodsData = ref({ |
||||
|
style: 'style-2', |
||||
|
num: 10, |
||||
|
source: 'all', |
||||
|
topElementRounded: 12, |
||||
|
bottomElementRounded: 12, |
||||
|
margin: { |
||||
|
both: 10, |
||||
|
bottom: 0, |
||||
|
top: 0 |
||||
|
}, |
||||
|
priceStyle: { |
||||
|
mainColor: "#FF4142", |
||||
|
control: true |
||||
|
}, |
||||
|
goodsNameStyle:{ |
||||
|
color: "#303133", |
||||
|
control: true, |
||||
|
fontWeight: "normal" |
||||
|
}, |
||||
|
saleStyle: { |
||||
|
color: "#999", |
||||
|
control: true |
||||
|
}, |
||||
|
labelStyle: { |
||||
|
isShow: true, |
||||
|
control: true |
||||
|
}, |
||||
|
btnStyle: { |
||||
|
fontWeight:false, |
||||
|
padding: 0, |
||||
|
aroundRadius:25, |
||||
|
textColor: "#fff", |
||||
|
startBgColor: '#FF4142', |
||||
|
endBgColor: '#FF4142', |
||||
|
style: 'nc-icon-gouwuche1', |
||||
|
control: true |
||||
|
} |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
|
||||
|
</style> |
||||