This commit is contained in:
2025-12-25 08:26:09 +08:00
parent ab5c01bf5c
commit 964e4f9c33
72 changed files with 2474 additions and 1065 deletions

10
App.vue
View File

@@ -1,14 +1,10 @@
<script>
export default {
globalData: {
indexParam: null
},
onLaunch: function() {
console.log('App Launch')
Vue.prototype.$checkAuth = () => {
if (!uni.getStorageSync('vuex_token')) {
uni.$emit("needLogin")
return false
}
return true
}
},
onShow: function() {
console.log('App Show')

View File

@@ -1,8 +1,8 @@
// 全局配置文件
// let baseUrl = 'http://192.168.0.108:8088';
// let staticUrl = 'http://192.168.0.108:8088';
let baseUrl = 'https://sourcebyte.vip';
let staticUrl = 'https://sourcebyte.vip';
// let baseUrl = 'https://sourcebyte.vip';
// let staticUrl = 'https://sourcebyte.vip';
let baseUrl = 'http://localhost:8089';
let staticUrl = 'http://localhost:8089';
// 版本号 用于更新
let version = 1;
// vuex_version版本号 用于显示

View File

@@ -12,8 +12,11 @@ const install = (Vue, vm) => {
// config.header.Token = '5d33018e653d897fc259b42cf022c1b3';
// 方式一存放在vuex的token假设使用了uView封装的vuex方式https://uviewui.com/components/globalVariable.html
// 自定义token头
// config.header.Authorization = vm.vuex_token;
return config;
config.header = {
...config.header, // ⭐ 保留你外部传入的 header
'content-type': 'application/json'
}
return config;
};
// 响应拦截,判断状态码是否通过
Vue.prototype.$u.http.interceptor.response = (res) => {

View File

@@ -0,0 +1,105 @@
<template>
<view class="date-filter">
<!-- 开始日期 -->
<view class="filter-item" @click="$refs.startPicker.open(startDate)">
<text class="label">开始日期</text>
<text class="value">{{ startDate || '请选择' }}</text>
</view>
<view class="divider"></view>
<!-- 结束日期 -->
<view class="filter-item" @click="$refs.endPicker.open(endDate)">
<text class="label">结束日期</text>
<text class="value">{{ endDate || '请选择' }}</text>
</view>
<!-- 开始日期选择器 -->
<DatePicker
v-model="startDate"
ref="startPicker"
@confirm="selectStart"
/>
<!-- 结束日期选择器 -->
<DatePicker
v-model="endDate"
ref="endPicker"
@confirm="selectEnd"
/>
</view>
</template>
<script>
import DatePicker from './DatePicker.vue';
export default {
name: 'DateFilter',
components: { DatePicker },
props: {
start: { type: String, default: '' },
end: { type: String, default: '' }
},
data() {
return {
startDate: this.start,
endDate: this.end
};
},
methods: {
/** 选择开始日期 */
selectStart(e) {
this.startDate = e;
this.$emit('update:start', e);
this.$emit('change', { start: this.startDate, end: this.endDate });
},
/** 选择结束日期 */
selectEnd(e) {
this.endDate = e;
this.$emit('update:end', e);
this.$emit('change', { start: this.startDate, end: this.endDate });
}
}
};
</script>
<style scoped lang="scss">
.date-filter {
display: flex;
align-items: center;
padding: 20rpx;
background: #ffffff;
border-radius: 12rpx;
}
.filter-item {
flex: 1;
display: flex;
flex-direction: column;
}
.label {
font-size: 24rpx;
color: #888;
}
.value {
font-size: 28rpx;
padding-left: 30rpx;
color: #333;
margin-top: 6rpx;
}
.divider {
width: 2rpx;
height: 40rpx;
background: #e5e5e5;
margin: 0 20rpx;
}
</style>

View File

@@ -0,0 +1,232 @@
<template>
<view v-if="visible" class="picker-overlay" @click.stop="close">
<view class="picker-container" @click.stop>
<view class="picker-header">
<text class="cancel" @click="close">取消</text>
<text class="title">请选择日期</text>
<text class="confirm" @click="confirm">确定</text>
</view>
<view class="picker-columns">
<!-- -->
<scroll-view
class="column"
scroll-y
:scroll-top="scrollPos[0]"
@scroll="onScroll(0,$event)"
>
<view
v-for="(y,i) in years"
:key="i"
class="item"
:class="{active:selected[0]===i}"
@click="select(0,i)"
>
{{y}}
</view>
</scroll-view>
<!-- -->
<scroll-view
class="column"
scroll-y
:scroll-top="scrollPos[1]"
@scroll="onScroll(1,$event)"
>
<view
v-for="(m,i) in months"
:key="i"
class="item"
:class="{active:selected[1]===i}"
@click="select(1,i)"
>
{{m}}
</view>
</scroll-view>
<!-- -->
<scroll-view
class="column"
scroll-y
:scroll-top="scrollPos[2]"
@scroll="onScroll(2,$event)"
>
<view
v-for="(d,i) in days"
:key="i"
class="item"
:class="{active:selected[2]===i}"
@click="select(2,i)"
>
{{d}}
</view>
</scroll-view>
</view>
</view>
</view>
</template>
<script>
export default {
props: { value: String },
data() {
return {
visible: false,
years: [],
months: Array.from({ length: 12 }, (_, i) => i + 1),
days: [],
selected: [0, 0, 0],
scrollPos: [0, 0, 0], // 小程序 scroll-view 只能用 scroll-top 来滚动
itemH: 40,
scrollTimer: null,
};
},
methods: {
open(val) {
this.visible = true;
const currentYear = new Date().getFullYear();
this.years = Array.from({ length: currentYear - 1980 + 1 }, (_, i) => 1980 + i);
const date = val ? new Date(val) : new Date();
const y = date.getFullYear();
const m = date.getMonth() + 1;
const d = date.getDate();
this.selected = [
this.years.indexOf(y),
m - 1,
0 // 先占位,待 days 生成后再修正
];
this.updateDays(() => {
this.selected[2] = Math.min(d - 1, this.days.length - 1);
this.updateScrollPos();
});
},
close() {
this.visible = false;
},
confirm() {
const y = this.years[this.selected[0]];
const m = this.selected[1] + 1;
const d = this.selected[2] + 1;
const dateStr = `${y}-${String(m).padStart(2,'0')}-${String(d).padStart(2,'0')}`;
this.$emit("input", dateStr);
this.$emit("confirm", dateStr);
this.close();
},
// 点击选择项
select(col, idx) {
this.selected.splice(col, 1, idx);
if (col === 0 || col === 1) {
this.updateDays(() => {
if (this.selected[2] >= this.days.length) {
this.selected[2] = this.days.length - 1;
}
this.updateScrollPos();
});
} else {
this.updateScrollPos();
}
},
// 滚动事件处理(兼容小程序)
onScroll(col, e) {
if (!e.detail) return;
const top = e.detail.scrollTop;
if (this.scrollTimer) clearTimeout(this.scrollTimer);
this.scrollTimer = setTimeout(() => {
const idx = Math.round(top / this.itemH);
this.selected.splice(col, 1, idx);
if (col === 0 || col === 1) {
this.updateDays(() => {
if (this.selected[2] >= this.days.length) {
this.selected[2] = this.days.length - 1;
}
this.updateScrollPos();
});
} else {
this.updateScrollPos();
}
}, 120);
},
// 更新 scroll-top使选中项居中
updateScrollPos() {
this.scrollPos = [
this.selected[0] * this.itemH,
this.selected[1] * this.itemH,
this.selected[2] * this.itemH,
];
},
updateDays(callback) {
const year = this.years[this.selected[0]];
const month = this.selected[1] + 1;
const max = new Date(year, month, 0).getDate();
this.days = Array.from({ length: max }, (_, i) => i + 1);
this.$nextTick(() => {
if (callback) callback();
});
}
}
};
</script>
<style scoped>
.picker-overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.4);
display: flex;
justify-content: center;
align-items: flex-end;
z-index: 999;
}
.picker-container {
background: #fff;
width: 100%;
border-radius: 16px 16px 0 0;
}
.picker-header {
display: flex;
justify-content: space-between;
padding: 12px 20px;
font-size: 16px;
}
.cancel { color: #999; }
.confirm { color:#007aff; }
.title { font-weight:bold; }
.picker-columns {
display: flex;
height: 200px;
}
.column {
flex: 1;
height: 100%;
}
.item {
height: 40px;
line-height: 40px;
text-align: center;
color:#666;
}
.item.active {
color:#007aff;
font-weight:bold;
font-size:18px;
}
</style>

View File

@@ -0,0 +1,295 @@
<template>
<view class="house-gallery">
<!-- 图片轮播 -->
<u-swiper
:list="swiperList"
:current="current"
height="1200rpx"
>
<template #default="{ item, index }">
<view class="slide-item">
<image :src="item.url" mode="aspectFill" class="slide-img" />
<!-- VR 按钮 -->
<view v-if="item.type === 'vr'" class="vr-btn" @click.stop="openVR">
VR
</view>
<!-- 视频按钮 -->
<view v-if="item.type === 'video'" class="video-btn" @click.stop="playVideo">
</view>
<!-- 图片索引 -->
<view class="slide-index">{{ index + 1 }}/{{ swiperList.length }}</view>
</view>
</template>
</u-swiper>
<!-- 广告遮罩栏 -->
<view v-if="adVisible && adImage" class="ad-mask" @click="onAdClick">
<image :src="adImage" mode="aspectFill" class="ad-img" />
<view class="ad-close" @click.stop="adVisible = false">×</view>
</view>
<!-- 底部 Tab -->
<view class="gallery-tab">
<view
class="gallery-tab-item"
v-for="(item, i) in tabs"
:key="i"
:id="'tab-' + i"
:class="{ active: activeTab === i }"
@click="onTabClick(i)"
>
{{ item.label }}
<text v-if="item.count">({{ item.count }})</text>
</view>
<!-- 滑块动画 -->
<view
class="tab-line"
:style="{ width: lineStyle.width + 'px', left: lineStyle.left + 'px' }"
></view>
</view>
<!-- 视频弹窗 -->
<u-popup v-model="videoVisible" mode="center" border-radius="20">
<video
:src="videoUrl"
autoplay
controls
style="width: 600rpx; height: 400rpx;"
></video>
</u-popup>
</view>
</template>
<script>
export default {
name: "AssetGallery",
props: {
vrImage: String,
videoCover: String,
videoUrl: String,
insideImages: Array,
planImages: Array,
adImage: String
},
data() {
return {
current: 0,
videoVisible: false,
adVisible: true,
tabs: [],
indexMap: [],
lineStyle: { width: 0, left: 0 }
};
},
mounted() {
this.$nextTick(() => {
setTimeout(() => this.updateTabLine(), 50);
});
},
watch: {
activeTab() {
this.$nextTick(() => this.updateTabLine());
}
},
computed: {
swiperList() {
let list = [];
this.tabs = [];
this.indexMap = [];
let idx = 0;
if (this.vrImage) {
this.tabs.push({ key: "vr", label: "VR", count: 1 });
this.indexMap.push(idx);
list.push({ type: "vr", url: this.vrImage });
idx++;
}
if (this.videoCover) {
this.tabs.push({ key: "video", label: "视频", count: 1 });
this.indexMap.push(idx);
list.push({ type: "video", url: this.videoCover });
idx++;
}
if (this.insideImages?.length > 0) {
this.tabs.push({ key: "inside", label: "内部图", count: this.insideImages.length });
this.indexMap.push(idx);
list.push(...this.insideImages.map((img) => ({ type: "inside", img })));
idx += this.insideImages.length;
}
if (this.planImages?.length > 0) {
this.tabs.push({ key: "plan", label: "平面图", count: this.planImages.length });
this.indexMap.push(idx);
list.push(...this.planImages.map((url) => ({ type: "plan", url })));
idx += this.planImages.length;
}
return list;
},
activeTab() {
for (let i = 0; i < this.indexMap.length; i++) {
const start = this.indexMap[i];
const end = this.indexMap[i + 1] ?? 99999;
if (this.current >= start && this.current < end) return i;
}
return 0;
}
},
methods: {
onAdClick() {
this.$emit("ad-click");
},
onSwiperChange(e) {
this.current = e.detail.current;
},
onTabClick(i) {
this.current = this.indexMap[i];
},
openVR() {
this.$emit("open-vr");
},
playVideo() {
this.videoVisible = true;
},
updateTabLine() {
const index = this.activeTab;
if (this.tabs.length === 0) return;
const id = `#tab-${index}`;
uni.createSelectorQuery()
.in(this)
.select(id)
.boundingClientRect((rect) => {
if (!rect) return;
this.lineStyle = { width: rect.width, left: rect.left };
})
.exec();
}
}
};
</script>
<style scoped>
.house-gallery {
width: 100%;
position: relative;
}
/* 轮播 */
.slide-item {
width: 100%;
height: 1200rpx;
position: relative;
}
.slide-img {
width: 100%;
height: 100%;
border-radius: 0;
}
.vr-btn,
.video-btn {
position: absolute;
bottom: 40rpx;
right: 40rpx;
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background: rgba(0, 0, 0, 0.6);
color: #fff;
display: flex;
justify-content: center;
align-items: center;
font-size: 30rpx;
}
.video-btn {
font-size: 50rpx;
}
.slide-index {
position: absolute;
bottom: 20rpx;
right: 20rpx;
padding: 4rpx 8rpx;
background: rgba(0,0,0,0.5);
color: #fff;
font-size: 24rpx;
border-radius: 10rpx;
}
/* 广告遮罩 */
.ad-mask {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 120rpx;
background: rgba(0,0,0,0.35);
padding: 0 20rpx;
z-index: 9;
}
.ad-img {
width: 100%;
height: 100%;
border-radius: 12rpx;
}
.ad-close {
position: absolute;
right: 20rpx;
top: 20rpx;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background: rgba(0,0,0,0.55);
color: #fff;
text-align: center;
line-height: 40rpx;
font-size: 30rpx;
}
/* Tab bar */
.gallery-tab {
position: absolute;
bottom: 0;
width: 100%;
display: flex;
background: rgba(255,255,255,0.9);
border-top: 1rpx solid #eee;
height: 90rpx;
align-items: center;
justify-content: space-around;
z-index: 10;
}
.gallery-tab-item {
padding: 20rpx 0;
font-size: 28rpx;
color: #666;
}
.gallery-tab-item.active {
color: #2979ff;
font-weight: bold;
}
.tab-line {
position: absolute;
bottom: 0;
height: 4rpx;
background: #2979ff;
transition: all 0.25s ease;
}
</style>

View File

@@ -6,8 +6,8 @@
<view class="nav-content">
<!-- 左侧返回 -->
<view class="nav-left">
<u-icon name="arrow-left" size="44" color="#333" @click="onBack"></u-icon>
<u-icon name="home" size="44" color="#333" @click="goHome"></u-icon>
<u-icon name="arrow-left" size="44" color="#333" @click="onBack" v-if="showBack"></u-icon>
<u-icon name="home" size="44" color="#333" @click="goHome" v-if="showHome"></u-icon>
</view>
<!-- 中间标题 -->
@@ -51,6 +51,14 @@ export default {
type: Function,
default: null,
},
showBack: {
type: Boolean,
default: true // 左侧返回按钮默认显示
},
showHome: {
type: Boolean,
default: true // 回首页按钮默认显示
}
},
data() {
return {

View File

@@ -0,0 +1,139 @@
<template>
<view
class="index-navbar"
:style="{ height: navHeight + 'px' }"
>
<view class="nav-content" :style="{ paddingTop: statusBarHeight + 'px' }">
<!-- 左侧返回 -->
<view class="nav-left">
<u-icon name="arrow-left" size="44" color="#333" @click="onBack" v-if="showBack"></u-icon>
<u-icon name="home" size="44" color="#333" @click="goHome" v-if="showHome"></u-icon>
</view>
<!-- 中间标题 -->
<view class="nav-title">{{ title }}</view>
<!-- 右侧按钮位于胶囊左侧 -->
<view
class="nav-right"
:style="{ marginRight: menuRightGap + 'px' }"
>
<view
v-for="(btn, index) in rightButtons"
:key="index"
class="nav-btn"
@click="btn.onClick && btn.onClick()"
>
<u-icon
:name="btn.icon"
:color="btn.color || '#333'"
:size="btn.size || 40"
></u-icon>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'indexNavbar',
props: {
title: {
type: String,
default: '',
},
rightButtons: {
type: Array,
default: () => [],
},
back: {
type: Function,
default: null,
},
showBack: {
type: Boolean,
default: false // 左侧返回按钮默认显示
},
showHome: {
type: Boolean,
default: false // 回首页按钮默认显示
}
},
data() {
return {
statusBarHeight: 0,
navHeight: 0,
menuRightGap: 0, // 胶囊按钮与右侧边距
navContentHeight:0
};
},
mounted() {
const systemInfo = uni.getSystemInfoSync();
const menuButton = wx.getMenuButtonBoundingClientRect ? wx.getMenuButtonBoundingClientRect() : null;
if (menuButton) {
this.statusBarHeight = systemInfo.statusBarHeight;
this.navContentHeight = menuButton.height; // 只算胶囊按钮区域高度
this.navHeight = this.statusBarHeight + this.navContentHeight; // 总高度
this.menuRightGap = systemInfo.screenWidth - menuButton.right + 8;
} else {
this.statusBarHeight = systemInfo.statusBarHeight;
this.navContentHeight = 44; // 默认内容高度
this.navHeight = this.statusBarHeight + this.navContentHeight;
this.menuRightGap = 16;
}
},
methods: {
onBack() {
if (this.back) {
this.back();
} else {
uni.navigateBack();
}
},
goHome(){
uni.reLaunch({
url:'../index/index'
})
}
},
};
</script>
<style lang="scss" scoped>
.index-navbar {
background-color: none;
.nav-content {
display: flex;
align-items: center;
justify-content: space-between;
height: 100%;
padding: 0 24rpx;
}
.nav-left {
width: 50rpx;
display: flex;
align-items: center;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.nav-right {
display: flex;
align-items: center;
.nav-btn {
margin-left: 20rpx;
}
}
}
</style>

View File

@@ -0,0 +1,99 @@
<template>
<view class="search-bar" :style="{ backgroundColor: backgroundColor,border:border}">
<view v-if="showChoseCity" class="location-chose" @click="$emit('chooseCity')" :style="{fontSize:fontSize,color:fontColor}">
{{ city || '选择' }}
<u-icon name="arrow-down-fill" class="choseIcon" size="15" :color="fontColor" :style="{marginLeft:'2rpx'}" />
</view>
<view class="search-input" @click="$emit('search')">
<u-icon v-if="searchIconShowLeft" name="search" size="40" :color="fontColor" class="search-icon-left" />
<text class="placeholder" :style="{fontSize:fontSize,color:fontColor}">{{ placeholder }}</text>
<u-icon v-if="!searchIconShowLeft" name="search" size="40" :color="fontColor" class="search-icon-right" />
</view>
</view>
</template>
<script>
export default {
name: 'SearchBar',
props: {
city: {
type: String,
default: '宜昌市'
},
showChoseCity: {
type: Boolean,
default: false
},
fontSize: {
type: String,
default: '30rpx'
},
searchIconShowLeft: {
type: Boolean,
default: true
},
fontColor:{
type: String,
default: '#FFFFFF'
},
backgroundColor: {
type: String,
default: 'rgba(255,255,255,0.6)' ,
},
border: {
type: String,
default: 'border: 1px solid #FFFFFF' ,
},
placeholder: {
type: String,
default: '搜索小区/城市/门店'
}
}
}
</script>
<style lang="scss" scoped>
.choseIcon {
margin-left: 5rpx;
}
.search-bar {
margin-top: 10rpx;
display: flex;
align-items: center;
padding: 6rpx 16rpx;
border-radius: 10rpx;
width: 100%;
}
.location-chose {
font-family: Noto Sans S Chinese;
font-weight: 400;
display: flex;
align-items: center;
width: 25%;
}
.search-input {
flex: 1;
display: flex;
align-items: center;
position: relative;
height: 83.5rpx;
padding: 25rpx 30rpx;
}
.placeholder {
font-family: Noto Sans S Chinese;
font-weight: 400;
flex: 1;
}
.search-icon-right {
}
.search-icon-left {
margin-right: 5%;
}
</style>

View File

@@ -26,7 +26,6 @@ Vue.mixin(mpShare)
Vue.component('customNavbar', customNavbar)
Vue.component('AuthLogin', AuthLogin)
Vue.component('PhoneSelect', PhoneSelect)
const app = new Vue({
store,
...App
@@ -34,8 +33,9 @@ const app = new Vue({
// http拦截器将此部分放在new Vue()和app.$mount()之间才能App.vue中正常使用
import httpInterceptor from '@/common/http.interceptor.js';
Vue.use(httpInterceptor, app);
// 挂载自定义 http 封装
Vue.use(httpInterceptor, app);
// 封装自定义提示框
import $mytip from 'common/utils/tip.js'
Vue.prototype.$mytip = $mytip

View File

@@ -1,13 +1,13 @@
// @ts-nocheck
export {};
; declare module 'vue' {
declare module 'vue' {
export interface GlobalComponents { }
export interface GlobalDirectives { }
}
; declare global {
declare global {
var __VLS_PROPS_FALLBACK: Record<string, unknown>;
const __VLS_directiveBindingRestFields: { instance: null, oldValue: null, modifiers: any, dir: any };
const __VLS_unref: typeof import('vue').unref;
const __VLS_placeholder: any;
const __VLS_intrinsics: globalThis.JSX.IntrinsicElements;
@@ -17,7 +17,7 @@ export {};
type __VLS_IsAny<T> = 0 extends 1 & T ? true : false;
type __VLS_PickNotAny<A, B> = __VLS_IsAny<A> extends true ? B : A;
type __VLS_SpreadMerge<A, B> = Omit<A, keyof B> & B;
type __VLS_WithComponent<N0 extends string, LocalComponents, Self, N1 extends string, N2 extends string, N3 extends string> =
type __VLS_WithComponent<N0 extends string, LocalComponents, Self, N1 extends string, N2 extends string = N1, N3 extends string = N1> =
N1 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N1] } :
N2 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N2] } :
N3 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N3] } :
@@ -39,7 +39,7 @@ export {};
attrs?: any;
slots?: T extends { $slots: infer Slots } ? Slots : Record<string, any>;
emit?: T extends { $emit: infer Emit } ? Emit : {};
props?: (T extends { $props: infer Props } ? Props : {}) & Record<string, unknown>;
props?: typeof props;
expose?: (exposed: T) => void;
};
};
@@ -99,12 +99,7 @@ export {};
type __VLS_ResolveDirectives<T> = {
[K in keyof T & string as `v${Capitalize<K>}`]: T[K];
};
type __VLS_PrettifyGlobal<T> = { [K in keyof T as K]: T[K]; } & {};
type __VLS_WithDefaultsGlobal<P, D> = {
[K in keyof P as K extends keyof D ? K : never]-?: P[K];
} & {
[K in keyof P as K extends keyof D ? never : K]: P[K];
};
type __VLS_PrettifyGlobal<T> = (T extends any ? { [K in keyof T]: T[K]; } : { [K in keyof T as K]: T[K]; }) & {};
type __VLS_UseTemplateRef<T> = Readonly<import('vue').ShallowRef<T | null>>;
type __VLS_ProxyRefs<T> = import('vue').ShallowUnwrapRef<T>;

View File

@@ -2,13 +2,14 @@
export {};
; declare global {
var __VLS_PROPS_FALLBACK: Record<string, unknown>;
const __VLS_directiveBindingRestFields: { instance: null, oldValue: null, modifiers: any, dir: any };
const __VLS_unref: typeof import('vue').unref;
const __VLS_placeholder: any;
const __VLS_intrinsics: import('vue/jsx-runtime').JSX.IntrinsicElements;
type __VLS_NativeElements = __VLS_SpreadMerge<SVGElementTagNameMap, HTMLElementTagNameMap>;
type __VLS_IntrinsicElements = import('vue/jsx-runtime').JSX.IntrinsicElements;
type __VLS_Element = import('vue/jsx-runtime').JSX.Element;
type __VLS_Elements = __VLS_SpreadMerge<SVGElementTagNameMap, HTMLElementTagNameMap>;
type __VLS_GlobalComponents = import('vue').GlobalComponents;
type __VLS_GlobalDirectives = import('vue').GlobalDirectives;
type __VLS_IsAny<T> = 0 extends 1 & T ? true : false;
@@ -31,12 +32,12 @@ export {};
? K extends { __ctx?: { props?: infer P } } ? NonNullable<P> : never
: T extends (props: infer P, ...args: any) => any ? P
: {};
type __VLS_FunctionalComponent<T> = (props: (T extends { $props: infer Props } ? Props : {}) & Record<string, unknown>, ctx?: any) => __VLS_Element & {
type __VLS_FunctionalComponent<T> = (props: (T extends { $props: infer Props } ? Props : {}) & Record<string, unknown>, ctx?: any) => import('vue/jsx-runtime').JSX.Element & {
__ctx?: {
attrs?: any;
slots?: T extends { $slots: infer Slots } ? Slots : Record<string, any>;
emit?: T extends { $emit: infer Emit } ? Emit : {};
props?: (T extends { $props: infer Props } ? Props : {}) & Record<string, unknown>;
props?: typeof props;
expose?: (exposed: T) => void;
};
};

View File

@@ -6,8 +6,8 @@
{
"path": "pages/index/index",
"style": {
"navigationStyle": "default" ,// 隐藏系统导航栏
"navigationBarTitleText": "首页",
"navigationStyle": "custom" ,// 隐藏系统导航栏
"navigationBarTitleText": "启辰资产",
"enablePullDownRefresh": true,
"app-plus": {
"pullToRefresh": {
@@ -166,7 +166,7 @@
},
{
"path" : "pages/content/content",
"path" : "pages/notice/noticeDetail",
"style": {
"navigationStyle": "custom" ,// 隐藏系统导航栏
"navigationBarTitleText": "资讯"
@@ -226,9 +226,9 @@
"path" : "pages/search/searchList",
"style" :
{
"navigationBarTitleText": "房源列表",
"navigationBarTitleText": "资产列表",
"enablePullDownRefresh": true,
// "navigationStyle": "custom" ,// 隐藏系统导航栏
"navigationStyle": "custom" ,// 隐藏系统导航栏
"app-plus": {
"pullToRefresh": {
"support": true,
@@ -354,7 +354,7 @@
"navigationBarTitleText": "我的预约",
"enablePullDownRefresh": false
}
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",

0
pages/auth/company.vue Normal file
View File

0
pages/auth/person.vue Normal file
View File

View File

@@ -4,10 +4,9 @@
<view class="user-header u-flex u-p-30">
<u-avatar :src="avatar" size="100"></u-avatar>
<view class="user-info u-m-l-20">
<!-- <view class="u-font-18 u-m-b-10">{{ userName }}</view> -->
<view class="u-font-18 u-m-b-10">吴康桥</view>
<view class="user-tag">用户类型: 个人</view>
<view class="user-tag">系统认证已认证</view>
<view class="u-font-18 u-m-b-10">{{vuex_user.nickName}}</view>
<view class="user-tag">用户类型: {{userType === '0' ? '个人' : '组织'}}</view>
<view class="user-tag">系统认证{{oaAuth === '1' ? '已认证' : '未认证'}}</view>
</view>
<view class="msg-setting">
<view class="u-relative u-m-r-20" @click="toMessage()">
@@ -35,301 +34,523 @@
</u-row>
<view class="vip-upgrade-btn" @click="upgrade">超值优惠 升级立享</view>
</view> -->
<!-- 功能网格 -->
<!-- <view class="function-grid u-m-t-30">
<u-grid :col="4" :border="false">
<u-grid-item v-for="(item,index) in gridList" :key="index" @click="clickNav(item.url)">
<u-icon :name="item.icon" size="65" color="#333"></u-icon>
<text class="u-font-14 u-m-t-10" >{{ item.name }}</text>
</u-grid-item>
</u-grid>
</view> -->
<!-- <view class="function-grid">
<swiper
:indicator-dots="true"
class="swiper"
>
<swiper-item>
<u-grid :border="false">
<u-grid-item
:customStyle="{width:220+'rpx',height:220+'rpx'}"
v-for="(item, index) in gridList"
:index="index"
:key="index"
>
<u-icon
:customStyle="{paddingTop:20+'rpx'}"
:name="item.icon"
size="46" color="#333"
></u-icon>
<text class="u-font-14 u-m-t-10">{{item.name}}</text>
</u-grid-item>
</u-grid>
</swiper-item>
<swiper-item>
<u-grid :border="false">
<u-grid-item
:customStyle="{width:220+'rpx',height:220+'rpx'}"
v-for="(item, index) in gridList"
:index="index + 9"
:key="index"
>
<u-icon
:customStyle="{paddingTop:20+'rpx'}"
:name="item.icon"
size="46" color="#333"
></u-icon>
<text class="u-font-14 u-m-t-10">{{item.name}}</text>
</u-grid-item>
</u-grid>
</swiper-item>
<swiper-item>
<u-grid :border="false">
<u-grid-item
:customStyle="{width:220+'rpx',height:220+'rpx'}"
v-for="(item, index) in gridList"
:index="index + 18"
:key="index"
>
<u-icon
:customStyle="{paddingTop:20+'rpx'}"
:name="item.icon"
size="46" color="#333"
></u-icon>
<text class="u-font-14 u-m-t-10">{{item.name}}</text>
</u-grid-item>
</u-grid>
</swiper-item>
</swiper>
</view> -->
<view class="center-nav">
<u-row>
<u-col span="3" text-align="center" v-for="(item,index) in gridList" :key="index">
<view v-if="item.name=='问题反馈'">
<!-- 调用微信反馈功能 -->
<button type="default" open-type="feedback" class="clearBtn" hover-class="none" style="background-color: #FFFFFF;">
<u-icon :name="item.icon" color="#909399" size="50"></u-icon>
<view class="tabName" style="padding-top: 15rpx;">{{item.name}}</view>
</button>
</view>
<view @click="clickNav(item.url)" v-else>
<image :src="item.icon" style="width: 80rpx;height: 80rpx;" mode="widthFix"></image>
<view class="tabName">{{item.name}}</view>
</view>
</u-col>
</u-row>
</view>
<!-- 分隔条广告 -->
<!-- <view class="ad-banner u-m-t-20">
<image src="/static/ad-banner.png" mode="widthFix" style="width: 100%; border-radius: 12rpx;"></image>
</view> -->
<!-- 其他功能列表 -->
<view class="other-list u-m-t-20 cell-group">
<view class="user-content">
<!-- 功能网格 -->
<!-- <view class="function-grid u-m-t-30">
<u-grid :col="4" :border="false">
<u-grid-item v-for="(item,index) in gridList" :key="index" @click="clickNav(item.url)">
<u-icon :name="item.icon" size="65" color="#333"></u-icon>
<text class="u-font-14 u-m-t-10" >{{ item.name }}</text>
</u-grid-item>
</u-grid>
</view> -->
<!-- <view class="function-grid">
<swiper
:indicator-dots="true"
class="swiper"
>
<swiper-item>
<u-grid :border="false">
<u-grid-item
:customStyle="{width:220+'rpx',height:220+'rpx'}"
v-for="(item, index) in gridList"
:index="index"
:key="index"
>
<u-icon
:customStyle="{paddingTop:20+'rpx'}"
:name="item.icon"
size="46" color="#333"
></u-icon>
<text class="u-font-14 u-m-t-10">{{item.name}}</text>
</u-grid-item>
</u-grid>
</swiper-item>
<swiper-item>
<u-grid :border="false">
<u-grid-item
:customStyle="{width:220+'rpx',height:220+'rpx'}"
v-for="(item, index) in gridList"
:index="index + 9"
:key="index"
>
<u-icon
:customStyle="{paddingTop:20+'rpx'}"
:name="item.icon"
size="46" color="#333"
></u-icon>
<text class="u-font-14 u-m-t-10">{{item.name}}</text>
</u-grid-item>
</u-grid>
</swiper-item>
<swiper-item>
<u-grid :border="false">
<u-grid-item
:customStyle="{width:220+'rpx',height:220+'rpx'}"
v-for="(item, index) in gridList"
:index="index + 18"
:key="index"
>
<u-icon
:customStyle="{paddingTop:20+'rpx'}"
:name="item.icon"
size="46" color="#333"
></u-icon>
<text class="u-font-14 u-m-t-10">{{item.name}}</text>
</u-grid-item>
</u-grid>
</swiper-item>
</swiper>
</view> -->
<view class="center-nav">
<u-row>
<u-col span="3" text-align="center" v-for="(item,index) in gridList" :key="index">
<view v-if="item.name=='问题反馈'">
<!-- 调用微信反馈功能 -->
<button type="default" open-type="feedback" class="clearBtn" hover-class="none"
style="background-color: #FFFFFF;">
<u-icon :name="item.icon" color="#909399" size="50"></u-icon>
<view class="tabName" style="padding-top: 15rpx;">{{item.name}}</view>
</button>
</view>
<view @click="clickNav(item.url)" v-else>
<image :src="item.icon" style="width: 80rpx;height: 80rpx;" mode="widthFix"></image>
<view class="tabName">{{item.name}}</view>
</view>
</u-col>
</u-row>
</view>
<!-- 分隔条广告 -->
<!-- <view class="ad-banner u-m-t-20">
<image src="/static/ad-banner.png" mode="widthFix" style="width: 100%; border-radius: 12rpx;"></image>
</view> -->
<UCellItemPlus
v-for="(item, index) in cellList"
:key="index"
:title="item.title"
:icon="item.icon"
arrow
@click="clickNav(item.url)"
/>
<!-- 其他功能列表 -->
<view class="other-list u-m-t-20 cell-group">
<!-- 功能网格 -->
<view class="function-grid">
<swiper
:indicator-dots="true"
class="swiper"
>
<swiper-item>
<u-grid :border="false">
<u-grid-item
:customStyle="{width:220+'rpx',height:220+'rpx'}"
v-for="(item, index) in cellList"
:index="index"
:key="index"
v-if="index < 9"
@click="clickNav(item.url)"
>
<!-- <u-icon
:customStyle="{paddingTop:20+'rpx'}"
:name="item.icon"
size="46" color="#333"
></u-icon> -->
<image :src="item.icon" style="width: 50px;height: 50rpx;" mode="widthFix"></image>
<text class="u-font-14 u-m-t-10">{{item.name}}</text>
</u-grid-item>
</u-grid>
</swiper-item>
<swiper-item>
<u-grid :border="false">
<u-grid-item
:customStyle="{width:220+'rpx',height:220+'rpx'}"
v-for="(item, index) in cellList"
:index="index + 9"
v-if="index >= 9 && index < 18 "
:key="index"
@click="clickNav(item.url)"
>
<!-- <u-icon
:customStyle="{paddingTop:20+'rpx'}"
:name="item.icon"
size="46" color="#333"
></u-icon> -->
<image :src="item.icon" style="width: 50px;height: 50rpx;" mode="widthFix"></image>
<text class="u-font-14 u-m-t-10">{{item.name}}</text>
</u-grid-item>
</u-grid>
</swiper-item>
<swiper-item>
<u-grid :border="false">
<u-grid-item
:customStyle="{width:220+'rpx',height:220+'rpx'}"
v-for="(item, index) in cellList"
:index="index + 18"
:key="index"
@click="clickNav(item.url)"
>
<!-- <u-icon
:customStyle="{paddingTop:20+'rpx'}"
:name="item.icon"
size="46" color="#333"
></u-icon> -->
<image :src="item.icon" style="width: 50px;height: 50rpx;" mode="widthFix"></image>
<text class="u-font-14 u-m-t-10">{{item.name}}</text>
</u-grid-item>
</u-grid>
</swiper-item>
</swiper>
</view>
</view>
<!-- 弹窗 -->
<!-- <u-popup v-model="showGetProfile" mode="center" border-radius="20">
<view class="popup-content">
<view class="title">获取头像</view>
<view class="desc">获取微信头像与昵称用于展示</view>
<button class="auth-btn" @click="getWxProfile">
授权获取头像
</button>
</view>
</u-popup> -->
<!-- <UCellItemPlus v-for="(item, index) in cellList" :key="index" :title="item.name" :icon="item.icon" arrow
@click="clickNav(item.url)" /> -->
</view>
</view>
</template>
<script>
import UCellItemPlus from "../../components/ucellitem/UCellItemPlus.vue";
import config from "@/common/config.js";
import UCellItemPlus from "../../components/ucellitem/UCellItemPlus.vue";
import config from "@/common/config.js";
export default {
components: { UCellItemPlus },
export default {
components: {
UCellItemPlus
},
data() {
const life = uni.getStorageSync('lifeData') || {}
const user = (life.vuex_user && life.vuex_user.user) || {}
data() {
const life = uni.getStorageSync('lifeData') || {}
const user = (life.vuex_user && life.vuex_user.user) || {}
return {
user, // user 信息放这里即可
show: true,
gridList: [
{name:"我的合同",icon:"../../static/icon/我的合同.png",url:"pages/center/history"},
{name:"我的账单",icon:"../../static/icon/我的账单.png",url:"pages/bill/bill"},
{name:"我的待付",icon:"../../static/icon/我的待付.png",url:"pages/unpaid/unpaid"},
{name:"水电缴费",icon:"../../static/icon/水电缴费.png",url:"pages/wae/wae"}
],
cellList: [
{title:"我的租赁资产",icon:"star",url:"pages/center/myLease"},
{title:"我的预约",icon:"star",url:"pages/reserve/reserveRecords"},
{title:"缴费记录",icon:"star",url:"pages/bill/payHistory"},
{title:"水电费明细",icon:"star",url:"pages/wae/waeRecords"}
]
}
},
return {
user, // user 信息放这里即可
show: true,
showGetProfile: false,
userType:'',
oaAuth:'',
gridList: [{
name: "我的合同",
icon: "/static/icon/sign.png",
url: "pages/center/history"
},
{
name: "我的账单",
icon: "../../static/icon/账单.png",
url: "pages/bill/bill"
},
{
name: "我的待付",
icon: "../../static/icon/待付款.png",
url: "pages/unpaid/unpaid"
},
{
name: "水电缴费",
icon: "../../static/icon/水电费.png",
url: "pages/wae/wae"
}
],
cellList: [{
name: "我的租赁资产",
icon: "/static/icon/住房C.png",
url: "pages/center/myLease"
},
{
name: "我的预约",
icon: "/static/icon/预约.png",
url: "pages/reserve/reserveRecords"
},
{
name: "缴费记录",
icon: "/static/icon/账本.png",
url: "pages/bill/payHistory"
},
{
name: "水电费明细",
icon: "/static/icon/书签.png",
url: "pages/wae/waeRecords"
},
{
name: "留言板",
icon: "/static/icon/留言纸.png",
url: "pages/wae/waeRecords"
}
]
}
},
computed: {
avatar() {
const life = uni.getStorageSync('lifeData') || {}
const user = (life.vuex_user && life.vuex_user.user) || {}
const raw = user.avatar || ""
computed: {
avatar() {
const life = uni.getStorageSync('lifeData') || {}
const user = life.vuex_user || {}
const raw = user.avatarUrl
if (!raw) return "/static/images/default-avatar.png"
if (!raw) return raw
return raw.includes(config.staticUrl)
? raw
: config.staticUrl + raw
}
},
return raw.includes(config.staticUrl) ?
raw :
config.staticUrl + raw
}
},
onLoad() {
uni.$on('updateAvatar', () => {
this.updateAvatar();
})
},
onLoad() {
uni.$on('updateAvatar', () => {
this.updateAvatar();
})
},
onUnload() {
uni.$off('updateAvatar')
},
onUnload() {
uni.$off('updateAvatar')
},
onShow() {
this.checkToken();
},
onShow() {
this.checkToken();
},
methods: {
logout() {
this.$u.vuex('vuex_token', '');
this.$u.vuex('vuex_user', {});
this.$u.route('/pages/login/login')
},
methods: {
logout() {
this.$u.vuex('vuex_token', '');
this.$u.vuex('vuex_user', {});
this.$u.route('/pages/login/login')
},
toMessage() {
this.$u.route('/pages/message/message')
},
toMessage() {
this.$u.route('/pages/message/message')
},
toSetting() {
this.$u.route('/pages/profile/setting')
},
toSetting() {
this.$u.route('/pages/profile/setting')
},
callPhoneNumber() {
uni.makePhoneCall({ phoneNumber: "18720989281" });
},
callPhoneNumber() {
uni.makePhoneCall({
phoneNumber: "18720989281"
});
},
aboutMe() {
this.$u.route('/pages/profile/aboutMe')
},
aboutMe() {
this.$u.route('/pages/profile/aboutMe')
},
toMyLease() {
this.$u.route('/pages/center/myLease')
},
toMyLease() {
this.$u.route('/pages/center/myLease')
},
checkToken() {
let lifeData = uni.getStorageSync('lifeData');
let token = lifeData.vuex_token
if (!token || token === null || token === undefined ||
(typeof token === 'object' && Object.keys(token).length === 0)) {
console.log("登录态不存在")
return uni.reLaunch({
url: '../login/login'
})
}
let url = "/login/checkExpiration";
this.$u.get(url, {}, {'WT': token}).then(obj => {
if (obj.data) {
uni.reLaunch({
url: '../login/login'
});
}
});
},
checkToken() {
let lifeData = uni.getStorageSync('lifeData') || {};
let token = lifeData.vuex_token
clickNav(url) {
if (url) {
this.$u.route(url);
} else {
this.$mytip.toast('敬请期待')
}
},
if (!token) {
console.log("登录态不存在")
return uni.reLaunch({ url:'../login/login' })
}
updateAvatar() {
// 触发 computed 自动更新
this.$forceUpdate()
},
// 进入个人中心时判断是否需要补充资料
checkNeedProfile() {
let life = uni.getStorageSync('lifeData') || {}
let user = life.vuex_user
let token = life.vuex_token
if (!token) return; // 未登录不弹窗
// 没有头像或昵称才弹窗
if (!user || !user.avatarUrl || !user.nickName) {
this.showGetProfile = true;
}
},
getWxProfile() {
uni.getUserProfile({
desc: "用于完善个人资料展示",
success: res => {
const userInfo = res.userInfo;
// 存储
this.$u.vuex('vuex_user', userInfo);
let url = "/api/profile/isExpiration";
this.$u.get(url,{ token }).then(obj => {
if(obj.data){
return uni.reLaunch({ url:'../login/login' })
}
});
},
// 也可同步调用后端更新头像昵称
// this.$u.post('/user/updateWxInfo', userInfo);
clickNav(url){
if(url){
this.$u.route(url);
}else{
this.$mytip.toast('敬请期待')
}
},
updateAvatar() {
// 触发 computed 自动更新
this.$forceUpdate()
}
}
}
this.showGetProfile = false;
},
fail: () => {
uni.showToast({
title: "授权失败",
icon: "none"
});
}
});
},
getUserOtherInfo() {
let life = uni.getStorageSync('lifeData') || {}
let token = life.vuex_token
let url = "/login/userOtherInfo";
this.$u.get(url, {}, {'WT': token}).then(obj => {
this.userType = obj.data.userType;
this.oaAuth = obj.data.oaAuth;
});
}
}
}
</script>
<style lang="scss" scoped>
.swiper {
height: 400rpx;
}
.user-center {
background-color: #f7f8fa;
min-height: 100vh;
padding: 15rpx;
}
.center-nav{
background-color: #FFFFFF;
margin-top: 30rpx;
padding: 30rpx 0;
border-radius: 8px;
.tabName{
color: #606266;
font-size: 26rpx;
padding-top: 10rpx;
.swiper {
height: 700rpx;
}
}
.cell-group {
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
}
.cell-group > view:not(:last-child) {
border-bottom: 1rpx solid #f0f0f0;
}
/* 用户信息卡 */
.user-header {
align-items: center;
margin-top: 200rpx;
.user-info {
.user-tag {
background: #f6f6f6;
color: #ff8c00;
padding: 8rpx 12rpx;
border-radius: 8rpx;
font-size: 24rpx;
width: fit-content;
.user-center {
background-color: #f7f8fa;
min-height: 100vh;
}
.user-content{
padding: 0 20rpx 20rpx 20rpx;
position: relative; /* 相对定位不会脱流,仅创建一个 z-index 环境 */
z-index: 1;
margin: 0 20rpx;
margin-top: 12rpx; /* 视觉上插入到延伸背景中,调节数值以适配 */
}
.center-nav {
background-color: #FFFFFF;
padding: 30rpx 0;
border-radius: 8px;
.tabName {
color: black;
font-size: 26rpx;
padding-top: 10rpx;
}
}
.msg-setting {
display: flex;
position: absolute;
right: 85rpx;
}
}
.vip-card {
margin: 20rpx;
padding: 30rpx;
background: linear-gradient(135deg, #1f1f1f 0%, #2a2a2a 100%);
border-radius: 20rpx;
color: #fff;
.vip-title {
font-size: 30rpx;
font-weight: bold;
color: #f8e28e;
}
.vip-upgrade-btn {
margin-top: 30rpx;
background: linear-gradient(to right, #ffb347, #ffcc33);
color: #fff;
text-align: center;
padding: 20rpx 0;
border-radius: 40rpx;
font-weight: bold;
}
}
.function-grid {
margin: 0 20rpx;
padding: 20rpx 0;
}
.cell-group {
background-color: #fff;
border-radius: 20rpx;
overflow: hidden;
}
.cell-group>view:not(:last-child) {
border-bottom: 1rpx solid #f0f0f0;
}
/* 用户信息卡 */
.user-header {
align-items: center;
position: relative;
padding-top: 200rpx !important;
padding-bottom: 80rpx !important;
background: linear-gradient(to right, #89CFF0, #5AA4D1);
overflow: visible; /* 必须,否则延伸区域会被裁掉 */
.user-info {
.user-tag {
color: black;
padding: 8rpx 12rpx;
border-radius: 8rpx;
font-size: 24rpx;
width: fit-content;
}
}
.msg-setting {
display: flex;
position: absolute;
right: 85rpx;
}
}
.user-header::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: -80rpx;
height: 80rpx;
background: inherit;
border-bottom-left-radius: 40rpx;
border-bottom-right-radius: 40rpx;
/* 把伪元素放在 header 背景后面 */
z-index: 0;
}
.vip-card {
margin: 20rpx;
padding: 30rpx;
background: linear-gradient(135deg, #1f1f1f 0%, #2a2a2a 100%);
border-radius: 20rpx;
color: #fff;
.vip-title {
font-size: 30rpx;
font-weight: bold;
color: #f8e28e;
}
.vip-upgrade-btn {
margin-top: 30rpx;
background: linear-gradient(to right, #ffb347, #ffcc33);
color: #fff;
text-align: center;
padding: 20rpx 0;
border-radius: 40rpx;
font-weight: bold;
}
}
.function-grid {
margin: 0 20rpx;
padding: 20rpx 0;
}
.popup-content {
padding: 40rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.auth-btn {
width: 80%;
height: 80rpx;
border-radius: 40rpx;
background: linear-gradient(90deg, #4CAF50, #07c160);
color: white;
line-height: 80rpx;
text-align: center;
}
.title {
font-size: 34rpx;
font-weight: bold;
margin-bottom: 20rpx;
}
.desc {
font-size: 26rpx;
color: #666;
margin-bottom: 40rpx;
}
</style>

View File

@@ -1,264 +1,336 @@
<template>
<view class="contract-page">
<!-- 顶部导航栏 -->
<customNavbar title="我的合同"/>
<!-- 顶部筛选栏 -->
<u-tabs
:list="tabList"
:current="currentTab"
@change="onTabChange"
lineColor="#2979ff"
activeColor="#2979ff"
></u-tabs>
<view class="contract-page">
<!-- 顶部导航栏 -->
<customNavbar title="我的合同" />
<!-- 合同列表 -->
<scroll-view
scroll-y
class="scroll-content"
@scrolltolower="loadMore"
:refresher-enabled="true"
:refresher-triggered="isRefreshing"
@refresherrefresh="refresh"
>
<view v-if="contracts.length > 0" class="contract-list">
<view
class="contract-card"
v-for="item in contracts"
:key="item.id"
@click="goDetail(item)"
>
<!-- 左侧封面图 -->
<image
class="contract-cover"
:src="item.coverUrl || defaultCover"
mode="aspectFill"
/>
<!-- Tabs -->
<view class="tab-wrapper">
<u-tabs :list="tabList" :current="currentTab" @change="onTabChange" lineColor="#2979ff"
activeColor="#2979ff" itemStyle="padding: 0 30rpx;"></u-tabs>
</view>
<!-- 右侧信息 -->
<view class="contract-info">
<view class="top-row">
<text class="asset-name">{{ item.assetName }}</text>
<u-icon name="arrow-right" size="28" color="#ccc" />
</view>
<view class="asset-room">{{ item.assetRoom || '—' }}</view>
<view class="date-range">{{ item.startDate }} ~ {{ item.endDate }}</view>
<DateFilter
:start="startDate"
:end="endDate"
@update:start="startDate = $event"
@update:end="endDate = $event"
@change="onDateFilterChange"
/>
<view :class="['status-tag', item.status]">
{{ getStatusText(item.status) }}
</view>
</view>
</view>
<!-- 合同列表 -->
<scroll-view scroll-y class="scroll-content" @scrolltolower="loadMore" :refresher-enabled="true"
:refresher-triggered="isRefreshing" @refresherrefresh="refresh">
<view v-if="contracts.length > 0" class="contract-list">
<view class="contract-card" v-for="item in contracts" :key="item.id" @click="goDetail(item)">
<image class="contract-cover" :src="item.coverUrl || defaultCover" mode="aspectFill" />
<view class="contract-info">
<view class="top-row">
<text class="asset-name">{{ item.assetName }}</text>
<u-icon name="arrow-right" size="28" color="#ccc" />
</view>
<view class="asset-room">{{ item.assetRoom || "—" }}</view>
<view class="date-range">{{ item.startDate }} ~ {{ item.endDate }}</view>
<view :class="['status-tag', item.status]">
{{ getStatusText(item.status) }}
</view>
</view>
</view>
<u-loadmore :status="loadStatus" />
</view>
<view v-else class="empty">
<u-empty mode="list" text="暂无合同记录" />
</view>
</scroll-view>
<u-loadmore :status="loadStatus" />
</view>
<view v-else class="empty">
<u-empty mode="list" text="暂无合同记录" />
</view>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
tabList: [
{ name: '全部', value: 'all' },
{ name: '待签署', value: 'pending' },
{ name: '已签署', value: 'signed' },
{ name: '已过期', value: 'expired' }
],
currentTab: 0,
statusFilter: 'all',
import DateFilter from '../../components/DatePicker/DateFilter.vue';
export default {
components: { DateFilter },
data() {
return {
tabList: [{
name: "全部",
value: "all"
},
{
name: "待签署",
value: "pending"
},
{
name: "已签署",
value: "signed"
},
{
name: "已过期",
value: "expired"
},
],
contracts: [],
pageNo: 1,
pageSize: 10,
loadStatus: 'loadmore',
isRefreshing: false,
defaultCover: '/static/img/index/swiper/swiper3.jpg' // 可换成你的默认封面
};
},
onLoad() {
this.loadContracts();
},
methods: {
onTabChange(index) {
this.currentTab = index;
this.statusFilter = this.tabList[index].value;
this.pageNo = 1;
this.contracts = [];
this.loadContracts();
},
currentTab: 0,
statusFilter: "all",
loadContracts() {
this.loadStatus = 'loading';
setTimeout(() => {
const allData = [
{
id: 1,
assetName: '金辉大厦',
assetRoom: '502',
coverUrl: '/static/img/index/swiper/swiper3.jpg',
status: 'pending',
startDate: '2025-01-01',
endDate: '2025-12-31'
},
{
id: 2,
assetName: '东方广场',
assetRoom: '',
coverUrl: '/static/img/index/swiper/swiper3.jpg',
status: 'signed',
startDate: '2024-03-01',
endDate: '2025-03-01'
},
{
id: 3,
assetName: '海景公寓',
assetRoom: 'A-1201',
coverUrl: '/static/img/index/swiper/swiper3.jpg',
status: 'expired',
startDate: '2023-01-01',
endDate: '2024-01-01'
}
];
// --- 时间筛选 ---
startDate: "",
endDate: "",
// 可选:限制日期范围
minDate: "2000-01-01",
maxDate: "2099-12-31",
contracts: [],
pageNo: 1,
pageSize: 10,
total: 0,
loadStatus: "loadmore",
isRefreshing: false,
defaultCover: "/static/img/index/swiper/swiper3.jpg",
};
},
const filtered =
this.statusFilter === 'all'
? allData
: allData.filter(i => i.status === this.statusFilter);
onLoad() {
this.loadContracts(true);
},
this.contracts = filtered;
this.loadStatus = 'nomore';
this.isRefreshing = false;
}, 400);
},
methods: {
onTabChange(index) {
this.currentTab = index;
this.statusFilter = this.tabList[index].value;
this.resetAndLoad();
},
onDateFilterChange({ start, end }) {
this.resetAndLoad();
},
formatDate(time) {
const d = new Date(time);
const m = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
return `${d.getFullYear()}-${m}-${day}`;
},
refresh() {
this.isRefreshing = true;
this.pageNo = 1;
this.loadContracts();
},
resetAndLoad() {
this.pageNo = 1;
this.contracts = [];
this.loadStatus = "loading";
this.loadContracts(true);
},
loadMore() {
if (this.loadStatus === 'nomore') return;
this.pageNo++;
this.loadContracts();
},
/** 你的 loadContracts() 保持不变,只需加:按时间筛选 */
loadContracts(isInit = false) {
this.loadStatus = "loading";
goDetail(item) {
console.log('跳转合同详情页')
uni.navigateTo({
url: `/pages/detail/contractDetail?id=${item.id}`
});
},
setTimeout(() => {
let mockAll = [{
id: 1,
assetName: '金辉大厦',
assetRoom: '502',
coverUrl: '',
status: 'pending',
startDate: '2025-01-01',
endDate: '2025-12-31'
},
{
id: 2,
assetName: '东方广场',
assetRoom: '',
status: 'signed',
startDate: '2024-03-01',
endDate: '2025-03-01'
},
{
id: 3,
assetName: '海景公寓',
assetRoom: 'A-1201',
status: 'expired',
startDate: '2023-01-01',
endDate: '2024-01-01'
},
{
id: 4,
assetName: '富力中心',
assetRoom: '1699',
status: 'pending',
startDate: '2024-10-01',
endDate: '2025-10-01'
},
{
id: 5,
assetName: '财富大厦',
assetRoom: '2308',
status: 'signed',
startDate: '2023-06-01',
endDate: '2024-06-01'
}
];
getStatusText(status) {
switch (status) {
case 'pending':
return '待签署';
case 'signed':
return '已签署';
case 'expired':
return '已过期';
default:
return '';
}
}
}
};
// 状态过滤
if (this.statusFilter !== "all") {
mockAll = mockAll.filter(i => i.status === this.statusFilter);
}
// 时间过滤
if (this.startDate) mockAll = mockAll.filter(i => i.startDate >= this.startDate);
if (this.endDate) mockAll = mockAll.filter(i => i.endDate <= this.endDate);
// 分页
this.total = mockAll.length;
const start = (this.pageNo - 1) * this.pageSize;
const pageList = mockAll.slice(start, start + this.pageSize);
if (this.pageNo === 1) {
this.contracts = pageList;
} else {
this.contracts = [...this.contracts, ...pageList];
}
this.loadStatus = this.contracts.length >= this.total ? "nomore" : "loadmore";
this.isRefreshing = false;
}, 400);
},
refresh() {
this.isRefreshing = true;
this.resetAndLoad();
},
loadMore() {
if (this.loadStatus !== "loadmore") return;
this.pageNo++;
this.loadContracts();
},
goDetail(item) {
uni.navigateTo({
url: `/pages/detail/contractDetail?id=${item.id}`,
});
},
getStatusText(status) {
switch (status) {
case "pending":
return "待签署";
case "signed":
return "已签署";
case "expired":
return "已过期";
}
},
},
};
</script>
<style lang="scss" scoped>
.contract-page {
background-color: #f8f8f8;
padding-top: 175rpx; /* 给导航栏留空间 */
min-height: 100vh;
}
.contract-page {
background-color: #f8f8f8;
padding-top: 175rpx;
/* 给导航栏留空间 */
min-height: 100vh;
}
.scroll-content {
height: calc(100vh - 100rpx);
}
.scroll-content {
height: calc(100vh - 100rpx);
}
.contract-list {
padding: 20rpx;
}
.contract-list {
padding: 20rpx;
}
.contract-card {
display: flex;
background: #fff;
border-radius: 16rpx;
margin-bottom: 20rpx;
padding: 20rpx;
box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.05);
position: relative;
}
.contract-card {
display: flex;
background: #fff;
border-radius: 16rpx;
margin-bottom: 20rpx;
padding: 20rpx;
box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.05);
position: relative;
}
.contract-cover {
width: 160rpx;
height: 160rpx;
border-radius: 12rpx;
background-color: #f0f0f0;
flex-shrink: 0;
}
.contract-cover {
width: 160rpx;
height: 160rpx;
border-radius: 12rpx;
background-color: #f0f0f0;
flex-shrink: 0;
}
.contract-info {
flex: 1;
margin-left: 20rpx;
display: flex;
flex-direction: column;
position: relative;
}
.contract-info {
flex: 1;
margin-left: 20rpx;
display: flex;
flex-direction: column;
position: relative;
}
.top-row {
display: flex;
justify-content: space-between;
align-items: center;
.asset-name {
font-size: 30rpx;
font-weight: bold;
color: #333;
flex: 1;
}
}
.top-row {
display: flex;
justify-content: space-between;
align-items: center;
.asset-room {
font-size: 26rpx;
color: #555;
margin-top: 10rpx;
}
.asset-name {
font-size: 30rpx;
font-weight: bold;
color: #333;
flex: 1;
}
}
.date-range {
font-size: 24rpx;
color: #888;
margin-top: 8rpx;
}
.asset-room {
font-size: 26rpx;
color: #555;
margin-top: 10rpx;
}
.status-tag {
position: absolute;
bottom: 10rpx;
right: 0rpx;
padding: 6rpx 16rpx;
border-radius: 24rpx;
font-size: 24rpx;
.date-range {
font-size: 24rpx;
color: #888;
margin-top: 8rpx;
}
&.pending {
background: #fff8e1;
color: #ff9800;
}
&.signed {
background: #e8f5e9;
color: #4caf50;
}
&.expired {
background: #fbe9e7;
color: #e53935;
}
}
.status-tag {
position: absolute;
bottom: 10rpx;
right: 0rpx;
padding: 6rpx 16rpx;
border-radius: 24rpx;
font-size: 24rpx;
.empty {
margin-top: 200rpx;
}
</style>
&.pending {
background: #fff8e1;
color: #ff9800;
}
&.signed {
background: #e8f5e9;
color: #4caf50;
}
&.expired {
background: #fbe9e7;
color: #e53935;
}
}
.empty {
margin-top: 200rpx;
}
.tab-wrapper {
background-color: #ffffff;
padding: 10rpx 0;
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.06);
position: sticky;
top: 0;
z-index: 10;
}
</style>

View File

@@ -2,40 +2,28 @@
<view class="asset-detail">
<!-- 顶部导航栏 -->
<customNavbar
title="不动资产详情"
></customNavbar>
title="不动资产详情" ref="navbar"/>
<!-- 顶部信息块 -->
<view class="top-block u-flex u-row-between u-p-20">
<!-- 左侧轮播图 -->
<view class="left-swiper">
<u-swiper
<view :style="{ paddingTop: navTotalHeight + 'px' }">
<!-- <u-swiper
:list="asset.images"
height="220"
border-radius="12"
autoplay
interval="3000"
></u-swiper>
</view>
></u-swiper> -->
<AssetGallery
:insideImages="asset.images"
:vrImage="asset.vrImage"
:vrSrc="asset.vr"
:videos="asset.videos"
:plans="asset.plans"
:adImage="asset.adImage"
@vr-click="viewVR"
@video-play="playVideo"
@ad-click="onAdClick"
/>
<!-- 右侧信息块 -->
<view class="right-info u-flex u-flex-col u-justify-between">
<view>
<view class="asset-name">{{ asset.name }}</view>
<view class="asset-community">所属小区{{ asset.community }}</view>
<view class="asset-rent">租金<text class="price">¥{{ formatMoney(asset.rent) }}/</text></view>
</view>
<view class="btn-group">
<u-button
size="mini"
type="primary"
icon="map"
@click="viewVR"
>
查看VR图
</u-button>
</view>
</view>
</view>
<view class="content">
@@ -93,17 +81,18 @@
<script>
import mycard from '../../components/card/mycard.vue';
import assetBottomBar from '../../components/bottom/assetBottomBar.vue';
import AssetGallery from '../../components/gallery/AssetGallery.vue'
export default {
components:{
mycard,
assetBottomBar
assetBottomBar,
AssetGallery
},
data() {
return {
// 控制预约弹窗显示
showReserve: false,
navTotalHeight: 0,
// 预约表单数据
reserveName: '',
reservePhone: '',
@@ -131,6 +120,13 @@ export default {
onLoad(options) {
this.assetId = options.id || 'A001';
this.loadAssetDetail();
// this.recordView();
},
mounted() {
const navbar = this.$refs.navbar;
const navHeight = navbar.navContentHeight; // 直接拿子组件 data
this.navTotalHeight = navHeight; // 加上额外间距
},
methods: {
// 模拟接口请求
@@ -150,12 +146,15 @@ export default {
lat: 23.135,
lng: 113.327,
images: [
'https://cdn.uviewui.com/uview/swiper/swiper1.png',
'https://cdn.uviewui.com/uview/swiper/swiper2.png',
{
image:'/static/img/index/swiper/swiper3.jpg'
}
],
remark: '该资产目前用于办公出租,维护状态良好。',
isFavorite: false,
status: 1
status: 0
};
this.markers = [
{
@@ -192,7 +191,15 @@ export default {
submitReserve (){
console.log('提交预约看房申请')
this.showReserve = false;
}
},
recordView(assetId) {
let lifeData = uni.getStorageSync('lifeData');
let token = lifeData.vuex_token
let url = "/assets/viewRecord";
this.$u.get(url, {}, {'WT': token}).then(obj => {
});
}
},
};
</script>

View File

@@ -1,120 +1,134 @@
<template>
<view class="u-p-l-10 u-p-r-10 index">
<!-- 流量主-腾讯banner广告 -->
<view>
<!-- <ad unit-id="adunit-fcfdcc4d7095b6b1" ad-intervals="30"></ad> -->
<!-- 轮播图 -->
<!-- #ifndef MP-WEIXIN -->
<!-- <u-swiper :list="swiperList" height="350"></u-swiper> -->
<!-- #endif -->
<u-swiper :list="swiperList" height="350" @click="moreInfo"></u-swiper>
<view class="index">
<image src="/static/index/index_bg.png" mode="aspectFill" class="bg"></image>
<!-- 搜索栏 -->
<view class="index-title">
启辰资产
</view>
<!-- 房源信息导航栏 -->
<view>
<view class="rowClass">
<u-row>
<u-col span="3" text-align="center" v-for="(item,index) in navList" :key="index">
<view class="u-padding-20" @tap="clickNav(item, index)" hover-class="hoverClass">
<image :src="item.src" style="width: 130rpx;height: 130rpx;" mode="widthFix"></image>
<view class="tabName">{{item.name}}</view>
<!-- 点击后出现下划线 -->
<view
v-if="activeIndex === index"
class="underline"
:style="{ background: item.underline_color }"
></view>
</view>
</u-col>
</u-row>
<!-- 下划线动态颜色 + 动画滑动 -->
<!-- <view
class="underline"
:style="{
transform: `translateX(${activeIndex * 100}%)`,
background: navList[activeIndex].underline_color
}"
></view> -->
</view>
<view class="rowClass">
<u-row>
<u-col span="3" text-align="center" v-for="(item,index) in gridList" :key="index">
<view class="u-padding-20" @tap="clickGrid(item)" hover-class="hoverClass">
<image :src="item.src" style="width: 110rpx;height: 110rpx;" mode="widthFix"></image>
<view class="tabName">{{item.name}}</view>
</view>
</u-col>
</u-row>
</view>
<u-gap height="10"></u-gap>
<!-- 公告栏 -->
<view @click="notice">
<u-notice-bar mode="vertical" :list="noticeList" type="primary" more-icon
bg-color="#fff" :duration="5000" border-radius="15"></u-notice-bar>
</view>
<!-- 搜索栏 -->
<!-- <u-navbar :is-back="false" :fixed="false" :safeAreaInsetTop="false"> -->
<view class="search-bar" style="display: flex;justify-content: center;align-items: center;">
<!-- 选择地区 -->
<view class="u-p-20 location-chose" @click="location">
{{vuex_city==''?'选择':vuex_city}}
<u-icon name="arrow-down-fill" class="u-p-l-10" color="#515356"></u-icon>
</view>
<!-- #ifdef MP-WEIXIN -->
<u-search placeholder="你想住在哪儿" v-model="keyword" input-align="center" :show-action="false" :clearabled="true"
:disabled="true" style="width: 380rpx;" @click="search"></u-search>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<u-search placeholder="你想住在哪儿" v-model="keyword" input-align="center" :show-action="false" :clearabled="true"
:disabled="true" style="width: 580rpx;" @click="search"></u-search>
<!-- #endif -->
<view class="content-wrapper">
<view class="search-wrapper">
<search-bar
:city="vuex_city==''?'选择':vuex_city"
placeholder="你想住在哪儿"
@chooseCity="location"
@search="search"
/>
</view>
<!-- </u-navbar> -->
<u-gap height="5"></u-gap>
<u-waterfall v-model="flowList" ref="uWaterfall">
<template v-slot:left="{leftList}">
<view class="demo-warter" v-for="(item, index) in leftList" :key="index">
<u-lazy-load threshold="750" border-radius="12" :image="item.coverImgUrl" :index="index"
@click="clickImage(item.id)"></u-lazy-load>
<view class="item-title">{{item.name}}</view>
<view class="item-desc">{{ item.footPrint}}</view>
<view class="item-price">¥{{item.rentFee}}</view>
</view>
</template>
<template v-slot:right="{rightList}">
<view class="demo-warter" v-for="(item, index) in rightList" :key="index">
<u-lazy-load threshold="750" border-radius="10" :image="item.coverImgUrl" :index="index"
@click="clickImage(item.id)"></u-lazy-load>
<view class="item-title">{{item.name}}</view>
<view class="item-desc">{{ item.footPrint}}</view>
<view class="item-price">¥{{item.rentFee}}</view>
</view>
</template>
</u-waterfall>
</view>
<u-loadmore bg-color="rgb(240, 240, 240)" :status="loadStatus" @loadmore="findHouseList" style="height: 80rpx;line-height: 80rpx;"></u-loadmore>
<u-back-top :scroll-top="scrollTop" top="1000"></u-back-top>
<u-no-network></u-no-network>
<!-- 联系客服 -->
<!-- <view class="buttom">
<view class="loginType">
<view class="wechat item">
<view class="icon"><u-icon size="60" name="server-man" color="#999" @click="server"></u-icon></view>
</view>
</view>
</view> -->
<!-- 房源信息导航栏 -->
<view class="nav-area">
<view class="rowClass">
<u-row>
<u-col span="3" text-align="center" v-for="(item,index) in navList" :key="index">
<view class="u-padding-20" @tap="clickNav(item, index)" hover-class="hoverClass">
<image :src="item.src" style="width: 86rpx;height: 86rpx;" mode="heightFix"></image>
<view class="tabName" :style="{color:'#FFFFFF'}">{{item.name}}</view>
</view>
</u-col>
</u-row>
</view>
<view class="rowClass navbox2">
<u-row>
<u-col span="3" text-align="center" v-for="(item,index) in gridList" :key="index">
<view class="u-padding-20" @tap="clickGrid(item)" hover-class="hoverClass">
<image :src="item.src" style="width: 50rpx;height: 50rpx;" mode="heightFix"></image>
<view class="tabName" :style="{color:'#222222'}">{{item.name}}</view>
</view>
</u-col>
</u-row>
</view>
</view>
<u-gap height="10"></u-gap>
<!-- 公告栏 -->
<view @click="notice" class="noticeStyle">
<image src="/static/index/公告.png" style="width: 38rpx;height: 37rpx;"></image>
<u-notice-bar mode="vertical" :list="noticeList" type="primary" more-icon
bg-color="none" :duration="5000" :fontSize = "30" color="#2D2B2C" :volumeIcon="false"></u-notice-bar>
</view>
<u-gap height="5"></u-gap>
<scroll-view
scroll-y
style="height: 100vh;"
:scroll-top="scrollTop"
@scrolltolower="findHouseList"
lower-threshold="50"
>
<u-waterfall v-model="flowList" ref="uWaterfall">
<template v-slot:left="{leftList}">
<view class="demo-warter" v-for="(item, index) in leftList" :key="index" style="margin-right: 10rpx;">
<u-lazy-load threshold="750" border-radius="12" image="/static/img/index/swiper/swiper3.jpg" :index="index" mode="widthFix"
@click="clickImage(item.id)"></u-lazy-load>
<view class="item-title">{{ item.assetsName}}</view>
<view class="item-desc">{{ item.footPrint}}</view>
<view class="item-price">¥{{item.rentFee}}</view>
</view>
</template>
<template v-slot:right="{rightList}">
<view class="demo-warter" v-for="(item, index) in rightList" :key="index" style="margin-left: 10rpx;">
<u-lazy-load threshold="750" border-radius="10" image="/static/img/index/swiper/swiper3.jpg" :index="index" mode="widthFix"
@click="clickImage(item.id)"></u-lazy-load>
<view class="item-title">{{ item.assetsName}}</view>
<view class="item-desc">{{ item.footPrint}}</view>
<view class="item-price">¥{{item.rentFee}}</view>
</view>
</template>
</u-waterfall>
</scroll-view>
<u-back-top :scroll-top="scrollTop" top="1000"></u-back-top>
<u-no-network></u-no-network>
</view>
<u-popup
v-model="showFillAuthInfo"
mode="center"
:mask-close-able="false"
border-radius="24"
>
<view class="auth-popup">
<!-- 标题 -->
<view class="auth-title">
请完善{{authLabel}}信息
</view>
<!-- 描述 -->
<view class="auth-desc">
您当前尚未完善相关信息为避免影响后续功能使用请尽快补充
</view>
<!-- 输入区域 -->
<view class="auth-input">
<u-input
v-model="authCode"
:placeholder="authPlaceholder"
clearable
border="bottom"
font-size="30"
placeholder-style="color:#bbb"
/>
</view>
<!-- 按钮 -->
<button class="auth-btn" @click="recordAuthInfo">
提交
</button>
</view>
</u-popup>
</view>
</template>
<script>
import config from "@/common/config.js" // 全局配置文件
import SearchBar from '../../components/searchBar/SearchBar.vue';
import indexNavbarVue from '../../components/navbar/indexNavbar.vue';
export default {
components: { SearchBar,indexNavbarVue},
data() {
return {
loginType: null,
authCode: '', // 身份证号 / 社会信用代码
showFillAuthInfo: false,
keyword:'',
pageNum: 1,
pageSize: 20,
@@ -131,56 +145,21 @@
title: '身无彩凤双飞翼,心有灵犀一点通'
},
],
noticeList: ['伍家岗招租'],
noticeList: [],
navList:[
{name:"保租房",src:"/static/icon/保租房.png",type:"0",underline_color:"linear-gradient(to right, #ff8c00, #ffcc33)"},
{name:"商铺",src:"/static/icon/商铺.png",type:"1",underline_color:"linear-gradient(to right, #ff8c00, #ffcc33)"},
{name:"厂房",src:"/static/icon/厂房.png",url:"/pages/center/tips",underline_color:"linear-gradient(to right, #ff8c00, #ffcc33)"},
{name:"停车场",src:"/static/icon/停车场.png",type:"2",underline_color:"linear-gradient(to right, #ff8c00, #ffcc33)"}
{name:"房",src:"/static/index/住房.png",type:"0",underline_color:"linear-gradient(to right, #ff8c00, #ffcc33)"},
{name:"商铺",src:"/static/index/商铺.png",type:"1",underline_color:"linear-gradient(to right, #89CFF0, #5AA4D1)"},
{name:"厂房",src:"/static/index/厂房.png",url:"/pages/center/tips",underline_color:"linear-gradient(to right, #0000FF, #800080)"},
{name:"停车场",src:"/static/index/停车场.png",type:"2",underline_color:"linear-gradient(to bottom, #000080, #00008080)"}
],
gridList:[
{name:"看房预约",src:"/static/icon/预约看房.png",url:"pages/reserve/reserveRecords"},
{name:"合同签约",src:"/static/icon/合同签约.png",url:"pages/center/contract"},
{name:"退租申请",src:"/static/icon/退租申请.png",url:"pages/form/leaseCancel"}
{name:"看房预约",src:"/static/index/看房预约.png",url:"pages/reserve/reserveRecords"},
{name:"合同签约",src:"/static/index/合同签约.png",url:"pages/center/contract"},
{name:"退租申请",src:"/static/index/退租申请.png",url:"pages/form/leaseCancel"},
{name:"留言板",src:"/static/index/留言板.png",url:"pages/form/leaseCancel"}
],
loadStatus: 'loadmore',
flowList: [
{
"id":"0001",
"name":"测试房屋",
"type":"0",
"rentFee": "1400",
"footPrint": "80",
"coverImgUrl":"/static/img/index/swiper/swiper3.jpg"
},
{
"id":"0002",
"name":"测试房屋",
"type":"0",
"rentFee": "1400",
"footPrint": "80",
"coverImgUrl":"/static/img/index/swiper/swiper3.jpg"
},
{
"id":"0003",
"name":"测试房屋",
"type":"0",
"rentFee": "1400",
"footPrint": "80",
"coverImgUrl":"/static/img/index/swiper/swiper3.jpg"
},
{
"id":"0001",
"name":"测试房屋",
"type":"0",
"rentFee": "1400",
"footPrint": "80",
"coverImgUrl":"/static/img/index/swiper/swiper3.jpg"
}
],
uvCode: uni.getStorageSync('uvCode')
}
@@ -192,11 +171,15 @@
this.findHouseList();
this.getNoticecList();
// 流量统计
this.appSysFlowInfo();
// this.appSysFlowInfo();
uni.$on('findIndexHouseList', (obj) => {
// 获取数据
this.findHouseList(1);
})
},
onShow(){
this.needFillAuthInfo();
},
onUnload() {
// 移除监听事件
@@ -217,6 +200,21 @@
// 关闭刷新
uni.stopPullDownRefresh();
},
mounted(){
},
computed: {
authLabel() {
return this.loginType === 'person'
? '身份证号'
: '企业社会信用代码'
},
authPlaceholder() {
return this.loginType === 'person'
? '请输入身份证号'
: '请输入企业社会信用代码'
}
},
methods: {
checkCity(){
// 检查是否已选择城市,如果未选择,跳转到选择城市页面
@@ -228,6 +226,18 @@
return this.$u.route('/pages/location/location');
}
},
// 简单身份证校验18 位)
checkIdCard(code) {
return /^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/.test(
code
);
},
// 统一社会信用代码校验18 位)
checkCreditCode(code) {
return /^[0-9A-Z]{18}$/.test(code);
}
,
location(){
this.$u.route({
url: 'pages/location/location',
@@ -250,8 +260,8 @@
this.flowList = []
this.$refs.uWaterfall.clear();
}
let url = "/api/houseApi/findHouseRoomList";
this.$u.get(url, {
let url = "/assets/findAssetList";
this.$u.post(url, {
state:1,
villageCity:uni.getStorageSync('lifeData').vuex_city,
pageNum: this.pageNum,
@@ -259,45 +269,26 @@
orderByColumn: 'update_time,create_time',
isAsc: 'desc'
}).then(result => {
const data = result.rows;
const data = result.data.result;
if(this.pageNum>1 && data.length < this.pageSize){
return this.loadStatus = 'nomore';
// return this.loadStatus = 'nomore';
}
this.houseList = data;
for (let i = 0; i < this.houseList.length; i++) {
// 先转成字符串再转成对象,避免数组对象引用导致数据混乱
let item = this.houseList[i]
if(!item.faceUrl.includes(config.staticUrl)){
item.image = config.staticUrl+item.faceUrl
}else{
item.image = item.faceUrl
}
if(item.type == 0){
item.type = '整租'
}else if(item.type == 1){
item.type = '合租'
}
if(item.roomType == 1){
item.roomType = '主卧'
}else if(item.roomType == 2){
item.roomType = '次卧'
}else{
item.roomType = '未知'
}
if(this.$u.test.isEmpty(item.houseNum)){
item.houseNum = ''
}
if(this.$u.test.isEmpty(item.houseHall)){
item.houseHall = ''
}
if(this.$u.test.isEmpty(item.toiletNum)){
item.toiletNum = ''
}
// if(!item.faceUrl.includes(config.staticUrl)){
// item.image = config.staticUrl+item.faceUrl
// }else{
// item.image = item.faceUrl
// }
this.flowList.push(item);
}
++ this.pageNum
this.loadStatus = 'loadmore';
});
}).catch(err => {
console.log("失败:", err)
})
},
clickSearch() {
this.$u.route('/pages/search/search');
@@ -347,18 +338,35 @@
});
},
getNoticecList(){
// let url = "/api/notice/findNoticeList";
// this.$u.get(url,{
// pageNum:1,
// pageSize:50,
// orderByColumn:'create_time',
// isAsc:'desc'
// }).then(obj => {
// let data = obj.rows
// data.filter(item=>{
// this.noticeList.push(item.noticeTitle)
// })
// });
let url = "/notice/findNoticeList";
this.$u.get(url,{
pageNum:1,
pageSize:50,
orderByColumn:'create_time',
isAsc:'desc'
}).then(obj => {
let data = obj.data.result
data.filter(item=>{
this.noticeList.push(item.noticeTitle)
})
});
},
needFillAuthInfo(){
let lifeData = uni.getStorageSync('lifeData');
let token = lifeData.vuex_token
if (!token || token === null || token === undefined ||
(typeof token === 'object' && Object.keys(token).length === 0)) {
return;
}
let url = "/login/needFillAuthInfo";
this.$u.get(url, {}, {'WT': token}).then(obj => {
console.log(obj.data)
if (obj.data) {
this.showFillAuthInfo = obj.data
const indexParam = uni.getStorageSync('indexParam') || {}
this.loginType = indexParam.loginType || 'person'
}
});
},
moreInfo(){
uni.navigateToMiniProgram({
@@ -374,37 +382,103 @@
}
})
},
recordAuthInfo() {
// 身份证校验
if (this.loginType === 'person' && !this.checkIdCard(this.authCode)) {
uni.showToast({
title: '身份证号格式不正确',
icon: 'none'
});
return;
}
// 企业社会信用代码校验
if (this.loginType === 'company' && !this.checkCreditCode(this.authCode)) {
uni.showToast({
title: '社会信用代码格式不正确',
icon: 'none'
});
return;
}
let lifeData = uni.getStorageSync('lifeData');
let token = lifeData.vuex_token
if (!token || token === null || token === undefined ||
(typeof token === 'object' && Object.keys(token).length === 0)) {
return;
}
let url = "/login/recordAuthInfo";
this.$u.post(url, {
code: this.authCode,
userType: this.loginType,
},{'WT': token}).then(obj => {
if (obj.flag) {
this.showFillAuthInfo = false;
this.authCode = '';
}
})
}
}
}
}
</script>
<style lang="scss" scoped>
.index{
margin-top: 20rpx;
.index {
background: #F8F8F8;
min-height: 100vh; // 防止内容少时顶部空白
position: relative;
}
.bg{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 550rpx; /* ✅ 用固定高度更稳,你可改 320/400 */
z-index: 0;
}
.index-title{
position: absolute;
z-index: 1;
top: 5%;
left: 50%;
transform: translateX(-50%);
transform: translateX(-50%);
font-family: Noto Sans S Chinese;
font-weight: 400;
font-size: 34rpx;
color: #FFFFFF;
}
.content-wrapper{
position: relative;
z-index: 1;
}
.search-wrapper {
}
.content-wrapper {
padding: 197rpx 30rpx 22rpx 30rpx; // 统一左右内边距
}
.navbox2 {
height: 170rpx;
padding-top: 2%;
background: #FFFFFF;
border-radius: 10rpx;
}
.nomore {
background-color: $u-bg-color;
}
.search-bar {
margin-top: 10rpx;
background-color: rgb(255, 255, 255);
border-radius: 8px;
}
.location-chose{
margin-right: 20rpx;
}
.search{
width: 70px;
height: 44px;
&:active{
background-color: $u-bg-color;
}
}
.function-grid {
margin: 0 20rpx;
padding: 20rpx 0;
}
.noticeStyle{
display: flex;
margin-top: 22rpx;
height: 86rpx;
background-color: #FFFFFF;
border-radius: 10rpx;
}
/* 下划线 */
.underline {
margin-top: 5rpx;
@@ -415,45 +489,53 @@
}
.rowClass{
border-radius: 8px;
background-color: rgb(255, 255, 255);
margin-top: 10rpx;
margin-top: 20rpx;
}
.rowBgColor{
background-color: rgb(248, 248, 248);
}
.leftOffSetRow{
margin-left: 20%;
}
.hoverClass{
background-color: #E4E7ED;
}
.tabName{
font-size: 28rpx;
color: $u-main-color;
}
.demo-warter {
border-radius: 8px;
margin-top: 3px;
background-color: #ffffff;
padding: 3px;
position: relative;
}
.u-close {
position: absolute;
top: 20rpx;
right: 20rpx;
}
.item-cover {
font-size: 55rpx;
color: $u-type-warning;
}
.item-tag {
font-size: 24rpx;
color: $u-tips-color;
margin-top: 3px;
}
.demo-warter {
margin-bottom: 20rpx;
background: #fff;
border-radius: 16rpx;
overflow: hidden;
}
.u-waterfall {
padding: 0 20rpx; /* 左右留白一致 */
}
.item-title {
font-size: 28rpx;
color: $u-main-color;
font-weight: bold;
padding-top: 5rpx;
padding-left: 10rpx;
font-size: 28rpx;
font-weight: 600;
margin-top: 14rpx;
padding: 0 10rpx;
}
.item-price {
@@ -461,21 +543,69 @@
font-size: 32rpx;
color: $u-type-warning;
}
.item-desc {
font-weight: normal;
font-size: 26rpx;
color: $u-tips-color;
padding-bottom: 5rpx;
padding-left: 10rpx;
}
.item-tag {
font-size: 24rpx;
color: $u-tips-color;
margin-top: 3px;
.item-desc, .item-price {
padding: 0 10rpx;
margin-top: 8rpx;
}
.auth-popup {
width: 600rpx;
padding: 48rpx 40rpx 40rpx;
background-color: #fff;
border-radius: 24rpx;
box-sizing: border-box;
}
/* 标题 */
.auth-title {
font-size: 34rpx;
font-weight: 600;
color: #1a1a1a;
text-align: center;
margin-bottom: 20rpx;
}
/* 描述 */
.auth-desc {
font-size: 26rpx;
color: #888;
text-align: center;
line-height: 1.6;
margin-bottom: 40rpx;
}
/* 输入区域 */
.auth-input {
margin-bottom: 48rpx;
::v-deep .u-input {
padding-bottom: 12rpx;
}
}
/* 提交按钮 */
.auth-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background: linear-gradient(135deg, #FF2F31, #FF9379);
color: #fff;
font-size: 30rpx;
font-weight: 500;
border-radius: 44rpx;
border: none;
box-shadow: 0 10rpx 24rpx rgba(41, 121, 255, 0.3);
}
/* 按压效果 */
.auth-btn:active {
opacity: 0.85;
}
.buttom {
.loginType {
font-size: 14px;

View File

@@ -1,6 +1,6 @@
<template>
<view>
<u-navbar :is-back="false" title="选择城市" :border-bottom="false"></u-navbar>
<view class="location-page">
<custom-navbar title="选择城市" />
<view class="selected" >
<view class="title">
已选 :
@@ -116,6 +116,9 @@
</script>
<style lang="scss" scoped>
.location-page{
padding-top: 175rpx;
}
.selected{
.title {
font-size: 30rpx;

View File

@@ -1,113 +1,190 @@
<template>
<view class="login-page">
<!-- 背景视频 -->
<video
class="bg-video"
src="/static/login.mp4"
autoplay
loop
muted
object-fit="cover"
controls="false"
show-center-play-btn="false"
show-play-btn="false"
show-fullscreen-btn="false"
show-mute-btn="false"
show-status-bar="false"
enable-progress-gesture="false"
/>
<!-- 遮罩层增强可读性 -->
<view class="overlay"></view>
<!-- 背景图 -->
<!-- <image class="bg-img" :src="currentBg" :class="{ fade: isFading }" mode="aspectFill"></image> -->
<!-- 内容层 -->
<view class="login-content">
<view class="title u-font-36 u-m-b-40">
微信授权登录
</view>
<button
class="login-btn"
open-type="getPhoneNumber"
@getphonenumber="onGetPhone"
>
使用微信手机号授权登录
</button>
<view class="privacy-tip u-m-t-30 u-text-center">
登录即同意
<text class="link" @click="goPrivacy('user')">用户协议</text>
<text class="link" @click="goPrivacy('privacy')">隐私政策</text>
<view class="login-container">
<!-- 顶部状态栏 -->
<view class="status-bar">
<u-icon
:name="btn.icon"
:color="btn.color || '#333'"
:size="btn.size || 40"
></u-icon>
</view>
<!-- 主体内容区 -->
<view class="content">
<!-- 标题区域 -->
<view class="title">登录</view>
<!-- 登录插图区域 -->
<view class="illustration">
<image src="/static/login/bg.png" mode="aspectFit"></image>
</view>
<!-- 登录按钮 -->
<button class="login-btn" open-type="getPhoneNumber" @getphonenumber="onGetPhone">
使用微信授权登录
</button>
<!-- 用户类型选择 -->
<view class="user-type" shape="square">
<u-radio-group v-model="loginType" active-color="#EA414A" shape="square" >
<u-radio name="person" >个人</u-radio>
<u-radio name="company" >企业</u-radio>
</u-radio-group>
</view>
<!-- 协议提示 -->
<view class="agreement">
登录即同意
<text class="link">用户协议</text>
<text class="link">隐私政策</text>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
bgImages: [
'/static/loginbg1.jpg',
'/static/loginbg2.jpg'
],
currentIndex: 0,
currentBg: '/static/loginbg1.jpg',
timer: null
data() {
return {
designWidth: 750,
screenWidth: 0,
scale: 1,
bgImages: [
'/static/loginbg1.jpg',
'/static/loginbg2.jpg'
],
currentIndex: 0,
currentBg: '/static/login/bg.png',
isFading: false,
timer: null,
loginType:"person"
}
},
onLoad() {
// this.startBgTimer();
wx.getWindowInfo({
success: (res) => {
this.screenWidth = res.windowWidth
this.scale = this.screenWidth / this.designWidth
}
},
onLoad() {
this.startBgTimer();
},
onUnload() {
clearInterval(this.timer);
},
})
},
onUnload() {
clearInterval(this.timer);
},
computed: {
// title 样式
titleStyle() {
const s = this.scale;
return {
width: `${93 * s}rpx`,
height: `${45 * s}rpx`,
fontSize: `${48 * s}rpx`,
lineHeight: `${40 * s}rpx`,
fontWeight: 500,
color: '#222222',
fontFamily: 'Noto Sans S Chinese',
textAlign: 'center'
}
},
headerStyle() {
const s = this.scale;
return {
marginTop: `${214 * s}rpx`,
flex: 1,
display: 'flex',
flexDirection: 'column',
alignItems: 'center', // 水平居中
justifyContent: 'flex-start' // 从顶部开始
}
},
// cover 样式
coverStyle() {
const s = this.scale;
return {
width: `${428 * s}rpx`,
height: `${608 * s}rpx`
}
}
},
methods: {
startBgTimer() {
this.timer = setInterval(() => {
this.currentIndex = (this.currentIndex + 1) % this.bgImages.length;
this.currentBg = this.bgImages[this.currentIndex];
}, 2000); // 每 4 秒切换一次
},
/** 背景切换定时器 */
startBgTimer() {
this.timer = setInterval(() => {
this.isFading = true;
setTimeout(() => {
this.currentIndex = (this.currentIndex + 1) % this.bgImages.length;
this.currentBg = this.bgImages[this.currentIndex];
this.isFading = false;
}, 500);
}, 4000);
},
/** 微信手机号授权 */
onGetPhone(e) {
const { code } = e.detail;
if (!code) {
this.$mytip.toast("授权失败");
return;
}
this.doLogin(code);
},
async doLogin(code) {
/** 执行登录逻辑 */
async doLogin(phoneGetCode) {
uni.showLoading({ title: "登录中...", mask: true });
try {
const authRes = await this.$u.get(`/api/weChatAuth?code=${code}`);
if (!authRes.hasPhone) {
this.showPhoneSelector(authRes.phoneList, authRes.openid);
return;
}
await this.loginWithOpenId(authRes.openid);
// 微信登录
const loginRes = await new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success: resolve,
fail: reject
});
});
// 调用后端登录接口
const authRes = await this.$u.post(`/login/weChatLogin`, {
phoneGetCode: phoneGetCode,
loginCode: loginRes.code
});
// 保存token
this.$u.vuex('vuex_token', authRes.data);
uni.hideLoading();
console.log(this.loginType)
uni.setStorageSync('indexParam', {
loginType: this.loginType
})
// 跳转首页
uni.switchTab({
url: '/pages/index/index'
});
} catch (err) {
uni.hideLoading();
this.$mytip.toast("登录失败");
uni.switchTab({ url: '/pages/index/index' });
console.error('Login failed:', err);
}
},
/** 触发手机号选择(子组件 → 父组件) */
showPhoneSelector(phoneList, openid) {
this.$emit("choosePhone", { phoneList, openid });
},
async loginWithOpenId(openid) {
const loginRes = await this.$u.post('/api/weChatLoginByOpenId', { openid });
this.$u.vuex('vuex_token', loginRes.token);
this.$u.vuex('vuex_user', loginRes.loginUser);
uni.hideLoading();
uni.switchTab({ url: '/pages/index/index' });
},
/** 查看协议 */
goPrivacy(type) {
const url = type === 'user'
? '/pages/privacy/userAgreement'
const url = type === 'user'
? '/pages/privacy/userAgreement'
: '/pages/privacy/privacyPolicy';
uni.navigateTo({ url });
}
}
@@ -115,95 +192,89 @@ export default {
</script>
<style lang="scss" scoped>
.login-page {
position: relative;
width: 100%;
.login-container {
height: 100vh;
background-color: #fff;
box-sizing: border-box;
overflow: hidden;
.bg-video {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: -1;
}
.status-bar {
margin-top: 7.56%;
height: var(--status-bar-height);
}
.overlay {
position: absolute;
top:0;
left:0;
width:100%;
height:100%;
background: rgba(0,0,0,0.3);
z-index:0;
}
.bg-img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
filter: brightness(0.7) blur(2px);
opacity: 1;
transition: opacity 0.5s ease;
}
.bg-img.fade {
opacity: 0;
}
.login-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80%;
max-width: 400rpx;
.content {
display: flex;
flex-direction: column;
align-items: center;
z-index: 1;
animation: floatBtn 2s ease-in-out infinite alternate;
.title {
color: #fff;
font-weight: bold;
text-align: center;
}
.login-btn {
width: 100%;
height: 80rpx;
line-height: 80rpx;
font-size: 28rpx;
color: #fff;
border-radius: 40rpx;
background: linear-gradient(90deg, #4CAF50, #07c160);
box-shadow: 0 6rpx 12rpx rgba(0,0,0,0.25);
text-align: center;
margin-top: 20rpx;
}
.privacy-tip {
font-size: 24rpx;
color: #fff;
margin-top: 20rpx;
text-align: center;
.link {
color: #ffd700;
text-decoration: underline;
margin: 0 4rpx;
}
}
height: calc(100vh - var(--status-bar-height));
margin-top: 12.8%;
}
.title {
height: 45rpx;
font-family: Noto Sans S Chinese;
font-weight: 500;
font-size: 48rpx;
color: #222222;
line-height: 40rpx;
}
.illustration {
width: 100%;
height: 44.6%;
padding-left: 21.47%;
padding-right: 21.47%;
margin-bottom: 6%;
overflow: hidden;
}
.illustration image {
width: 100%;
height: 100%;
}
.login-btn {
width: 78.6%;
height: 7.12%;
background: linear-gradient(90deg, #FF2F31 0%, #FF9379 100%);
border-radius: 49rpx;
font-family: Noto Sans S Chinese;
font-weight: 400;
font-size: 36rpx;
color: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 40rpx;
}
.user-type {
display: flex;
justify-content: center;
gap: 40rpx;
margin-bottom: 20rpx;
}
.agreement {
font-family: Noto Sans S Chinese;
font-weight: 400;
font-size: 30rpx;
color: #CECECE;
align-items: center;
margin-top: 6.24%;
}
.link{
color: #334254;
}
}
@keyframes floatBtn {
0% { transform: translate(-50%, -50%) translateY(0); }
100% { transform: translate(-50%, -50%) translateY(-10rpx); }
/* 响应式适配 */
@media (max-width: 320rpx) {
.title { font-size: 42rpx; margin-bottom: 100rpx; }
.illustration { height: 360rpx; }
.login-btn { height: 80rpx; font-size: 30rpx; }
}
</style>

View File

@@ -12,7 +12,7 @@
<scroll-view scroll-y class="message-list">
<view v-for="(msg,index) in messages" :key="index" class="msg-item u-flex u-row-between" @click="viewMessage(msg)">
<view class="u-flex">
<u-icon :name="msg.icon" size="48" :color="msg.read ? '#ccc' : '#ff8c00'"></u-icon>
<u-icon :name="msg.icon" size="48" :color="msg.read ? '#ccc' : 'cornflowerblue'"></u-icon>
<view class="msg-content u-m-l-20">
<view class="msg-title u-font-16">{{ msg.title }}</view>
<view class="msg-desc u-tips-color u-font-12 u-line-2">{{ msg.desc }}</view>

View File

@@ -40,27 +40,27 @@ export default {
methods: {
clickContent(item){
if(item.id){
this.$u.route('/pages/content/content', {
this.$u.route('/pages/notice/noticeDetail', {
id: item.id
});
}
},
getNoticecList(){
let url = "/api/notice/findNoticeList";
let url = "/notice/findNoticeList";
this.$u.get(url,{
pageNum:this.pageNum,
pageSize:this.pageSize,
orderByColumn:'create_time',
isAsc:'desc'
}).then(obj => {
let data = obj.rows
let data = obj.data.result
data.filter(item=>{
this.dataList.push(
{
id:item.noticeId,
title: item.noticeTitle,
date:item.createTime,
content: item.remark,
content: item.reMark,
}
)
})

View File

@@ -1,6 +1,7 @@
<template>
<view>
<!-- <u-navbar :is-back="true" title="房源列表" :border-bottom="false"></u-navbar> -->
<view class="search-list-page">
<!-- 顶部导航栏 -->
<customNavbar title="资产列表" />
<u-sticky offset-top="0">
<view class="sticky">
<filterDropdown :menuTop="0" :filterData="filterData" :defaultSelected="defaultSelected" :updateMenuName="true" @confirm="confirm" dataFormat="Object"></filterDropdown>
@@ -8,24 +9,24 @@
</u-sticky>
<view class="u-p-l-10 u-p-r-10 waterfall">
<u-waterfall v-model="flowList" ref="uWaterfall">
<template v-slot:left="{leftList}">
<view class="demo-warter" v-for="(item, index) in leftList" :key="index">
<u-lazy-load threshold="750" border-radius="12" :image="item.image" :index="index"
@click="clickImage(item.id)"></u-lazy-load>
<view class="item-title">{{item.villageName}} {{item.type == '整租' ? item.houseNum + item.houseHall + item.toiletNum : item.roomType}}</view>
<view class="item-price">¥{{item.price}}</view>
<view class="item-desc">{{item.type}} | {{item.type == '整租' ? item.houseArea : item.roomArea}} | {{item.decoration}} </view>
</view>
</template>
<template v-slot:right="{rightList}">
<view class="demo-warter" v-for="(item, index) in rightList" :key="index">
<u-lazy-load threshold="750" border-radius="10" :image="item.image" :index="index"
@click="clickImage(item.id)"></u-lazy-load>
<view class="item-title">{{item.villageName}} {{item.type == '整租' ? item.houseNum + item.houseHall + item.toiletNum : item.roomType}}</view>
<view class="item-price">¥{{item.price}}</view>
<view class="item-desc">{{item.type}} | {{item.type == '整租' ? item.houseArea : item.roomArea}} | {{item.decoration}} </view>
</view>
</template>
<template v-slot:left="{leftList}">
<view class="demo-warter" v-for="(item, index) in leftList" :key="index">
<u-lazy-load threshold="750" border-radius="12" :image="item.coverImgUrl" :index="index"
@click="clickImage(item.id)"></u-lazy-load>
<view class="item-title">{{item.name}}</view>
<view class="item-desc">{{ item.footPrint}}</view>
<view class="item-price">¥{{item.rentFee}}</view>
</view>
</template>
<template v-slot:right="{rightList}">
<view class="demo-warter" v-for="(item, index) in rightList" :key="index">
<u-lazy-load threshold="750" border-radius="10" :image="item.coverImgUrl" :index="index"
@click="clickImage(item.id)"></u-lazy-load>
<view class="item-title">{{item.name}}</view>
<view class="item-desc">{{ item.footPrint}}</view>
<view class="item-price">¥{{item.rentFee}}</view>
</view>
</template>
</u-waterfall>
<u-loadmore bg-color="rgb(240, 240, 240)" :status="loadStatus" @loadmore="findHouseList" style="height: 80rpx;line-height: 80rpx;"></u-loadmore>
<u-back-top :scroll-top="scrollTop" top="1000"></u-back-top>
@@ -91,59 +92,42 @@
uni.stopPullDownRefresh();
},
methods: {
findHouseList() {
let url = "/api/houseApi/findHouseRoomList";
let defaultData = {
state:1,
villageCity:uni.getStorageSync('lifeData').vuex_city,
pageNum: this.pageNum,
pageSize: this.pageSize,
orderByColumn: 'update_time,create_time',
isAsc: 'desc'
}
this.$u.get(url, {...defaultData,...this.searchData}).then(result => {
console.log(this.searchData);
console.log(result);
const data = result.rows;
if(this.pageNum>1 && data.length < this.pageSize){
return this.loadStatus = 'nomore';
}
this.houseList = data;
for (let i = 0; i < this.houseList.length; i++) {
// 先转成字符串再转成对象,避免数组对象引用导致数据混乱
let item = this.houseList[i]
if(!item.faceUrl.includes(config.staticUrl)){
item.image = config.staticUrl+item.faceUrl
}else{
item.image = item.faceUrl
}
if(item.type == 0){
item.type = '整租'
}else if(item.type == 1){
item.type = '合租'
}
if(item.roomType == 1){
item.roomType = '主卧'
}else if(item.roomType == 2){
item.roomType = '次卧'
}else{
item.roomType = '未知'
}
if(this.$u.test.isEmpty(item.houseNum)){
item.houseNum = ''
}
if(this.$u.test.isEmpty(item.houseHall)){
item.houseHall = ''
}
if(this.$u.test.isEmpty(item.toiletNum)){
item.toiletNum = ''
}
this.flowList.push(item);
}
++ this.pageNum
this.loadStatus = 'loadmore';
});
},
findHouseList(type = 0) {
if(type == 1){
this.pageNum = 1
this.flowList = []
this.$refs.uWaterfall.clear();
}
let url = "/assets/findAssetList";
this.$u.post(url, {
state:1,
villageCity:uni.getStorageSync('lifeData').vuex_city,
pageNum: this.pageNum,
pageSize: this.pageSize,
orderByColumn: 'update_time,create_time',
isAsc: 'desc'
}).then(result => {
const data = result.data.result;
if(this.pageNum>1 && data.length < this.pageSize){
// return this.loadStatus = 'nomore';
}
this.houseList = data;
for (let i = 0; i < this.houseList.length; i++) {
// 先转成字符串再转成对象,避免数组对象引用导致数据混乱
let item = this.houseList[i]
// if(!item.faceUrl.includes(config.staticUrl)){
// item.image = config.staticUrl+item.faceUrl
// }else{
// item.image = item.faceUrl
// }
this.flowList.push(item);
}
++ this.pageNum
this.loadStatus = 'loadmore';
}).catch(err => {
console.log("失败:", err)
})
},
findVillageList() {
let url = "/api/houseApi/findVillageList";
this.$u.get(url,{
@@ -213,6 +197,9 @@
</script>
<style lang="scss" scoped>
.search-list-page{
padding-top: 175rpx;
}
.waterfall{
// padding-top: 85rpx;
}

26
pages/vr/vr.vue Normal file
View File

@@ -0,0 +1,26 @@
<template>
<view class="vr-page">
<web-view :src="src"></web-view>
</view>
</template>
<script>
export default {
data() {
return {
src: ""
};
},
onLoad(options) {
this.src = decodeURIComponent(options.url);
}
};
</script>
<style>
.vr-page,
.vr-page web-view {
width: 100%;
height: 100%;
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<view v-if="src">
<web-view :webview-styles="wbStyles" :src="src" :fullscreen="false" />
<web-view :webview-styles="wbStyles" :src="src" :fullscreen="false" bindmessage="handleGetMessage"/>
</view>
</template>
@@ -16,6 +16,11 @@ export default {
},
}
},
methods: {
handleGetMessage(e){
}
},
onLoad(option) {
console.log(option.title);
this.title = option?.title

BIN
static/icon/parking.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
static/icon/sign.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
static/icon/书签.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
static/icon/住房C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 204 KiB

BIN
static/icon/厂房C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
static/icon/商铺C.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
static/icon/待付款.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
static/icon/水电费.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
static/icon/留言纸.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/icon/账单.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
static/icon/账本.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
static/icon/退租.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
static/icon/预约.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
static/index/index_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

BIN
static/index/住房.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
static/index/停车场.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
static/index/公告.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
static/index/厂房.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
static/index/商铺.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 949 KiB

BIN
static/index/留言板.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
static/login/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

View File

@@ -4,12 +4,26 @@ module.exports = {
devServer: {
proxy: {
'/api': {
target: 'https://sourcebyte.vip',
target: 'http://localhost:8089',
changeOrigin: true,
pathRewrite: {
'^/api': '/api'
}
}
}
}
}
// module.exports = {
// publicPath: './',
// devServer: {
// proxy: {
// '/api': {
// target: 'https://sourcebyte.vip',
// changeOrigin: true,
// pathRewrite: {
// '^/api': '/api'
// }
// }
// }
// }
// }