提交 e295ab8a 作者: 姚志忠

Initial commit

父级
import CollectionApi from "./apis/CollectionApi";
import PhotoApi from "./apis/PhotoApi";
import ItineraryApi from "./apis/ItineraryApi";
export { CollectionApi, PhotoApi, ItineraryApi };
export default class CollectionApi {
constructor(app) {
this.app = app;
}
async get() {
const { qmurl, post, globalData } = this.app;
if (!globalData.user) return null;
try {
const {
data: { code, data },
} = await post(`${qmurl}/api/v1/applet/getUserCollectionList`, {
userId: globalData.user.user.userId,
});
if (code === 200) {
return data;
}
return null;
} catch (error) {
console.error(error);
return null;
}
}
async post(poi) {
const { qmurl, post, globalData } = this.app;
if (!globalData.user) return false;
if (!poi) return false;
try {
const {
data: { code },
} = await post(`${qmurl}/api/v1/applet/addUserCollection`, {
userId: globalData.user.user.userId,
shopType: poi.facility ? 2 : 1,
shopId: poi.shopId ? poi.shopId : poi.facilityId,
});
if (code === 200) return true;
return false;
} catch (error) {
console.error(error);
return false;
}
}
async delete(poi) {
const { qmurl, post, globalData } = this.app;
if (!globalData.user) return false;
if (!poi) return false;
try {
const {
data: { code },
} = await post(`${qmurl}/api/v1/applet/delUserCollection`, {
userId: globalData.user.user.userId,
shopType: poi.facility ? 2 : 1,
shopId: poi.shopId ? poi.shopId : poi.facilityId,
});
if (code === 200) return true;
return false;
} catch (error) {
console.error(error);
return false;
}
}
async bulkDelete(ids) {
const { qmurl, post, globalData } = this.app;
if (!globalData.user) return false;
if (!ids) return;
try {
const {
data: { code },
} = await post(`${qmurl}/api/v1/applet/delUserCollectionBatch`, {
ids,
});
if (code === 200) return true;
return false;
} catch (error) {
console.error(error);
return false;
}
}
}
export default class ItineraryApi {
constructor(app) {
this.app = app;
}
async get() {
const { qmurl, post, globalData } = this.app;
if (!globalData.user) return null;
try {
const {
data: { code, data },
} = await post(`${qmurl}/api/v1/applet/getUserTripList`, {
userId: globalData.user.user.userId,
});
if (code === 200) {
return data;
}
return null;
} catch (error) {
console.error(error);
return null;
}
}
async post({ shopId, goodsTitle, goodsPrice, goodsCover }) {
const { qmurl, post, globalData } = this.app;
if (!globalData.user) return false;
if (!goodsTitle) return false;
try {
const {
data: { code, data },
} = await post(`${qmurl}/api/v1/applet/addUserTrip`, {
userId: globalData.user.user.userId,
shopId,
goodsTitle,
goodsPrice,
goodsCover,
});
if (code === 200) return data;
return null;
} catch (error) {
console.error(error);
return null;
}
}
async updateStatus(id) {
const { qmurl, post, globalData } = this.app;
if (!globalData.user) return false;
if (!id) return false;
try {
const {
data: { code },
} = await post(`${qmurl}/api/v1/applet/updUserTripStatus`, {
id,
status: 1,
});
if (code === 200) return true;
return false;
} catch (error) {
console.error(error);
return false;
}
}
async bulkDelete(ids) {
const { qmurl, post, globalData } = this.app;
if (!globalData.user) return false;
if (!ids) return;
try {
const {
data: { code },
} = await post(`${qmurl}/api/v1/applet/delUserTrip`, {
ids,
});
if (code === 200) return true;
return false;
} catch (error) {
console.error(error);
return false;
}
}
}
export default class PhotoApi {
constructor(app) {
this.app = app;
}
upload(filePath) {
return new Promise((resolve) => {
const { uploadurl } = this.app;
wx.uploadFile({
url: `${uploadurl}/buyerapi/v1/common/upload`, //仅为示例,非真实的接口地址
filePath,
name: "file",
success(res) {
try {
resolve(JSON.parse(res.data).real_file_path);
} catch (error) {
resolve(null);
}
},
fail() {
resolve(null);
},
});
});
}
async get() {
const { qmurl, post, globalData } = this.app;
if (!globalData.user) return null;
try {
const {
data: { code, data },
} = await post(`${qmurl}/api/v1/applet/getUserPhotoRecordList`, {
userId: globalData.user.user.userId,
});
if (code === 200) return data;
return null;
} catch (error) {
console.error(error);
return null;
}
}
async post(photoUrl, source = 1) {
const { qmurl, post, globalData } = this.app;
if (!globalData.user) return false;
if (!photoUrl) return false;
try {
const {
data: { code },
} = await post(`${qmurl}/api/v1/applet/addUserPhotoRecord`, {
userId: globalData.user.user.userId,
photoUrl,
source,
});
if (code === 200) return true;
return false;
} catch (error) {
console.error(error);
return false;
}
}
async clear(source) {
const { qmurl, get, globalData } = this.app;
if (!globalData.user) return false;
if (!source) return false;
try {
const {
data: { code },
} = await get(
`${qmurl}/api/v1/applet/clearUserPhotoRecord?userId=${globalData.user.user.userId}&source=${source}`
);
if (code === 200) return true;
return false;
} catch (error) {
console.error(error);
return false;
}
}
async search(imageUrl, rect) {
const { marketurl, post, globalData } = this.app;
if (!globalData.user) return false;
if (!imageUrl) return false;
if (!rect) {
try {
const { width, height } = await wx.getImageInfo({ src: imageUrl });
rect = { width, height, x: 0, y: 0 };
} catch (error) {
console.error(error);
}
}
try {
const {
data: { data, code },
} = await post(`${marketurl}/detector/baidu/image/search`, {
imageRectDto: rect,
imageUrl,
});
if (code === 20000) return data;
return null;
} catch (error) {
console.error(error);
return null;
}
}
}
差异被折叠。 点击展开。
{
"pages": [
"pages/index/index",
"pages/mine/index",
"pages/search/index",
"pages/shop/index",
"pages/wap/index",
"pages/login/index",
"pages/privacy/index",
"pages/aisearch/index",
"pages/aisearchresult/index",
"pages/itinerary/index",
"pages/nav-end/index",
"pages/poi-map/index"
],
"permission": {
"scope.userLocation": {
"desc": "获取您的位置信息将用于在首页地图展示您所在的楼层及精确位置"
}
},
"requiredPrivateInfos": ["getLocation"],
"window": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "小商AI导航",
"navigationBarBackgroundColor": "#ffffff"
},
"style": "v2",
"componentFramework": "glass-easel",
"sitemapLocation": "sitemap.json",
"lazyCodeLoading": "requiredComponents",
"plugins": {
"WechatSI": {
"version": "0.3.6",
"provider": "wx069ba97219f66d99"
},
"indoormap": {
"version": "1.3.27",
"provider": "wxfe8f0709a4de371b",
"export": "pluginReport.js"
}
}
}
view,
image {
box-sizing: border-box;
}
module.exports = Behavior({
properties: {},
data: {
collectList: getApp().collectList,
shopCollectMap: getApp().shopCollectMap,
facilityCollectMap: getApp().facilityCollectMap,
},
lifetimes: {
attached() {
this.setCollectionData = () => {
this.setData({
collectList: getApp().collectList,
shopCollectMap: getApp().shopCollectMap,
facilityCollectMap: getApp().facilityCollectMap,
});
if (this.setCollected) {
this.setCollected();
}
};
this.setCollectionData();
getApp().events.on("collectionRefresh", this.setCollectionData);
},
detached() {
getApp().events.off("collectionRefresh", this.setCollectionData);
},
},
methods: {
async toggleCollection(poi) {
const { collectionApi } = getApp();
try {
let success;
const collected = this.isCollected(poi);
try {
getApp().sensors?.track("CollectNavPosition", {
action_type: collected ? "取消收藏" : "收藏",
short_market_name: "二区东",
...(poi.tx
? {
x: Number(poi.tx.poi_location.longitude),
y: Number(poi.tx.poi_location.latitude),
}
: {}),
...(poi.facilityId
? {
nav_target_type: poi.facility.name,
booth_floor: poi.tx.poi_fl_seq,
}
: {
store_name: poi.ywZh.name,
booth_addr_street: poi.ywZh.boothAddrStreet,
booth_cover_img: poi.ywZh.cover,
booth_no: poi.ywZh.boothNo,
industry: poi.ywZh.frontIndustryCategory,
booth_floor: poi.ywZh.addrFloor,
main_product_arr: poi.ywZh.mainGoods,
}),
});
} catch (error) {
console.error("埋点失败", error);
}
if (collected) {
success = await collectionApi.delete(poi);
if (success) {
wx.showToast({
title: this.data.isEn ? "uncollected" : "取消收藏",
icon: "none",
});
getApp().refreshCollection();
return;
}
} else {
success = await collectionApi.post(poi);
if (success) {
wx.showToast({
title: this.data.isEn ? "collected" : "已收藏",
icon: "none",
});
getApp().refreshCollection();
return;
}
}
} catch (error) {
console.error(error);
wx.showToast({
title: this.data.isEn ? "collect fail" : "收藏失败",
icon: "none",
});
return;
}
},
isCollected(poi) {
const app = getApp();
if (poi.shopId) {
return !!app.shopCollectMap[poi.shopId];
}
if (poi.facilityId) {
return !!app.facilityCollectMap[poi.facilityId];
}
return false;
},
},
});
module.exports = Behavior({
behaviors: [],
properties: {},
data: {
isEn: getApp().globalData.isEn,
},
lifetimes: {
attached() {
this.setData({ isEn: getApp().globalData.isEn });
this.setIsEn = (isEn) => {
this.setData({ isEn });
if (this.onIsEnChange && typeof this.onIsEnChange === "function") {
this.onIsEnChange(isEn);
}
};
getApp().events.on("lang", this.setIsEn);
},
detached() {
getApp().events.off("lang", this.setIsEn);
},
},
});
module.exports = Behavior({
behaviors: [],
properties: {},
data: {
showBluetoothModal: false,
showLocationModal: false,
showAuthModal: false,
},
methods: {
openBluetoothModal() {
this.setData({ showBluetoothModal: true });
},
closeBluetoothModal() {
this.setData({ showBluetoothModal: false });
},
openLocationModal() {
this.setData({ showLocationModal: true });
},
closeLocationModal() {
this.setData({ showLocationModal: false });
},
openAuthModal() {
this.setData({ showAuthModal: true });
},
closeAuthModal() {
this.setData({ showAuthModal: false });
},
},
});
module.exports = Behavior({
behaviors: [],
properties: {},
data: {
focusedPoi: getApp().globalData.focusedPoi,
},
lifetimes: {
attached() {
this.setData({ focusedPoi: getApp().globalData.focusedPoi });
this.setFocusedPoi = (focusedPoi) => {
this.setData({ focusedPoi }, () => {
if (
this.onFocusedPoiChange &&
typeof this.onFocusedPoiChange === "function"
) {
this.onFocusedPoiChange(focusedPoi);
}
});
};
getApp().events.on("focusedPoi", this.setFocusedPoi);
},
detached() {
getApp().events.off("focusedPoi", this.setFocusedPoi);
},
},
});
module.exports = Behavior({
behaviors: [],
properties: {},
data: {
user: getApp().globalData.user,
},
lifetimes: {
attached() {
this.setData({ user: getApp().globalData.user });
this.setUser = (user) => {
this.setData({ user });
};
getApp().events.on("user", this.setUser);
},
detached() {
getApp().events.off("user", this.setUser);
},
},
});
import langBehavior from "../../behaviors/langBehavior";
Component({
behaviors: [langBehavior],
properties: {},
/**
* 组件的初始数据
*/
data: {},
/**
* 组件的方法列表
*/
methods: {
closeTip() {
this.triggerEvent("close");
},
openSetting() {
console.log("openSetting");
wx.openSetting({
success: ({ authSetting }) => {
if (
authSetting["scope.bluetooth"] &&
authSetting["scope.userLocation"]
)
this.triggerEvent("close");
},
});
},
donothing() {},
},
});
{
"component": true,
"usingComponents": {}
}
\ No newline at end of file
.tip-bg {
position: absolute;
z-index: 3000;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.6);
}
.tip {
position: absolute;
z-index: 3001;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
.arrow {
position: absolute;
top: 7px;
right: 64px;
width: 246px;
height: 32.5px;
}
.image {
position: absolute;
top: 53px;
left: 0;
right: 0;
margin: auto;
width: 304px;
height: 572px;
}
.btns {
position: absolute;
top: 564px;
left: 0;
right: 0;
margin: auto;
width: 260px;
display: flex;
gap: 8px;
.btn {
flex: 1;
height: 38px;
}
}
}
<textarea disabled class="tip-bg"></textarea>
<view class="tip" bind:tap="closeTip">
<image src="./arrow.png" class="arrow" mode="" />
<image catch:tap="donothing" class="image" src="https://cdnimg.chinagoods.com/png/2025/09/03/idwejhk7ytqknlmzs8crrxz8kexgx8tu.png" mode="" />
<view class="btns">
<view class="btn" catch:tap="closeTip"></view>
<view class="btn" catch:tap="openSetting"></view>
</view>
</view>
\ No newline at end of file
import langBehavior from "../../behaviors/langBehavior";
Component({
behaviors: [langBehavior],
properties: {},
/**
* 组件的初始数据
*/
data: { isIOS: getApp().isIOS },
/**
* 组件的方法列表
*/
methods: {
close() {
this.triggerEvent("close");
},
doNothing() {},
},
});
{
"component": true,
"usingComponents": {}
}
\ No newline at end of file
.bluetooth-bg {
position: absolute;
z-index: 3000;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.6);
}
.bluetooth-modal {
position: fixed;
top: 0;
left: 0;
z-index: 3001;
width: 100vw;
height: 100vh;
.image {
position: absolute;
top: 32px;
left: 0;
right: 0;
margin: auto;
width: 304px;
height: 572px;
}
.btn {
position: absolute;
top: 542px;
left: 0;
right: 0;
margin: auto;
width: 260px;
height: 38px;
}
}
<textarea disabled class="bluetooth-bg"></textarea>
<view class="bluetooth-modal" bind:tap="close">
<image wx:if="{{isIOS}}" class="image" catch:tap="donothing" src="https://cdnimg.chinagoods.com/png/2025/09/01/dacfdaux2wtvloq0mfvhd48otw8gplir.png" mode="" />
<image wx:else catch:tap="donothing" class="image" src="https://cdnimg.chinagoods.com/png/2025/09/01/mbx4nn2dwya3xovr7lp1yfhzrn6qgxsq.png" catch:tap="doNothing" mode="" />
<view class="btn" catch:tap="close"></view>
</view>
\ No newline at end of file
Component({
behaviors: [collectBehavior],
properties: {
poi: {
type: Object,
value: null
}
},
data: {
collected: false
},
observers: {
poi() {
this.setData({
collected: this.isCollected(this.data.poi)
});
}
},
methods: {
async handleCollect() {
if (!getApp().checkUser()) return;
const result = await this.toggleCollection(this.data.poi);
if (result.success) {
getApp().refreshCollection();
}
}
}
});
\ No newline at end of file
import langBehavior from "../../behaviors/langBehavior";
import poiFocusBehavior from "../../behaviors/poiFocusBehavior";
Component({
behaviors: [langBehavior, poiFocusBehavior],
properties: { currentIndex: Number },
data: {
tabs: [
{
url: "/pages/index/index",
text: "导航",
textEn: "Nav",
icon: "./index.png",
activeIcon: "./selectedIndex.png",
},
{
url: "/pages/index/index?mode=surroundings",
text: "周边",
textEn: "Nearby",
icon: "./surroundings.png",
activeIcon: "./selectedSurroundings.png",
},
{
url: "/pages/mine/index",
text: "我的",
textEn: "My",
icon: "./mine.png",
activeIcon: "./selectedMine.png",
},
],
},
/**
* 组件的方法列表
*/
methods: {
handleTap({ currentTarget: { id } }) {
if (
["0", "1", "2"].includes(id) &&
this.data.currentIndex !== Number(id)
) {
const { currentIndex } = this.data;
const tab = this.data.tabs[id];
const lastTab = this.data.tabs[currentIndex];
getApp().sensors?.track("BottomMenuClick", {
menu_name: tab.text,
btn_name_referrer: lastTab.text,
});
this.triggerEvent("tab", tab);
}
},
},
});
{
"component": true,
"usingComponents": {}
}
\ No newline at end of file
.tab-bar {
position: fixed;
bottom: 0;
left: 0;
width: 100vw;
height: 80px;
display: flex;
justify-content: space-between;
background: #fff;
padding: 0 16px 0 16px;
z-index: 1000;
border-top: 1px solid rgba(0, 0, 0, 0.03);
&.hide {
display: none;
}
.tab {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
width: 80px;
height: 56px;
color: rgba(0, 0, 0, 0.4);
font-size: 10px;
line-height: 14px;
font-weight: 500;
&.active {
color: #ed383e;
font-weight: 600;
}
.icon {
width: 24px;
height: 24px;
}
.text {
opacity: 0.9;
}
}
}
<view class="tab-bar {{!!focusedPoi?'hide':''}}">
<view class="tab {{currentIndex===index?'active':''}}" wx:key="url" wx:for="{{tabs}}" id="{{index}}" bind:tap="handleTap">
<image src="{{item.activeIcon}}" hidden="{{currentIndex!==index}}" class="icon" mode="" />
<image src="{{item.icon}}" hidden="{{currentIndex===index}}" class="icon" mode="" />
<view class="text">{{isEn ? item.textEn: item.text}}</view>
</view>
</view>
\ No newline at end of file
import langBehavior from "../../behaviors/langBehavior";
Component({
behaviors: [langBehavior],
/**
* 组件的属性列表
*/
properties: {},
/**
* 组件的初始数据
*/
data: {},
/**
* 组件的方法列表
*/
methods: {},
});
{
"component": true,
"usingComponents": {}
}
\ No newline at end of file
.empty {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
color: #999;
font-family: "PingFang SC";
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: normal;
.icon {
width: 114px;
height: 56px;
}
}
<view class="empty">
<image src="./empty.png" class="icon" mode="" />
{{isEn?'No information available yet':'还没有任何信息'}}
</view>
\ No newline at end of file
import langBehavior from "../../behaviors/langBehavior";
Component({
behaviors: [langBehavior],
/**
* 组件的属性列表
*/
properties: {},
/**
* 组件的初始数据
*/
data: {},
/**
* 组件的方法列表
*/
methods: {},
});
{
"component": true,
"usingComponents": {}
}
\ No newline at end of file
.end {
width: 100%;
display: flex;
height: 40px;
gap: 6px;
align-items: center;
justify-content: center;
.icon {
width: 20px;
height: 20px;
}
color: #999;
font-family: "PingFang SC";
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: normal;
}
<view class="end">
<image class="icon" src="./end.png" mode="" />
{{isEn?'no more':'暂无更多'}}
</view>
\ No newline at end of file
import langBehavior from "../../behaviors/langBehavior";
Component({
behaviors: [langBehavior],
properties: {
floors: Array,
floorName: String,
showReset: {
type: Boolean,
value: true,
},
refreshing: Boolean,
showSuccess: Boolean,
neverAuthed: Boolean,
},
/**
* 组件的初始数据
*/
data: {},
/**
* 组件的方法列表
*/
methods: {
handleReset() {
this.triggerEvent("resetmap");
},
handleFloor({ currentTarget: { id: floorName } }) {
this.triggerEvent("floorname", floorName);
},
},
});
{
"component": true,
"usingComponents": {}
}
\ No newline at end of file
.reset-wrapper {
position: absolute;
top: -64px;
right: 10px;
.tip1 {
position: absolute;
z-index: 1;
top: 0;
right: 0;
height: 40px;
border-radius: 8px;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(1px);
display: inline-flex;
padding-left: 14px;
padding-right: 50px;
color: #fff;
font-family: "PingFang SC";
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 18px;
align-items: center;
white-space: nowrap;
}
.tip2 {
position: absolute;
z-index: 1;
top: 0;
right: 0;
height: 40px;
border-radius: 8px;
background: linear-gradient(90deg, #fa643c 0%, #e92927 100%);
display: inline-flex;
padding-left: 14px;
padding-right: 50px;
color: #fff;
font-family: "PingFang SC";
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 18px;
align-items: center;
white-space: nowrap;
}
.tip3 {
position: absolute;
right: 44px;
top: 0;
height: 40px;
display: flex;
align-items: center;
.left {
display: inline-flex;
align-items: center;
height: 24px;
border-radius: 4px;
background: linear-gradient(90deg, #fa643c 0%, #e92927 100%);
padding: 0 6px;
color: #fff;
text-align: center;
font-family: "PingFang SC";
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px;
white-space: nowrap;
}
.right {
width: 3px;
height: 8px;
}
}
}
.reset {
position: absolute;
z-index: 2;
top: 0;
right: 0;
display: flex;
width: 40px;
height: 40px;
justify-content: center;
align-items: center;
border-radius: 8px;
background: #fff;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.05);
z-index: 2;
.size-24 {
width: 24px;
height: 24px;
}
.refresh {
animation: spin 0.5s linear infinite;
}
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.reset-mask {
position: absolute;
top: -74px;
right: 0px;
width: 60px;
height: 60px;
z-index: 1;
}
.floors {
position: absolute;
display: flex;
flex-direction: column;
top: -152px;
&.has-reset {
top: -202px;
}
right: 10px;
padding: 7px 4px;
z-index: 2;
gap: 2px;
align-items: center;
border-radius: 8px;
background: var(--VI-0, #fff);
box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.05);
z-index: 2;
.arrow {
width: 8px;
height: 3px;
}
.list {
display: flex;
flex-direction: column;
gap: 4px;
.floor {
display: flex;
width: 32px;
height: 32px;
align-items: center;
justify-content: center;
color: #323337;
text-align: center;
font-family: DINPro;
font-size: 14px;
font-style: normal;
font-weight: 500;
border-radius: 8px;
&.active {
background: #fff3f3;
color: #e92927;
}
}
}
}
.floors-mask {
position: absolute;
top: -152px;
&.has-reset {
top: -202px;
}
right: 10px;
width: 40px;
height: 128px;
z-index: 1;
}
<textarea disabled class="reset-mask" wx:if="{{showReset}}"></textarea>
<view class="reset-wrapper">
<view class="reset" bind:tap="handleReset" wx:if="{{showReset}}">
<image wx:if="{{refreshing}}" class="size-24 refresh" src="./refresh.png" mode="" />
<image wx:else class="size-24" src="./reset.png" mode="" />
</view>
<view class="tip1" wx:if="{{refreshing}}">{{isEn?'Locating...':'定位中…'}}</view>
<view class="tip2" wx:elif="{{showSuccess}}">{{isEn?'Successfully located':'定位成功'}}</view>
<view class="tip3" wx:elif="{{neverAuthed}}">
<view class="left">{{isEn?'Tap to locate':'点击获取定位'}}</view>
<image src="./tri.png" class="right" mode="" />
</view>
</view>
<textarea disabled class="floors-mask {{showReset?'has-reset':''}}"></textarea>
<view class="floors {{showReset?'has-reset':''}}">
<image class="arrow" src="./floorUp.png" mode="" />
<view class="list">
<view wx:for="{{floors}}" class="floor {{floorName===item.floorName?'active':''}}" id="{{item.floorName}}" wx:key="floorName" bind:tap="handleFloor">{{item.displayName}}</view>
</view>
<image class="arrow" src="./floorDown.png" mode="" />
</view>
\ No newline at end of file
// components/highlight-text/index.js
Component({
properties: {
text: {
type: String,
value: "",
},
keyword: {
type: String,
value: "",
},
},
observers: {
"text, keyword": function (text, keyword) {
this.updateTextArray(text, keyword);
},
},
data: {
textArray: [],
},
methods: {
updateTextArray(text, keyword) {
if (!text || !keyword) {
this.setData({
textArray: [{ text: text || "", isKeyword: false }],
});
return;
}
const reg = new RegExp(`(${keyword})`, "gi");
const parts = text.split(reg);
const textArray = parts.map((part) => ({
text: part,
isKeyword: part.toLowerCase() === keyword.toLowerCase(),
}));
this.setData({ textArray });
},
},
});
{
"component": true,
"usingComponents": {}
}
\ No newline at end of file
/* components/highlight-text/index.wxss */
.highlight {
color: #e92927;
}
<block wx:for="{{textArray}}" wx:key="index">
<text class="{{item.isKeyword ? 'highlight' : ''}}">{{item.text}}</text>
</block>
\ No newline at end of file
import langBehavior from "../../behaviors/langBehavior";
const langs = [
"简体中文",
"English",
"العربية",
// "日本語",
"한국어",
"Deutsch",
"Français",
"Português",
"Español",
"Italiano",
"Bahasa Melayu",
"русский",
"Bahasa Indonesia",
"ภาษาไทย",
"Türkçe",
"繁體中文 香港/澳門",
"繁體中文 中國臺灣",
"Tiếng Việt",
];
Component({
behaviors: [langBehavior],
properties: {
type: String,
},
/**
* 组件的初始数据
*/
data: {
showOptions: false,
langs,
showTip: false,
},
/**
* 组件的方法列表
*/
methods: {
handleLang() {
this.setData({ showOptions: true });
},
closeOptions() {
this.setData({ showOptions: false });
},
chooseLang({ currentTarget: { id: lang } }) {
if (
(lang === "简体中文" && this.data.isEn) ||
(lang === "English" && !this.data.isEn)
) {
getApp().switchLang();
}
this.setData({ showOptions: false });
if (!(lang === "简体中文" || lang === "English")) {
this.setData({ showTip: true });
}
},
closeTip() {
this.setData({ showTip: false });
},
donothing() {},
},
});
{
"component": true,
"usingComponents": {}
}
\ No newline at end of file
.langBtn {
position: absolute;
z-index: 2;
top: 10px;
right: 10px;
display: flex;
flex-direction: column;
width: 40px;
height: 64px;
gap: 2px;
justify-content: center;
align-items: center;
border-radius: 8px;
background: #fff;
box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.05);
&.white {
background: none;
box-shadow: none;
height: 40px;
}
&.black {
background: rgba(0, 0, 0, 0.5);
box-shadow: none;
.texts {
color: #f3f3f3;
}
}
.logo {
width: 24px;
height: 24px;
}
.texts {
display: flex;
height: 24px;
flex-direction: column;
color: #333;
text-align: center;
font-family: "PingFang SC";
font-size: 10px;
font-style: normal;
font-weight: 400;
line-height: 12px; /* 120% */
}
}
.options-bg {
position: absolute;
z-index: 3000;
top: 0;
right: 0;
width: 100vw;
height: 196px;
border-radius: 0 0 8px 8px;
border-top: #f3f3f3;
background: #fff;
}
.options {
position: absolute;
z-index: 3001;
top: 0;
right: 0;
width: 100vw;
height: 196px;
.close {
position: absolute;
top: 16px;
right: 8px;
width: 32px;
height: 32px;
}
.t1 {
position: absolute;
top: 23px;
left: 12px;
color: #333;
text-align: center;
font-family: "PingFang SC";
font-size: 14px;
font-style: normal;
font-weight: 600;
line-height: 18px;
}
.langs {
position: absolute;
top: 56px;
left: 0;
width: 100vw;
height: 128px;
.grid {
width: 1064px;
height: 128px;
padding: 0 12px;
display: grid;
grid-auto-flow: column;
grid-template-rows: 1fr 1fr 1fr;
gap: 4px;
.item {
position: relative;
display: flex;
align-items: center;
padding-left: 10px;
width: 170px;
height: 40px;
border-radius: 4px;
border-bottom: 0.5px solid rgba(255, 255, 255, 0.1);
background: #f7f7f7;
color: #333;
font-family: "PingFang SC";
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 18px;
.tick {
position: absolute;
width: 12px;
height: 12px;
top: 0;
bottom: 0;
right: 18px;
margin: auto;
}
}
}
}
}
.tip-bg {
position: absolute;
z-index: 3000;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.6);
}
.tip {
position: absolute;
z-index: 3001;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
.arrow {
position: absolute;
top: 7px;
right: 64px;
width: 246px;
height: 32.5px;
}
.image {
position: absolute;
top: 53px;
left: 0;
right: 0;
margin: auto;
width: 304px;
height: 530px;
}
.btn {
position: absolute;
top: 521px;
left: 0;
right: 0;
margin: auto;
z-index: 2;
width: 260px;
height: 38px;
}
}
<view class="langBtn {{type}}" catch:tap="handleLang">
<block wx:if="{{type==='white'}}">
<image class="logo" src="./switchWhite.png" mode="" />
</block>
<block wx:elif="{{type==='black'}}">
<image class="logo" src="./switchWhite.png" mode="" />
</block>
<block wx:else>
<image class="logo" src="./switch.png" mode="" />
</block>
<view class="texts" wx:if="{{type!== 'white'}}">
<view>{{isEn?'Change':'更换'}}</view>
<view>{{isEn?'Lang':'语言'}}</view>
</view>
</view>
<textarea disabled class="options-bg" wx:if="{{showOptions}}"></textarea>
<view wx:if="{{showOptions}}" class="options">
<view class="t1">{{isEn?'Change language':'更换语言'}}</view>
<image src="./close.png" class="close" mode="" bind:tap="closeOptions" />
<scroll-view scroll-x enhanced show-scrollbar="{{false}}" class="langs">
<view class="grid">
<view class="item" wx:for="{{langs}}" wx:key="*this" bind:tap="chooseLang" id="{{item}}">{{item}}
<image wx:if="{{item==='简体中文'&&!isEn||item==='English'&&isEn}}" src="./tick.png" class="tick" mode="" />
</view>
</view>
</scroll-view>
</view>
<textarea disabled class="tip-bg" wx:if="{{showTip}}"></textarea>
<view wx:if="{{showTip}}" class="tip" bind:tap="closeTip">
<image src="./arrow.png" class="arrow" mode="" />
<image catch:tap="donothing" class="image" src="https://cdnimg.chinagoods.com/png/2025/09/03/e3evgoktoetqebfymujhse0kmoxzjkt8.png" mode="" />
<view class="btn" catch:tap="closeTip"></view>
</view>
\ No newline at end of file
import langBehavior from "../../behaviors/langBehavior";
Component({
behaviors: [langBehavior],
/**
* 组件的属性列表
*/
properties: {},
/**
* 组件的初始数据
*/
data: {},
/**
* 组件的方法列表
*/
methods: {},
});
{
"component": true,
"usingComponents": {}
}
\ No newline at end of file
.loading {
width: 100%;
display: flex;
height: 90px;
padding-top: 20px;
padding-bottom: 50px;
gap: 6px;
align-items: center;
justify-content: center;
.icon {
width: 20px;
height: 20px;
animation: spin 0.5s linear infinite;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
color: #999;
font-family: "PingFang SC";
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: normal;
}
<view class="loading">
<image class="icon" src="./loading.png" mode="" />
{{isEn?'loading':'加载中'}}
</view>
\ No newline at end of file
import langBehavior from "../../behaviors/langBehavior";
Component({
behaviors: [langBehavior],
properties: {},
/**
* 组件的初始数据
*/
data: { isIOS: getApp().isIOS },
/**
* 组件的方法列表
*/
methods: {
close() {
this.triggerEvent("close");
},
doNothing() {},
},
});
{
"component": true,
"usingComponents": {}
}
\ No newline at end of file
.bluetooth-bg {
position: absolute;
z-index: 3000;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.6);
}
.bluetooth-modal {
position: fixed;
top: 0;
left: 0;
z-index: 3001;
width: 100vw;
height: 100vh;
.image {
position: absolute;
top: 32px;
left: 0;
right: 0;
margin: auto;
width: 304px;
height: 572px;
}
.btn {
position: absolute;
top: 542px;
left: 0;
right: 0;
margin: auto;
width: 260px;
height: 38px;
}
}
<textarea disabled class="bluetooth-bg"></textarea>
<view class="bluetooth-modal" bind:tap="close">
<image wx:if="{{isIOS}}" class="image" catch:tap="donothing" src="https://cdnimg.chinagoods.com/png/2025/09/01/l5z7pr16cdmgrjxqzwtkps1fl5m4yaiy.png" mode="" />
<image wx:else catch:tap="donothing" class="image" src="https://cdnimg.chinagoods.com/png/2025/09/01/6rkar7dc5ot10homibl4ohrnrun8vwtq.png" catch:tap="doNothing" mode="" />
<view class="btn" catch:tap="close"></view>
</view>
\ No newline at end of file
{
"component": true,
"usingComponents": {}
}
\ No newline at end of file
/* pages/index/bottom-hud/map-wrapper/index.wxss */
.map {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: calc(100vh - 90px);
z-index: 1;
}
<map class="map" id="indexMap" show-compass show-location="{{userLocation&&!(userLocation&&userLocation.floorName&&userLocation.floorName!==floorName)}}" enable-rotate enable-3D enable-overlooking enable-auto-max-overlooking enable-indoor subkey="QUIBZ-OTL6U-T2MVM-GMPIT-EON66-LGBCU" latitude="{{latitude}}" longitude="{{longitude}}" markers="{{markers}}" max-scale="22" setting="{{setting}}" bind:tap="{{type === 'index' ? 'handleTap' : ''}}" bind:poitap="{{type === 'index' ? 'handleTap' : ''}}" bind:markertap="{{type === 'surroundings' ? 'handleMarkerTap' : ''}}" bind:callouttap="{{type === 'index' ? 'handleCalloutTap' : ''}}" bind:indoorchange="handleIndoorChange"></map>
\ No newline at end of file
import poiFocusBehavior from "../../behaviors/poiFocusBehavior";
import langBehavior from "../../behaviors/langBehavior";
import collectBehavior from "../../behaviors/collectBehavior";
import { toRoutePlan } from "../../util";
Component({
behaviors: [poiFocusBehavior, langBehavior, collectBehavior],
properties: {
isIndex: Boolean,
},
data: {
collected: false,
},
observers: {
focusedPoi() {
this.setCollected();
},
},
methods: {
setCollected() {
if (this.data.focusedPoi)
this.setData({ collected: this.isCollected(this.data.focusedPoi) });
},
handleClose() {
getApp().blurPoi();
const pages = getCurrentPages();
if (pages.length > 1) {
wx.navigateBack();
}
},
handleDetail() {
wx.navigateTo({
url: `/pages/shop/index?shopId=${this.data.focusedPoi.shopId}`,
});
},
async handleGo() {
const { isEn, focusedPoi: poi, isIndex } = this.data;
try {
getApp().sensors?.track("NavEntryBtnClick", {
page_name: isIndex ? "导航弹出框" : "周边弹出框",
short_market_name: "二区东",
...(poi.tx
? {
x: Number(poi.tx.poi_location.longitude),
y: Number(poi.tx.poi_location.latitude),
}
: {}),
...(poi.facilityId
? {
nav_target_type: poi.facility.name,
booth_floor: poi.tx.poi_fl_seq,
}
: {
store_name: poi.ywZh.name,
booth_addr_street: poi.ywZh.boothAddrStreet,
booth_cover_img: poi.ywZh.cover,
booth_no: poi.ywZh.boothNo,
industry: poi.ywZh.frontIndustryCategory,
booth_floor: poi.ywZh.addrFloor,
}),
});
} catch (error) {
console.error("埋点失败", error);
}
try {
const res = await toRoutePlan(poi, isEn);
if (res === "showBluetoothModal")
return this.triggerEvent("showbluetoothmodal");
if (res === "showLocationModal")
return this.triggerEvent("showlocationmodal");
if (res === "showAuthModal") return this.triggerEvent("showauthmodal");
} catch (error) {
console.error(error);
}
},
async handleCollect() {
if (!getApp().checkUser()) return;
this.toggleCollection(this.data.focusedPoi);
},
},
lifetimes: {
attached() {
this.setCollected();
},
},
});
{
"component": true,
"usingComponents": {
"shop-bottom": "../shop-bottom/index",
"shop-maingoods": "../shop-maingoods/index",
"shop-addr": "../shop-addr/index",
"shop-name": "../shop-name/index"
}
}
.popup {
position: absolute;
bottom: 0;
left: 0;
z-index: 300;
width: 100vw;
border-radius: 16px 16px 0px 0px;
background: #fff;
backdrop-filter: blur(4px);
padding: 18px 17px 110px 16px;
min-height: 188px;
.close {
position: absolute;
width: 32px;
height: 32px;
top: 8px;
right: 8px;
}
.t1 {
color: #333;
font-family: "PingFang SC";
font-size: 16px;
font-style: normal;
font-weight: 600;
line-height: 22px;
padding-right: 23px;
}
.t2 {
display: inline-flex;
align-items: center;
gap: 4px;
color: #666;
font-family: "PingFang SC";
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px;
}
.t3 {
color: #666;
font-family: "PingFang SC";
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 18px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-wrap: break-word;
}
.stick {
width: 1px;
height: 16px;
background: #666666;
}
.fac {
display: flex;
gap: 10px;
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
}
.right {
display: flex;
flex-direction: column;
gap: 6px;
}
}
.shop {
display: flex;
gap: 10px;
.avatar {
width: 96px;
height: 96px;
border-radius: 8px;
}
.right {
display: flex;
flex-direction: column;
gap: 16px;
flex: 1;
.c-gap-6 {
display: flex;
flex-direction: column;
gap: 6px;
}
}
}
}
<view class="popup" wx:if="{{focusedPoi}}">
<slot></slot>
<view class="shop" wx:if="{{focusedPoi.facility&&focusedPoi.facility.name==='yandoocafe'}}">
<image wx:if="{{focusedPoi.facility.logo}}" src="{{focusedPoi.facility.logo}}" class="avatar" mode="" />
<view class="right">
<view class="c-gap-6">
<view class="t1">{{isEn?focusedPoi.facility.nameEn:focusedPoi.facility.name}}</view>
<view class="t2">{{isEn?focusedPoi.facility.addressEn:focusedPoi.facility.address}}</view>
</view>
</view>
</view>
<view class="fac" wx:elif="{{focusedPoi.facility}}">
<image wx:if="{{focusedPoi.facility.logo}}" src="{{focusedPoi.facility.logo}}" class="avatar" mode="" />
<view class="right">
<view class="t1">{{isEn?focusedPoi.facility.nameEn:focusedPoi.facility.name}}</view>
<view class="t2">{{isEn?focusedPoi.facility.addressEn:focusedPoi.facility.address}}</view>
</view>
</view>
<view class="fac" wx:elif="{{isEn&&!focusedPoi.ywEn}}">
<image wx:if="{{focusedPoi.ywZh.cover}}" src="{{focusedPoi.ywZh.cover}}" class="avatar" mode="" />
<view class="right">
<view class="t1">
<shop-name shop="{{focusedPoi}}"></shop-name>
</view>
<view class="t2">
<shop-addr shop="{{focusedPoi}}"></shop-addr>
</view>
</view>
</view>
<view class="shop" wx:else>
<image wx:if="{{focusedPoi.ywZh.cover}}" src="{{focusedPoi.ywZh.cover}}" class="avatar" mode="" />
<view class="right">
<view class="c-gap-6">
<view class="t1">
<shop-name shop="{{focusedPoi}}"></shop-name>
</view>
<view class="t2">
<shop-addr shop="{{focusedPoi}}"></shop-addr>
</view>
</view>
<view class="t3">
<shop-maingoods shop="{{focusedPoi}}"></shop-maingoods>
</view>
</view>
</view>
<image class="close" src="./close.png" mode="" bind:tap="handleClose" />
<shop-bottom show-detail="{{focusedPoi.ywZh}}" collected="{{collected}}" show-go="{{focusedPoi.tx}}" bind:detail="handleDetail" bind:go="handleGo" bind:collect="handleCollect"></shop-bottom>
</view>
\ No newline at end of file
import langBehavior from "../../behaviors/langBehavior";
import { getAddr } from "../../util";
Component({
behaviors: [langBehavior],
properties: {
shop: Object,
keyword: {
type: String,
value: "",
},
bigGap: Boolean,
},
data: {
enAddr: "",
buildingFloor: "",
street: "",
},
observers: {
isEn() {
this.setText();
},
shop() {
this.setText();
},
},
methods: {
setText() {
const { isEn, shop } = this.data;
const { enAddr, buildingFloor, street } = getAddr(shop, isEn);
this.setData({ enAddr, buildingFloor, street });
},
},
lifetimes: {
attached() {
this.setText();
},
},
});
{
"component": true,
"usingComponents": {
"highlight-text": "../highlight-text/index"
}
}
.shop-addr {
display: flex;
gap: 4px;
align-items: center;
&.big-gap {
gap: 8px;
}
.stick {
width: 1px;
height: 8px;
flex-shrink: 0;
background: currentColor;
}
}
<block wx:if="{{keyword}}">
<view class="shop-addr" wx:if="{{isEn}}">
<highlight-text text="{{enAddr}}" keyword="{{keyword}}"></highlight-text>
</view>
<view wx:else class="shop-addr">
<highlight-text text="{{buildingFloor}}" keyword="{{keyword}}"></highlight-text>
<view class="stick"></view>
<highlight-text text="{{street}}" keyword="{{keyword}}"></highlight-text>
<view class="stick"></view>
<highlight-text text="{{shop.ywZh.boothNo}}" keyword="{{keyword}}"></highlight-text>
</view>
</block>
<block wx:else>
<view class="shop-addr" wx:if="{{isEn}}">
{{enAddr}}
</view>
<view wx:else class="shop-addr {{bigGap?'big-gap':''}}">
{{buildingFloor}}
<view class="stick"></view>
{{street}}
<view class="stick"></view>
{{shop.ywZh.boothNo}}
</view>
</block>
\ No newline at end of file
import langBehavior from "../../behaviors/langBehavior";
Component({
behaviors: [langBehavior],
properties: {
showDetail: Boolean,
collected: Boolean,
showGo: Boolean,
},
/**
* 组件的初始数据
*/
data: {},
/**
* 组件的方法列表
*/
methods: {
handleDetail() {
this.triggerEvent("detail");
},
handleCollect() {
this.triggerEvent("collect");
},
handleGo() {
this.triggerEvent("go");
},
},
});
{
"component": true,
"usingComponents": {}
}
\ No newline at end of file
.shop-bottom {
position: absolute;
bottom: 0;
left: 0;
width: 100vw;
height: 90px;
background: #fff;
border-top: 1px solid var(--B-3, rgba(0, 0, 0, 0.03));
padding: 12px;
padding-top: 8px;
display: flex;
gap: 27px;
.btns {
display: flex;
gap: 16px;
height: 40px;
.btn {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
width: auto;
padding: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
padding-bottom: 0;
background-color: none;
background: none;
color: #999;
font-family: "PingFang SC";
font-size: 10px;
font-style: normal;
font-weight: 400;
line-height: 12px;
.size-24 {
width: 24px;
height: 24px;
}
}
}
.go {
display: flex;
flex: 1;
height: 38px;
border-radius: 99px;
background: linear-gradient(90deg, #fa643c 0%, #e92927 100%);
gap: 4px;
align-items: center;
justify-content: center;
.size-20 {
width: 20px;
height: 20px;
}
color: #fff;
font-family: "PingFang SC";
font-size: 14px;
font-style: normal;
font-weight: 600;
line-height: 18px;
}
}
<view class="shop-bottom">
<view class="btns">
<view class="btn" wx:if="{{showDetail}}" bind:tap="handleDetail">
<image class="size-24" src="./detail.png" mode="" />
<view>{{isEn?'Detail':'详情'}}</view>
</view>
<button class="btn" open-type="share">
<image class="size-24" src="./share.png" mode="" />
<view>{{isEn?'Share':'分享'}}</view>
</button>
<view class="btn" bind:tap="handleCollect">
<image wx:if="{{collected}}" class="size-24" src="./starred.png" mode="" />
<image wx:else class="size-24" src="./star.png" mode="" />
<view>{{isEn?'Collect':'收藏'}}</view>
</view>
</view>
<view class="go" wx:if="{{showGo}}" bind:tap="handleGo">
<image class="size-20" src="./go.png" mode="" />
<view>{{isEn?'Go here':'到这去'}}</view>
</view>
</view>
\ No newline at end of file
差异被折叠。 点击展开。
差异被折叠。 点击展开。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论