feat(projects): 首页主备优化
All checks were successful
CI Build & Upload (WeApp) / build-upload-dev (push) Has been skipped
CI Build & Upload (WeApp) / build-upload-prod (push) Successful in 6m43s
CI Build & Upload (WeApp) / build-upload-reserve (push) Successful in 2m25s

This commit is contained in:
2025-12-02 23:57:04 +08:00
parent b6b6649a9b
commit a30d3572a5
22 changed files with 1204 additions and 337 deletions

View File

@@ -12,5 +12,7 @@ TARO_APP_API='https://test.wanzhuanyongcheng.cn/app'
TARO_APP_NAME='捷兑通'
TARO_APP_COP='玩赚商城版权所有'
TARO_APP_TITLE_IMG='../../static/index/1.png'
TARO_APP_LOGO_IMG='../../../static/logo.jpg'
TARO_APP_TITLE_IMG='../../../static/index/1.png'
TARO_APP_LOGO_IMG='../../../static/logo.jpg'
TARO_APP_SHOW_NEW_HOME='no'

View File

@@ -12,5 +12,8 @@ TARO_APP_API='https://www.wanzhuanyongcheng.cn/app'
TARO_APP_NAME='捷兑通'
TARO_APP_COP='鑫瓴科技版权所有'
TARO_APP_TITLE_IMG='../../static/index/1.png'
TARO_APP_LOGO_IMG='../../../static/logo.jpg'
TARO_APP_TITLE_IMG='../../../static/index/1.png'
TARO_APP_LOGO_IMG='../../../static/logo.jpg'
TARO_APP_SHOW_NEW_HOME='no'

View File

@@ -12,5 +12,7 @@ TARO_APP_NAME='玩赚商城'
TARO_APP_COP='玩赚商城版权所有'
TARO_APP_TITLE_IMG='../../static/index/title.png'
TARO_APP_LOGO_IMG='../../../static/logo-1.jpg'
TARO_APP_TITLE_IMG='../../../static/index/title.png'
TARO_APP_LOGO_IMG='../../../static/logo-1.jpg'
TARO_APP_SHOW_NEW_HOME='yes'

View File

@@ -13,4 +13,6 @@ TARO_APP_NAME='捷兑通'
TARO_APP_COP='鑫瓴科技版权所有'
TARO_APP_TITLE_IMG='../../static/index/1.png'
TARO_APP_LOGO_IMG='../../../static/logo.jpg'
TARO_APP_LOGO_IMG='../../../static/logo.jpg'
TARO_APP_SHOW_NEW_HOME='no'

8
.idea/.gitignore generated vendored
View File

@@ -1,8 +0,0 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -1,60 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="LINE_SEPARATOR" value="&#10;" />
<HTMLCodeStyleSettings>
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
</HTMLCodeStyleSettings>
<JSCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
</JSCodeStyleSettings>
<TypeScriptCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
</TypeScriptCodeStyleSettings>
<VueCodeStyleSettings>
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
</VueCodeStyleSettings>
<codeStyleSettings language="HTML">
<option name="SOFT_MARGINS" value="80" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="SOFT_MARGINS" value="80" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="TypeScript">
<option name="SOFT_MARGINS" value="80" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Vue">
<option name="SOFT_MARGINS" value="80" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

View File

@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@@ -1,7 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Stylelint" enabled="true" level="ERROR" enabled_by_default="true" />
</profile>
</component>

12
.idea/jdt-user.iml generated
View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml generated
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/jdt-user.iml" filepath="$PROJECT_DIR$/.idea/jdt-user.iml" />
</modules>
</component>
</project>

6
.idea/prettier.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PrettierConfiguration">
<option name="myConfigurationMode" value="AUTOMATIC" />
</component>
</project>

6
.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -4,7 +4,6 @@ module.exports = {
env: {
NODE_ENV: '"development"',
},
defineConstants: {},
mini: {},
h5: {},
};

View File

@@ -4,7 +4,6 @@ module.exports = {
env: {
NODE_ENV: '"production"',
},
defineConstants: {},
mini: {},
h5: {
/**

37
config/reserve.ts Normal file
View File

@@ -0,0 +1,37 @@
// @ts-nocheck
module.exports = {
env: {
NODE_ENV: '"production"',
},
mini: {},
h5: {
/**
* WebpackChain 插件配置
* @docs https://github.com/neutrinojs/webpack-chain
*/
// webpackChain (chain) {
// /**
// * 如果 h5 端编译后体积过大,可以使用 webpack-bundle-analyzer 插件对打包体积进行分析。
// * @docs https://github.com/webpack-contrib/webpack-bundle-analyzer
// */
// chain.plugin('analyzer')
// .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
// /**
// * 如果 h5 端首屏加载时间过长,可以使用 prerender-spa-plugin 插件预加载首页。
// * @docs https://github.com/chrisvfritz/prerender-spa-plugin
// */
// const path = require('path')
// const Prerender = require('prerender-spa-plugin')
// const staticDir = path.join(__dirname, '..', 'dist')
// chain
// .plugin('prerender')
// .use(new Prerender({
// staticDir,
// routes: [ '/pages/index/index' ],
// postProcess: (context) => ({ ...context, outputPath: path.join(staticDir, 'index.html') })
// }))
// }
},
};

View File

@@ -1,8 +1,8 @@
{
"name": "jdt-user",
"version": "4.0.15",
"version": "4.0.16",
"taroConfig": {
"version": "4.0.15"
"version": "4.0.16"
},
"scripts": {
"build:weapp": "taro build --type weapp",

View File

@@ -0,0 +1,874 @@
<template>
<view class="home-new">
<!-- Header & Search -->
<view class="header-fixed">
<view :style="{ paddingTop: statusBarHeight + 'px' }"></view>
<view class="header-content">
<!-- Location -->
<view class="location-bar">
<view class="location-inner" @click="getUserLocal">
<view class="iconfont icon-dizhi location-icon"></view>
<text class="location-text">{{ address }}</text>
<view class="iconfont icon-xiangxia location-arrow"></view>
</view>
</view>
<!-- Search Bar -->
<view class="search-container">
<input
type="text"
placeholder="请输入商家名称搜索"
class="search-input"
v-model="searchVal" />
<view class="search-icon-left">
<view class="iconfont icon-sousuo"></view>
</view>
<view class="search-btn" @click.stop="clickSearch">搜索</view>
</view>
</view>
</view>
<!-- Main Content -->
<view class="main-content">
<!-- Banner / Hero -->
<view class="banner-hero">
<!-- Background Pattern Decoration -->
<view class="banner-decoration banner-decoration-top"></view>
<view class="banner-decoration banner-decoration-bottom"></view>
<view class="banner-inner">
<view class="banner-title">
玩赚积分<text class="banner-title-yellow">商城</text>
</view>
<view class="banner-subtitle-wrapper">
<view class="banner-divider"></view>
<text class="banner-subtitle">积分当钱花 · 兑换无上限</text>
<view class="banner-divider"></view>
</view>
<view class="banner-buttons">
<view class="banner-btn banner-btn-primary">
<text class="banner-btn-icon">💎</text> 积分兑换
</view>
<view class="banner-btn banner-btn-secondary">同城兑换</view>
</view>
</view>
</view>
<!-- Quick Actions Grid -->
<view class="nav-grid-wrapper">
<view class="nav-grid">
<view
class="nav-item"
v-for="(item, index) in navigationList"
:key="index"
@click="toPage(item)">
<view class="nav-icon-wrapper">
<image :src="item.icon" class="nav-icon" mode="aspectFit" />
</view>
<text class="nav-label">{{ item.name }}</text>
</view>
</view>
</view>
<!-- Store List -->
<view class="store-section">
<view class="store-header">
<view class="store-header-line"></view>
附近商家
</view>
<view class="store-list">
<view
class="store-item"
v-for="(item, index) in merList"
:key="index"
@click="toMerDetails(item)">
<view class="store-image-wrapper">
<image
:src="item.head_photo"
class="store-image"
mode="aspectFill" />
<!-- <view class="store-badge" v-if="index < 3">严选</view> -->
</view>
<view class="store-info">
<view class="store-name">{{ item.name }}</view>
<view class="store-rating">
<view class="rating-stars">
<text class="stars-text"></text>
<text class="rating-score">5.0</text>
</view>
<text class="rating-divider">|</text>
<text class="rating-sales">月售 1000+</text>
</view>
<view class="store-distance">
<text class="distance-placeholder">.</text>
<text class="distance-text">
距离我{{
calculateDistance(
userLocalNum.t,
userLocalNum.l,
Number(item.lat),
Number(item.lon),
)
}}
</text>
</view>
</view>
</view>
<!-- Empty State -->
<view v-if="merList.length === 0" class="store-empty">
暂无附近商家
</view>
</view>
</view>
</view>
<Popup :imgArr="imgList" />
</view>
</template>
<script setup lang="ts">
import Taro from '@tarojs/taro';
import { ref } from 'vue';
import { getHomeList } from '@/api/home';
import { getMerList } from '@/api/goods';
import { bindParent } from '@/api/user';
import { calculateDistance } from '@/utils';
import Popup from '@/components/Popup.vue';
const statusBarHeight = Taro.getSystemInfoSync().statusBarHeight || 20;
const address = ref('获取位置中...');
const searchVal = ref('');
const navigationList = ref<any[]>([]);
const merList = ref<any[]>([]);
const pageVal = ref({
page: 1,
total: 0,
});
const userLocalNum = ref({
l: 0,
t: 0,
});
const imgList = ref([require('../../../static/zhenggai.png')]);
// Lifecycle
Taro.useLoad(() => {
getPrivacy();
getUserLocal();
getNavLists();
checkBindParent();
});
Taro.useShareAppMessage(() => ({
title: '捷兑通',
path: `/pages/index/index?scene=${Taro.getStorageSync('token')}`,
imageUrl: 'https://upload.jdt168.com/1714375021923881119_Share.jpg',
}));
Taro.useReachBottom(() => {
if (merList.value.length >= pageVal.value.total) {
if (pageVal.value.total > 0) {
Taro.showToast({ title: '没有更多了', icon: 'none' });
}
return;
}
pageVal.value.page += 1;
fetchMerList();
});
// Methods
const checkBindParent = async () => {
if (Taro.getStorageSync('token') && Taro.getStorageSync('bind_id')) {
try {
const res: any = await bindParent({
uid: Taro.getStorageSync('bind_id'),
});
Taro.showToast({
title: res.msg,
});
Taro.removeStorageSync('bind_id');
} catch (error) {
Taro.removeStorageSync('bind_id');
}
}
};
const getPrivacy = () => {
Taro.getPrivacySetting({
success: res => {
if (res.needAuthorization) {
Taro.requirePrivacyAuthorize({
success: () => console.log('用户同意授权'),
fail: () => {
console.log('用户拒绝授权');
// Taro.exitMiniProgram();
},
});
}
},
});
};
const getUserLocal = () => {
Taro.getLocation({
type: 'wgs84',
success: res => {
userLocalNum.value.l = res.longitude;
userLocalNum.value.t = res.latitude;
fetchAddress(res.latitude, res.longitude);
// Refresh list with location
pageVal.value.page = 1;
merList.value = [];
fetchMerList();
},
fail: () => {
address.value = '未能获取定位';
// Load list anyway
fetchMerList();
},
});
};
const fetchAddress = (lat, lon) => {
Taro.request({
url: `https://api.tianditu.gov.cn/geocoder?postStr={'lon':${lon},'lat':${lat},'ver':1}&type=geocode&tk=42db4f3dfd1a18d31e73ee90aa2ce054`,
method: 'GET',
success: res => {
const { msg, result } = res.data;
if (msg === 'ok') {
address.value = `${result.addressComponent.county}${result.addressComponent.town}${result.addressComponent.address}`;
}
},
});
};
const getNavLists = async () => {
const defaults = [
{
type: 1,
icon: '//p0.meituan.net/csc/f33ad2443a67e9f3474a1d5fd9d529db7504.png',
url: '/pages/users/settled_mer/index',
name: '商户入驻',
},
{
type: 1,
icon: '//p0.meituan.net/csc/0403cf37dd14a6b44b22ffccaa2878f95703.png',
url: '/pages/allClassList/index',
name: '全部服务',
},
];
try {
const res = await getHomeList();
const apiList = res.data.data.map(item => ({
ID: item.ID,
type: 2,
icon: item.icon,
url: item.url,
name: item.name,
}));
navigationList.value = [...apiList, ...defaults];
} catch (e) {
console.error(e);
navigationList.value = defaults;
}
};
const fetchMerList = async () => {
try {
const res = await getMerList({
PageNum: pageVal.value.page,
PageSize: 10,
// class_id: 0
});
pageVal.value.total = res.data.total;
if (res.data.data) {
merList.value.push(...res.data.data);
}
} catch (e) {
console.error(e);
}
};
const clickSearch = () => {
if (!searchVal.value)
return Taro.showToast({ title: '请输入商家名称', icon: 'none' });
Taro.navigateTo({ url: `/pages/search/index?name=${searchVal.value}` });
searchVal.value = '';
};
const toPage = item => {
item.type !== 1
? Taro.navigateTo({
url: `/pages/search/index?id=${item.ID}&name=${item.name}`,
})
: Taro.navigateTo({ url: item.url });
};
const toMerDetails = item => {
Taro.navigateTo({ url: `/pages/mer/mer_detail/index?bid=${item.bid}` });
};
</script>
<style lang="scss">
@import './home-new.scss';
/* 所有新版样式包裹在 .home-new 作用域下 */
.home-new {
/* Header Styles */
.header-fixed {
position: sticky;
top: 0;
z-index: 50;
background: linear-gradient(180deg, #eff6ff 0%, #f5f6f7 100%);
}
.header-content {
padding: 10px 20px 10px;
}
.location-bar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
font-size: 22px;
color: #6b7280;
}
.location-inner {
display: flex;
align-items: center;
max-width: 90%;
background-color: rgba(255, 255, 255, 0.6);
padding: 8px 16px;
border-radius: 50px;
backdrop-filter: blur(10px);
}
.location-icon {
width: 26px;
height: 26px;
margin-right: 8px;
color: #374151;
}
.location-text {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 500;
}
.location-arrow {
width: 20px;
height: 20px;
margin-left: 8px;
color: #9ca3af;
}
.search-container {
position: relative;
// margin: 10px auto;
margin-top: 20px;
}
.search-input {
height: 70px;
background-color: #ffffff;
border: none;
border-radius: 20px;
padding: 10px 20px 10px 70px;
font-size: 28px;
}
.search-icon-left {
position: absolute;
left: 20px;
top: 50%;
transform: translateY(-50%);
color: #9ca3af;
font-size: 30px;
}
.search-btn {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: linear-gradient(90deg, #fa2c19 0%, #ff5d45 100%);
color: #ffffff;
font-size: 24px;
font-weight: bold;
padding: 12px 30px;
border-radius: 16px;
box-shadow: 0 4px 6px rgba(250, 44, 25, 0.2);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
/* Main Content */
.main-content {
padding: 0 20px 180px;
}
/* Banner Hero */
.banner-hero {
position: relative;
border-radius: 20px;
overflow: hidden;
background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%);
height: 280px;
margin-top: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.banner-decoration {
position: absolute;
border-radius: 50%;
opacity: 0.1;
}
.banner-decoration-top {
top: -80px;
right: -80px;
width: 240px;
height: 240px;
background-color: rgba(255, 255, 255, 0.3);
}
.banner-decoration-bottom {
bottom: -60px;
left: -60px;
width: 180px;
height: 180px;
background-color: rgba(147, 197, 253, 0.3);
}
.banner-inner {
position: relative;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
padding: 30px;
}
.banner-title {
font-size: 60px;
font-weight: 900;
color: #ffffff;
letter-spacing: 4px;
font-style: italic;
margin-bottom: 8px;
}
.banner-title-yellow {
color: #fde047;
}
.banner-subtitle-wrapper {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 30px;
opacity: 0.95;
}
.banner-divider {
width: 60px;
height: 1px;
background-color: rgba(255, 255, 255, 0.5);
}
.banner-subtitle {
color: #ffffff;
font-size: 22px;
font-weight: 500;
letter-spacing: 2px;
}
.banner-buttons {
display: flex;
width: 100%;
max-width: 600px;
gap: 20px;
}
.banner-btn {
flex: 1;
border-radius: 50px;
padding: 15px 0;
font-size: 26px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
pointer-events: none;
transform: scale(0.9);
}
.banner-btn-primary {
background-color: rgba(255, 255, 255, 0.1);
color: #ffffff;
border: 1px solid rgba(255, 255, 255, 0.2);
backdrop-filter: blur(10px);
}
.banner-btn-secondary {
background-color: rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
}
.banner-btn-icon {
font-size: 36px;
opacity: 0.8;
}
/* Navigation Grid */
.nav-grid-wrapper {
background-color: #ffffff;
border-radius: 20px;
padding: 30px;
margin-top: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.nav-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
.nav-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.nav-icon-wrapper {
width: 88px;
height: 88px;
border-radius: 50%;
background-color: #f9fafb;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.nav-icon {
width: 60px;
height: 60px;
}
.nav-label {
font-size: 22px;
font-weight: 500;
color: #374151;
text-align: center;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Featured Section */
.featured-section {
display: flex;
gap: 20px;
height: 280px;
margin-top: 20px;
}
.featured-card {
background-color: #ffffff;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
position: relative;
}
.featured-card-left {
flex: 1.2;
background: linear-gradient(135deg, #fff7ed 0%, #ffffff 100%);
border: 1px solid #fed7aa;
padding: 20px;
display: flex;
flex-direction: column;
}
.featured-content {
position: relative;
z-index: 10;
}
.featured-title {
font-size: 30px;
font-weight: bold;
color: #1f2937;
}
.featured-desc {
font-size: 20px;
color: #f97316;
margin-top: 4px;
}
.featured-btn {
margin-top: 20px;
background: linear-gradient(90deg, #f97316 0%, #ef4444 100%);
color: #ffffff;
font-size: 22px;
font-weight: bold;
padding: 10px 20px;
border-radius: 50px;
border: none;
display: inline-block;
line-height: 1.5;
box-shadow: 0 2px 4px rgba(249, 115, 22, 0.3);
}
.featured-bg {
position: absolute;
bottom: 0;
right: 0;
opacity: 0.1;
pointer-events: none;
}
.featured-svg {
width: 180px;
height: 180px;
color: #ea580c;
transform: rotate(-12deg) translate(30px, 30px);
}
.featured-card-right {
flex: 1;
border: 1px solid #e5e7eb;
display: flex;
flex-direction: column;
}
.featured-image {
height: 67%;
position: relative;
}
.featured-img {
width: 100%;
height: 100%;
}
.featured-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.6) 100%);
padding: 10px 15px;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.featured-overlay-title {
color: #ffffff;
font-weight: bold;
font-size: 26px;
}
.featured-footer {
height: 33%;
padding: 0 15px;
display: flex;
align-items: center;
justify-content: space-between;
background-color: #ffffff;
}
.featured-footer-left {
display: flex;
flex-direction: column;
}
.featured-footer-title {
font-size: 22px;
font-weight: bold;
color: #1f2937;
}
.featured-footer-arrow {
width: 44px;
height: 44px;
border-radius: 50%;
background-color: #eff6ff;
color: #3b82f6;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
}
/* Store Section */
.store-section {
padding-bottom: 20px;
margin-top: 20px;
}
.store-header {
font-size: 30px;
font-weight: bold;
color: #1f2937;
padding: 0 8px 15px;
display: flex;
align-items: center;
}
.store-header-line {
width: 6px;
height: 30px;
background-color: #fa2c19;
border-radius: 50px;
margin-right: 15px;
}
.store-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.store-item {
background-color: #ffffff;
border-radius: 20px;
padding: 15px;
display: flex;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
border: 1px solid #f9fafb;
}
.store-image-wrapper {
width: 180px;
height: 180px;
flex-shrink: 0;
background-color: #f3f4f6;
border-radius: 16px;
overflow: hidden;
position: relative;
}
.store-image {
width: 100%;
height: 100%;
}
.store-badge {
position: absolute;
top: 0;
left: 0;
background-color: #eab308;
color: #ffffff;
font-size: 20px;
padding: 4px 12px;
border-bottom-right-radius: 16px;
font-weight: bold;
}
.store-info {
margin-left: 20px;
flex: 1;
display: flex;
flex-direction: column;
height: 180px;
justify-content: space-between;
padding: 8px 0;
}
.store-name {
font-weight: bold;
color: #1f2937;
font-size: 30px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 1.2;
}
.store-rating {
display: flex;
align-items: center;
font-size: 22px;
margin-top: 4px;
}
.rating-stars {
display: flex;
align-items: center;
margin-right: 15px;
}
.stars-text {
color: #fbbf24;
font-size: 20px;
margin-right: 8px;
}
.rating-score {
color: #fa2c19;
font-weight: bold;
}
.rating-divider {
color: #d1d5db;
margin: 0 10px;
}
.rating-sales {
color: #6b7280;
}
.store-distance {
display: flex;
align-items: center;
justify-content: space-between;
}
.distance-placeholder {
font-size: 22px;
color: transparent;
user-select: none;
}
.distance-text {
font-size: 22px;
color: #9ca3af;
}
.store-empty {
text-align: center;
padding: 80px 0;
color: #9ca3af;
font-size: 26px;
}
.iconfont {
font-size: inherit;
}
} /* .home-new 作用域结束 */
</style>

View File

@@ -0,0 +1,245 @@
<template>
<view class="home-old">
<view :style="{ marginTop: BarHeight + 'px' }"></view>
<view class="local">
<view class="iconfont icon-dizhi" style="font-size: 20px"></view>
<text class="text-[28px]">{{ address }}</text>
</view>
<view class="search">
<view
class="iconfont icon-sousuo"
style="color: #8f8f8f; margin-right: 8px"></view>
<input placeholder="请输入商家名称搜索" v-model="searchVal" />
<view class="search-btn" @click="clickSearch">搜索</view>
</view>
<view class="banner">
<!-- <view
class="title"
:style="{
backgroundImage: `url('${titleImgPath}')`,
}"></view> -->
<image class="title" :src="titleImgPath"></image>
<view class="jf-btn" hover-class="none"></view>
<!-- <view class="footer">
<navigator
hover-class="none"
class="btn-1"
url="/pages/fastBuy/index?type=1"></navigator>
<navigator
hover-class="none"
openType="switchTab"
url="/pages/game/gamehome/index"
class="btn-2"></navigator>
<navigator
hover-class="none"
class="btn-3"
url="/pages/fastBuy/index?type=2"></navigator>
</view> -->
</view>
<view class="navigation">
<view
class="item"
v-for="(item, index) in navigationList"
:key="index"
@click="toPage(item)">
<image class="icon" :src="item.icon" />
<view class="text">{{ item.name }}</view>
</view>
</view>
<!-- <navigator
class="ad"
url="/pages/marketing/yq/index"
open-type="navigate"
hover-class="none">
</navigator> -->
<MerList :get-user-location="getUserLocal" />
<Popup :imgArr="imgList" />
</view>
</template>
<script setup lang="ts">
import Taro from '@tarojs/taro';
import { ref } from 'vue';
import { getHomeList } from '@/api/home';
import { bindParent } from '@/api/user';
import MerList from '@/components/MerList.vue';
import Popup from '@/components/Popup.vue';
const statusBarHeight = Taro.getSystemInfoSync().statusBarHeight;
const BarHeight = ref((statusBarHeight as number) + 7);
const titleImgPath = require(process.env.TARO_APP_TITLE_IMG as string);
interface navigationType {
ID?: number;
type: number;
icon: string;
name: string;
url: string;
}
const navigationList = ref<Array<navigationType>>([]);
const imgList = ref([
// require('../../static/popTip-1.png'),
// require('../../static/popTip.png'),
require('../../../static/zhenggai.png'),
]);
Taro.useShareAppMessage(() => ({
title: '捷兑通',
path: `/pages/index/index?scene=${Taro.getStorageSync('token')}`,
imageUrl: 'https://upload.jdt168.com/1714375021923881119_Share.jpg',
}));
const searchVal = ref('');
const clickSearch = () => {
if (!searchVal.value)
return Taro.showToast({
title: '请输入商家名称再搜索',
icon: 'none',
});
Taro.navigateTo({
url: `/pages/search/index?name=${searchVal.value}`,
});
searchVal.value = '';
};
Taro.useLoad(() => {
Taro.getPrivacySetting({
success: res => {
if (res.needAuthorization) {
Taro.requirePrivacyAuthorize({
success: () => {
console.log('用户同意授权');
},
fail: () => {
console.log('用户拒绝授权');
Taro.exitMiniProgram();
},
});
}
},
});
getNavLists();
getUserLocal();
checkBindParent();
});
const checkBindParent = async () => {
if (Taro.getStorageSync('token') && Taro.getStorageSync('bind_id')) {
try {
const res: any = await bindParent({
uid: Taro.getStorageSync('bind_id'),
});
Taro.showToast({
title: res.msg,
});
Taro.removeStorageSync('bind_id');
} catch (error) {
Taro.removeStorageSync('bind_id');
}
}
};
const address = ref('获取位置中......');
const getUserLocal = async () => {
Taro.getLocation({
type: 'wgs84',
success: res => {
// Taro.request({
// url: `https://apis.map.qq.com/ws/geocoder/v1/?location=${res.latitude},${res.longitude}&key=4EJBZ-TZXCV-IHUPX-UMI2L-MK3N3-37FSQ&get_poi=1`,
// method: 'GET',
// success: res => {
// switch (res.data.status) {
// case 121:
// Taro.showToast({
// title: res.data.message,
// icon: 'none',
// });
// break;
// default:
// const data = res.data.result.address_component;
// address.value = `${data.district}${data.street_number}`;
// break;
// }
// },
// });
Taro.request({
url: `https://api.tianditu.gov.cn/geocoder?postStr={'lon':${res.longitude},'lat':${res.latitude},'ver':1}&type=geocode&tk=42db4f3dfd1a18d31e73ee90aa2ce054`,
method: 'GET',
success: res => {
const { msg, result } = res.data;
if (msg === 'ok') {
address.value = `${result.addressComponent.county}${result.addressComponent.town}${result.addressComponent.address}`;
} else {
Taro.showToast({
title: msg,
icon: 'none',
});
}
},
});
},
});
};
const getNavLists = async () => {
navigationList.value = [
// {
// type: 1,
// icon: '//p0.meituan.net/csc/5c770748f0028c63741c5ec14df3cc386715.png',
// url: '',
// name: '活动商家',
// },
// {
// type: 1,
// icon: '//p0.meituan.net/csc/4868c06b99008ff7d5f81e6514858c8a7950.png',
// url: '',
// name: '兑换商家',
// },
{
type: 1,
icon: '//p0.meituan.net/csc/f33ad2443a67e9f3474a1d5fd9d529db7504.png',
url: '/pages/users/settled_mer/index',
name: '商户入驻',
},
{
type: 1,
icon: '//p0.meituan.net/csc/0403cf37dd14a6b44b22ffccaa2878f95703.png',
url: '/pages/allClassList/index',
name: '全部服务',
},
];
const res = await getHomeList();
res.data.data.forEach(item => {
navigationList.value.unshift({
ID: item.ID,
type: 2,
icon: item.icon,
url: item.url,
name: item.name,
});
});
};
const toPage = item => {
item.type !== 1
? Taro.navigateTo({
url: `/pages/search/index?id=${item.ID}&name=${item.name}`,
})
: Taro.navigateTo({
url: item.url as string,
});
};
</script>
<style lang="scss">
/* 旧版样式包裹在 .home-old 作用域下 */
.home-old {
@import './home-old.scss';
}
</style>

View File

@@ -0,0 +1,10 @@
/* HomeNew 页面样式 */
page {
background-color: #f5f6f7;
}
.home-new {
background-color: #f5f6f7;
min-height: 100vh;
}

View File

@@ -70,7 +70,7 @@ page {
.btn-2 {
// background-image: url('//p1.meituan.net/csc/8cc2b518031eb7424b184d90a583d805146287.png');
background-image: url('../../static/game_banner.png');
background-image: url('../../../static/game_banner.png');
background-size: 100% 100%;
width: 300px;
height: 220px;
@@ -121,3 +121,4 @@ page {
height: 180px;
margin: 20px;
}

View File

@@ -1,223 +1,19 @@
<template>
<view>
<view :style="{ marginTop: BarHeight + 'px' }"></view>
<view class="local">
<view class="iconfont icon-dizhi" style="font-size: 20px"></view>
<text class="text-[28px]">{{ address }}</text>
</view>
<view class="search">
<view
class="iconfont icon-sousuo"
style="color: #8f8f8f; margin-right: 8px"></view>
<input placeholder="请输入商家名称搜索" v-model="searchVal" />
<view class="search-btn" @click="clickSearch">搜索</view>
</view>
<view class="banner">
<!-- <view
class="title"
:style="{
backgroundImage: `url('${titleImgPath}')`,
}"></view> -->
<image class="title" :src="titleImgPath"></image>
<view class="jf-btn" hover-class="none"></view>
<!-- <view class="footer">
<navigator
hover-class="none"
class="btn-1"
url="/pages/fastBuy/index?type=1"></navigator>
<navigator
hover-class="none"
openType="switchTab"
url="/pages/game/gamehome/index"
class="btn-2"></navigator>
<navigator
hover-class="none"
class="btn-3"
url="/pages/fastBuy/index?type=2"></navigator>
</view> -->
</view>
<view class="navigation">
<view
class="item"
v-for="(item, index) in navigationList"
:key="index"
@click="toPage(item)">
<image class="icon" :src="item.icon" />
<view class="text">{{ item.name }}</view>
</view>
</view>
<!-- <navigator
class="ad"
url="/pages/marketing/yq/index"
open-type="navigate"
hover-class="none">
</navigator> -->
<MerList :get-user-location="getUserLocal" />
<Popup :imgArr="imgList" />
<HomeNew v-if="isNewHome" />
<HomeOld v-else />
</view>
</template>
<script setup lang="ts">
import Taro from '@tarojs/taro';
import { ref } from 'vue';
import { getHomeList } from '@/api/home';
import MerList from '@/components/MerList.vue';
import Popup from '@/components/Popup.vue';
import HomeOld from './components/HomeOld.vue';
import HomeNew from './components/HomeNew.vue';
const statusBarHeight = Taro.getSystemInfoSync().statusBarHeight;
const BarHeight = ref((statusBarHeight as number) + 7);
const isNewHome = ref(process.env.TARO_APP_SHOW_NEW_HOME === 'yes');
const titleImgPath = require(process.env.TARO_APP_TITLE_IMG as string);
interface navigationType {
ID?: number;
type: number;
icon: string;
name: string;
url: string;
}
const navigationList = ref<Array<navigationType>>([]);
const imgList = ref([
// require('../../static/popTip-1.png'),
// require('../../static/popTip.png'),
require('../../static/zhenggai.png'),
]);
Taro.useShareAppMessage(() => ({
title: '捷兑通',
path: `/pages/index/index?scene=${Taro.getStorageSync('token')}`,
imageUrl: 'https://upload.jdt168.com/1714375021923881119_Share.jpg',
}));
const searchVal = ref('');
const clickSearch = () => {
if (!searchVal.value)
return Taro.showToast({
title: '请输入商家名称再搜索',
icon: 'none',
});
Taro.navigateTo({
url: `/pages/search/index?name=${searchVal.value}`,
});
searchVal.value = '';
};
Taro.useLoad(() => {
Taro.getPrivacySetting({
success: res => {
if (res.needAuthorization) {
Taro.requirePrivacyAuthorize({
success: () => {
console.log('用户同意授权');
},
fail: () => {
console.log('用户拒绝授权');
Taro.exitMiniProgram();
},
});
}
},
});
getNavLists();
getUserLocal();
});
const address = ref('获取位置中......');
const getUserLocal = async () => {
Taro.getLocation({
type: 'wgs84',
success: res => {
// Taro.request({
// url: `https://apis.map.qq.com/ws/geocoder/v1/?location=${res.latitude},${res.longitude}&key=4EJBZ-TZXCV-IHUPX-UMI2L-MK3N3-37FSQ&get_poi=1`,
// method: 'GET',
// success: res => {
// switch (res.data.status) {
// case 121:
// Taro.showToast({
// title: res.data.message,
// icon: 'none',
// });
// break;
// default:
// const data = res.data.result.address_component;
// address.value = `${data.district}${data.street_number}`;
// break;
// }
// },
// });
Taro.request({
url: `https://api.tianditu.gov.cn/geocoder?postStr={'lon':${res.longitude},'lat':${res.latitude},'ver':1}&type=geocode&tk=42db4f3dfd1a18d31e73ee90aa2ce054`,
method: 'GET',
success: res => {
const { msg, result } = res.data;
if (msg === 'ok') {
address.value = `${result.addressComponent.county}${result.addressComponent.town}${result.addressComponent.address}`;
} else {
Taro.showToast({
title: msg,
icon: 'none',
});
}
},
});
},
});
};
const getNavLists = async () => {
navigationList.value = [
// {
// type: 1,
// icon: '//p0.meituan.net/csc/5c770748f0028c63741c5ec14df3cc386715.png',
// url: '',
// name: '活动商家',
// },
// {
// type: 1,
// icon: '//p0.meituan.net/csc/4868c06b99008ff7d5f81e6514858c8a7950.png',
// url: '',
// name: '兑换商家',
// },
{
type: 1,
icon: '//p0.meituan.net/csc/f33ad2443a67e9f3474a1d5fd9d529db7504.png',
url: '/pages/users/settled_mer/index',
name: '商户入驻',
},
{
type: 1,
icon: '//p0.meituan.net/csc/0403cf37dd14a6b44b22ffccaa2878f95703.png',
url: '/pages/allClassList/index',
name: '全部服务',
},
];
const res = await getHomeList();
res.data.data.forEach(item => {
navigationList.value.unshift({
ID: item.ID,
type: 2,
icon: item.icon,
url: item.url,
name: item.name,
});
});
};
const toPage = item => {
item.type !== 1
? Taro.navigateTo({
url: `/pages/search/index?id=${item.ID}&name=${item.name}`,
})
: Taro.navigateTo({
url: item.url as string,
});
};
console.log(process.env.TARO_APP_SHOW_NEW_HOME);
console.log(isNewHome.value);
</script>
<style lang="scss">
@import './index.scss';
</style>
<style lang="scss" scoped></style>

View File

@@ -2,7 +2,16 @@
module.exports = {
content: ['./src/pages/index.html', './src/**/*.{html,js,ts,jsx,tsx,vue}'],
theme: {
extend: {},
extend: {
colors: {
primary: '#fa2c19', // Theme Red
secondary: '#ff5d45', // Lighter Red
surface: '#ffffff',
},
boxShadow: {
'card': '0 2px 8px rgba(0, 0, 0, 0.04)',
},
},
},
plugins: [],
corePlugins: {