初始化参股
48
前端/App.uvue
Normal file
@@ -0,0 +1,48 @@
|
||||
<script lang="uts">
|
||||
// #ifdef APP-ANDROID || APP-HARMONY
|
||||
let firstBackTime = 0
|
||||
// #endif
|
||||
export default {
|
||||
onLaunch() {
|
||||
console.log('App Launch')
|
||||
},
|
||||
onShow() {
|
||||
console.log('App Show')
|
||||
},
|
||||
onHide() {
|
||||
console.log('App Hide')
|
||||
},
|
||||
// #ifdef APP-ANDROID || APP-HARMONY
|
||||
onLastPageBackPress() {
|
||||
console.log('App LastPageBackPress')
|
||||
if (firstBackTime == 0) {
|
||||
uni.showToast({
|
||||
title: '再按一次退出应用',
|
||||
position: 'bottom',
|
||||
})
|
||||
firstBackTime = Date.now()
|
||||
setTimeout(() => {
|
||||
firstBackTime = 0
|
||||
}, 2000)
|
||||
} else if (Date.now() - firstBackTime < 2000) {
|
||||
firstBackTime = Date.now()
|
||||
uni.exit()
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
onExit() {
|
||||
console.log('App Exit')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/*每个页面公共css */
|
||||
.uni-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.uni-column {
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
142
前端/README.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# 优艺家沙发翻新小程序
|
||||
|
||||
## 项目简介
|
||||
|
||||
基于 uni-app x 开发的沙发翻新案例展示小程序,支持微信小程序、H5等多端运行。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **框架**:uni-app x (Vue 3 + UTS)
|
||||
- **样式**:SCSS
|
||||
- **状态管理**:组合式 API (Composition API)
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
├── api/ # API接口定义
|
||||
│ └── index.uts # 接口统一管理
|
||||
├── components/ # 公共组件
|
||||
│ ├── case-card/ # 案例卡片组件
|
||||
│ ├── before-after/ # 翻新前后对比组件
|
||||
│ ├── nav-bar/ # 自定义导航栏
|
||||
│ ├── section-header/ # 区块标题组件
|
||||
│ └── service-card/ # 服务卡片组件
|
||||
├── pages/ # 页面文件
|
||||
│ ├── index/ # 首页
|
||||
│ ├── cases/ # 案例模块
|
||||
│ │ ├── list.uvue # 案例列表
|
||||
│ │ └── detail.uvue # 案例详情
|
||||
│ ├── service/ # 服务介绍
|
||||
│ ├── about/ # 关于我们
|
||||
│ ├── booking/ # 预约咨询
|
||||
│ └── user/ # 用户中心
|
||||
├── static/ # 静态资源
|
||||
│ ├── icons/ # 图标文件
|
||||
│ ├── images/ # 图片资源
|
||||
│ └── mock/ # Mock图片
|
||||
├── utils/ # 工具函数
|
||||
│ ├── config.uts # 配置文件
|
||||
│ ├── request.uts # 网络请求封装
|
||||
│ └── mock.uts # Mock数据
|
||||
├── pages.json # 页面配置
|
||||
├── manifest.json # 应用配置
|
||||
└── uni.scss # 全局样式变量
|
||||
```
|
||||
|
||||
## 功能模块
|
||||
|
||||
### 1. 首页
|
||||
- 轮播图展示
|
||||
- 服务类型入口
|
||||
- 公司优势展示
|
||||
- 热门案例推荐
|
||||
- 预约入口
|
||||
|
||||
### 2. 案例展示
|
||||
- 分类筛选(皮沙发/布艺沙发/功能沙发等)
|
||||
- 案例列表(瀑布流/列表展示)
|
||||
- 案例详情(翻新前后对比、详细信息)
|
||||
|
||||
### 3. 服务介绍
|
||||
- 服务类型说明
|
||||
- 服务流程展示
|
||||
- 材质说明
|
||||
- 常见问题
|
||||
|
||||
### 4. 预约咨询
|
||||
- 预约表单
|
||||
- 图片上传
|
||||
- 表单验证
|
||||
|
||||
### 5. 用户中心
|
||||
- 用户登录
|
||||
- 我的预约
|
||||
- 我的收藏
|
||||
- 设置功能
|
||||
|
||||
## 开发说明
|
||||
|
||||
### 安装依赖
|
||||
|
||||
```bash
|
||||
# 使用 HBuilderX 打开项目
|
||||
```
|
||||
|
||||
### 运行项目
|
||||
|
||||
1. 使用 HBuilderX 打开项目
|
||||
2. 点击运行 -> 运行到小程序模拟器 -> 微信开发者工具
|
||||
|
||||
### Mock 数据
|
||||
|
||||
项目默认使用 Mock 数据,修改 `utils/config.uts` 中的 `useMock` 变量:
|
||||
|
||||
```typescript
|
||||
// 是否使用Mock数据
|
||||
export const useMock = true // 开发阶段
|
||||
export const useMock = false // 对接后端
|
||||
```
|
||||
|
||||
### 后端接口
|
||||
|
||||
项目已预留后端接口,接口定义在 `api/index.uts` 中:
|
||||
|
||||
| 接口 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| /banners | GET | 获取轮播图 |
|
||||
| /cases | GET | 获取案例列表 |
|
||||
| /cases/:id | GET | 获取案例详情 |
|
||||
| /cases/hot | GET | 获取热门案例 |
|
||||
| /service/process | GET | 获取服务流程 |
|
||||
| /company/info | GET | 获取公司信息 |
|
||||
| /booking | POST | 提交预约 |
|
||||
| /user/info | GET | 获取用户信息 |
|
||||
| /user/favorites | GET/POST | 收藏管理 |
|
||||
|
||||
## 后续开发计划
|
||||
|
||||
### 前端待完善
|
||||
- [ ] 添加 TabBar 图标
|
||||
- [ ] 添加案例图片资源
|
||||
- [ ] 完善图片预览功能
|
||||
- [ ] 添加下拉刷新
|
||||
- [ ] 完善分享功能
|
||||
- [ ] 添加骨架屏加载
|
||||
|
||||
### 后端开发
|
||||
- [ ] 用户认证模块
|
||||
- [ ] 案例管理接口
|
||||
- [ ] 预约管理接口
|
||||
- [ ] 文件上传接口
|
||||
- [ ] 管理后台
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 运行前请确保已安装 HBuilderX
|
||||
2. 微信小程序需配置 appid
|
||||
3. 图片资源需自行准备
|
||||
4. 正式上线前关闭 Mock 数据
|
||||
|
||||
## 联系方式
|
||||
|
||||
如有问题,请联系开发团队。
|
||||
126
前端/api/index.uts
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* API接口统一管理
|
||||
*/
|
||||
import { get, post } from '../utils/request.uts'
|
||||
|
||||
/**
|
||||
* 获取轮播图 (临时从配置获取,后续可从后端获取)
|
||||
*/
|
||||
export const getBanners = () => {
|
||||
// 由于后端暂时没有轮播图API,先返回固定数据
|
||||
return Promise.resolve({
|
||||
code: 0,
|
||||
message: 'success',
|
||||
data: [
|
||||
{
|
||||
id: '1',
|
||||
image: '/static/mock/banner1.svg',
|
||||
title: '专业沙发翻新服务',
|
||||
link: '/pages/service/index'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
image: '/static/mock/banner2.svg',
|
||||
title: '十年品质保证',
|
||||
link: '/pages/about/index'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
image: '/static/mock/banner3.svg',
|
||||
title: '免费上门评估',
|
||||
link: '/pages/booking/index'
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取案例列表
|
||||
*/
|
||||
export const getCaseList = (params ?: UTSJSONObject) => {
|
||||
// 后端使用page和limit参数,需要转换
|
||||
const queryParams = params ? {
|
||||
page: params['page'] || 1,
|
||||
limit: params['pageSize'] || params['limit'] || 10,
|
||||
serviceType: params['category'], // 后端使用serviceType
|
||||
status: 'published' // 只获取已发布的案例
|
||||
} : {}
|
||||
return get('/cases', queryParams as UTSJSONObject)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取案例详情
|
||||
*/
|
||||
export const getCaseDetail = (id : string) => {
|
||||
return get(`/cases/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取热门案例 (临时使用前4条案例)
|
||||
*/
|
||||
export const getHotCases = () => {
|
||||
return get('/cases', { limit: 4, status: 'published' } as UTSJSONObject)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务流程 (使用服务列表替代)
|
||||
*/
|
||||
export const getServiceProcess = () => {
|
||||
return get('/services')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取有效服务列表
|
||||
*/
|
||||
export const getActiveServices = () => {
|
||||
return get('/services/active')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公司信息
|
||||
*/
|
||||
export const getCompanyInfo = () => {
|
||||
return get('/company/info')
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交预约
|
||||
*/
|
||||
export const submitBooking = (data : UTSJSONObject) => {
|
||||
return post('/booking', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
export const getUserInfo = () => {
|
||||
return get('/user/info')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户收藏列表
|
||||
*/
|
||||
export const getFavorites = () => {
|
||||
return get('/user/favorites')
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加收藏
|
||||
*/
|
||||
export const addFavorite = (caseId : string) => {
|
||||
return post('/user/favorites', { caseId: caseId } as UTSJSONObject)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消收藏
|
||||
*/
|
||||
export const removeFavorite = (caseId : string) => {
|
||||
return post('/user/favorites/remove', { caseId: caseId } as UTSJSONObject)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取预约记录
|
||||
*/
|
||||
export const getBookingList = () => {
|
||||
return get('/user/bookings')
|
||||
}
|
||||
125
前端/components/before-after/before-after.uvue
Normal file
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<view class="before-after">
|
||||
<view class="ba-title" v-if="showTitle">
|
||||
<text class="ba-title-text">翻新前后对比</text>
|
||||
</view>
|
||||
|
||||
<view class="ba-container">
|
||||
<!-- 翻新前 -->
|
||||
<view class="ba-item" @click="previewImage(beforeImage, 'before')">
|
||||
<view class="ba-label before-label">
|
||||
<text class="ba-label-text">翻新前</text>
|
||||
</view>
|
||||
<image
|
||||
class="ba-image"
|
||||
:src="beforeImage"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
</view>
|
||||
|
||||
<!-- 箭头 -->
|
||||
<view class="ba-arrow">
|
||||
<text class="ba-arrow-text">→</text>
|
||||
</view>
|
||||
|
||||
<!-- 翻新后 -->
|
||||
<view class="ba-item" @click="previewImage(afterImage, 'after')">
|
||||
<view class="ba-label after-label">
|
||||
<text class="ba-label-text">翻新后</text>
|
||||
</view>
|
||||
<image
|
||||
class="ba-image"
|
||||
:src="afterImage"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
const props = defineProps<{
|
||||
beforeImage : string
|
||||
afterImage : string
|
||||
showTitle ?: boolean
|
||||
}>()
|
||||
|
||||
// 预览图片
|
||||
const previewImage = (url : string, type : string) => {
|
||||
const urls = type == 'before' ? [props.beforeImage] : [props.afterImage]
|
||||
uni.previewImage({
|
||||
current: url,
|
||||
urls: urls
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.before-after {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.ba-title {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.ba-title-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.ba-container {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.ba-item {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ba-image {
|
||||
width: 100%;
|
||||
height: 280rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.ba-label {
|
||||
position: absolute;
|
||||
bottom: 16rpx;
|
||||
left: 16rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.before-label {
|
||||
background-color: rgba(144, 147, 153, 0.9);
|
||||
}
|
||||
|
||||
.after-label {
|
||||
background-color: rgba(212, 165, 116, 0.9);
|
||||
}
|
||||
|
||||
.ba-label-text {
|
||||
font-size: 22rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.ba-arrow {
|
||||
padding: 0 16rpx;
|
||||
}
|
||||
|
||||
.ba-arrow-text {
|
||||
font-size: 40rpx;
|
||||
color: #D4A574;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
154
前端/components/case-card/case-card.uvue
Normal file
@@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<view class="case-card" @click="handleClick">
|
||||
<!-- 封面图 -->
|
||||
<view class="card-image-wrapper">
|
||||
<image
|
||||
class="card-image"
|
||||
:src="caseData.coverImage"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="card-category">{{ caseData.categoryName }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<view class="card-content">
|
||||
<text class="card-title">{{ caseData.title }}</text>
|
||||
<view class="card-info">
|
||||
<view class="info-item">
|
||||
<text class="info-label">材质:</text>
|
||||
<text class="info-value">{{ caseData.material }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">工期:</text>
|
||||
<text class="info-value">{{ caseData.duration }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-footer">
|
||||
<text class="card-price">{{ caseData.price }}</text>
|
||||
<view class="card-stats">
|
||||
<text class="stat-item">👁 {{ caseData.views }}</text>
|
||||
<text class="stat-item">❤ {{ caseData.likes }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
// 定义Props类型
|
||||
type CaseItem = {
|
||||
id : string
|
||||
title : string
|
||||
category : string
|
||||
categoryName : string
|
||||
coverImage : string
|
||||
material : string
|
||||
duration : string
|
||||
price : string
|
||||
views : number
|
||||
likes : number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
caseData : CaseItem
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e : 'click', id : string) : void
|
||||
}>()
|
||||
|
||||
const handleClick = () => {
|
||||
emit('click', props.caseData.id)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.case-card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.card-image-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 360rpx;
|
||||
}
|
||||
|
||||
.card-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.card-category {
|
||||
position: absolute;
|
||||
top: 16rpx;
|
||||
left: 16rpx;
|
||||
background-color: rgba(212, 165, 116, 0.9);
|
||||
color: #ffffff;
|
||||
font-size: 22rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
margin-bottom: 16rpx;
|
||||
lines: 1;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.card-info {
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
flex-direction: row;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 26rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 26rpx;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 16rpx;
|
||||
padding-top: 16rpx;
|
||||
border-top-width: 1rpx;
|
||||
border-top-style: solid;
|
||||
border-top-color: #EBEEF5;
|
||||
}
|
||||
|
||||
.card-price {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #D4A574;
|
||||
}
|
||||
|
||||
.card-stats {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
font-size: 24rpx;
|
||||
color: #909399;
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
</style>
|
||||
105
前端/components/nav-bar/nav-bar.uvue
Normal file
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<view class="nav-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="nav-content" :style="{ height: navBarHeight + 'px' }">
|
||||
<!-- 左侧返回按钮 -->
|
||||
<view class="nav-left" v-if="showBack" @click="handleBack">
|
||||
<text class="nav-back-icon">←</text>
|
||||
</view>
|
||||
<view class="nav-left" v-else></view>
|
||||
|
||||
<!-- 标题 -->
|
||||
<view class="nav-center">
|
||||
<text class="nav-title" :style="{ color: titleColor }">{{ title }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 右侧插槽 -->
|
||||
<view class="nav-right">
|
||||
<slot name="right"></slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 占位高度 -->
|
||||
<view :style="{ height: (statusBarHeight + navBarHeight) + 'px' }"></view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
const props = defineProps<{
|
||||
title ?: string
|
||||
showBack ?: boolean
|
||||
titleColor ?: string
|
||||
bgColor ?: string
|
||||
}>()
|
||||
|
||||
// 状态栏高度
|
||||
const statusBarHeight = ref(20)
|
||||
// 导航栏高度
|
||||
const navBarHeight = ref(44)
|
||||
|
||||
onMounted(() => {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
statusBarHeight.value = sysInfo.statusBarHeight
|
||||
// #ifdef MP-WEIXIN
|
||||
const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
|
||||
navBarHeight.value = (menuButtonInfo.top - sysInfo.statusBarHeight) * 2 + menuButtonInfo.height
|
||||
// #endif
|
||||
})
|
||||
|
||||
const handleBack = () => {
|
||||
uni.navigateBack({
|
||||
fail: () => {
|
||||
uni.switchTab({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.nav-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #ffffff;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.nav-content {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.nav-left {
|
||||
width: 80rpx;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-back-icon {
|
||||
font-size: 40rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.nav-center {
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.nav-right {
|
||||
width: 80rpx;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
71
前端/components/section-header/section-header.uvue
Normal file
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<view class="section-header">
|
||||
<view class="section-left">
|
||||
<view class="section-line"></view>
|
||||
<text class="section-title">{{ title }}</text>
|
||||
</view>
|
||||
<view class="section-right" v-if="showMore" @click="handleMore">
|
||||
<text class="section-more">查看更多</text>
|
||||
<text class="section-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
const props = defineProps<{
|
||||
title : string
|
||||
showMore ?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e : 'more') : void
|
||||
}>()
|
||||
|
||||
const handleMore = () => {
|
||||
emit('more')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.section-header {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 32rpx 0 24rpx 0;
|
||||
}
|
||||
|
||||
.section-left {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section-line {
|
||||
width: 8rpx;
|
||||
height: 36rpx;
|
||||
background-color: #D4A574;
|
||||
border-radius: 4rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.section-right {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section-more {
|
||||
font-size: 26rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.section-arrow {
|
||||
font-size: 32rpx;
|
||||
color: #909399;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
</style>
|
||||
56
前端/components/service-card/service-card.uvue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<view class="service-card" @click="handleClick">
|
||||
<view class="service-icon-wrapper">
|
||||
<image class="service-icon" :src="icon" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="service-name">{{ name }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
const props = defineProps<{
|
||||
id : string
|
||||
name : string
|
||||
icon : string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e : 'click', id : string) : void
|
||||
}>()
|
||||
|
||||
const handleClick = () => {
|
||||
emit('click', props.id)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.service-card {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24rpx 16rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
width: 160rpx;
|
||||
}
|
||||
|
||||
.service-icon-wrapper {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background-color: #FDF6EE;
|
||||
border-radius: 50%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.service-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 26rpx;
|
||||
color: #333333;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
58
前端/convert-icons.js
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 图标转换脚本
|
||||
* 将 SVG 图标转换为 PNG 格式
|
||||
*
|
||||
* 使用方法:
|
||||
* 1. npm install sharp
|
||||
* 2. node convert-icons.js
|
||||
*/
|
||||
|
||||
const sharp = require('sharp');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const iconsDir = path.join(__dirname, 'static/icons');
|
||||
const outputSize = 81;
|
||||
|
||||
const svgFiles = [
|
||||
'home.svg',
|
||||
'home-active.svg',
|
||||
'cases.svg',
|
||||
'cases-active.svg',
|
||||
'service.svg',
|
||||
'service-active.svg',
|
||||
'user.svg',
|
||||
'user-active.svg'
|
||||
];
|
||||
|
||||
async function convertSvgToPng(svgFile) {
|
||||
const inputPath = path.join(iconsDir, svgFile);
|
||||
const outputPath = path.join(iconsDir, svgFile.replace('.svg', '.png'));
|
||||
|
||||
try {
|
||||
await sharp(inputPath)
|
||||
.resize(outputSize, outputSize)
|
||||
.png()
|
||||
.toFile(outputPath);
|
||||
console.log(`✓ 转换成功: ${svgFile} -> ${svgFile.replace('.svg', '.png')}`);
|
||||
} catch (error) {
|
||||
console.error(`✗ 转换失败: ${svgFile}`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('开始转换图标...\n');
|
||||
|
||||
for (const file of svgFiles) {
|
||||
const filePath = path.join(iconsDir, file);
|
||||
if (fs.existsSync(filePath)) {
|
||||
await convertSvgToPng(file);
|
||||
} else {
|
||||
console.log(`⚠ 文件不存在: ${file}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n转换完成!');
|
||||
}
|
||||
|
||||
main();
|
||||
394
前端/docs/backend-plan.md
Normal file
@@ -0,0 +1,394 @@
|
||||
# 后端开发规划
|
||||
|
||||
## 一、技术选型
|
||||
|
||||
| 项目 | 技术方案 | 说明 |
|
||||
|------|----------|------|
|
||||
| 框架 | Node.js + Express / Koa | 或 Python FastAPI / Java Spring Boot |
|
||||
| 数据库 | MySQL / PostgreSQL | 关系型数据库存储业务数据 |
|
||||
| 缓存 | Redis | 缓存热点数据、会话管理 |
|
||||
| 文件存储 | 阿里云OSS / 腾讯云COS | 图片、文件存储 |
|
||||
| 认证 | JWT + 微信登录 | 用户身份认证 |
|
||||
|
||||
## 二、数据库设计
|
||||
|
||||
### 2.1 用户表 (users)
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
open_id VARCHAR(64) UNIQUE NOT NULL COMMENT '微信openid',
|
||||
union_id VARCHAR(64) COMMENT '微信unionid',
|
||||
nick_name VARCHAR(64) COMMENT '昵称',
|
||||
avatar VARCHAR(255) COMMENT '头像URL',
|
||||
phone VARCHAR(20) COMMENT '手机号',
|
||||
gender TINYINT DEFAULT 0 COMMENT '性别 0未知 1男 2女',
|
||||
status TINYINT DEFAULT 1 COMMENT '状态 0禁用 1正常',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_open_id (open_id),
|
||||
INDEX idx_phone (phone)
|
||||
);
|
||||
```
|
||||
|
||||
### 2.2 案例分类表 (categories)
|
||||
|
||||
```sql
|
||||
CREATE TABLE categories (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
name VARCHAR(32) NOT NULL COMMENT '分类名称',
|
||||
code VARCHAR(32) UNIQUE NOT NULL COMMENT '分类编码',
|
||||
icon VARCHAR(255) COMMENT '图标URL',
|
||||
sort_order INT DEFAULT 0 COMMENT '排序',
|
||||
status TINYINT DEFAULT 1 COMMENT '状态 0禁用 1正常',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 初始数据
|
||||
INSERT INTO categories (name, code, sort_order) VALUES
|
||||
('皮沙发', 'leather', 1),
|
||||
('布艺沙发', 'fabric', 2),
|
||||
('功能沙发', 'functional', 3),
|
||||
('古典沙发', 'antique', 4),
|
||||
('办公沙发', 'office', 5);
|
||||
```
|
||||
|
||||
### 2.3 案例表 (cases)
|
||||
|
||||
```sql
|
||||
CREATE TABLE cases (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
title VARCHAR(128) NOT NULL COMMENT '案例标题',
|
||||
category_id INT NOT NULL COMMENT '分类ID',
|
||||
cover_image VARCHAR(255) NOT NULL COMMENT '封面图',
|
||||
before_images JSON COMMENT '翻新前图片JSON数组',
|
||||
after_images JSON COMMENT '翻新后图片JSON数组',
|
||||
description TEXT COMMENT '案例描述',
|
||||
material VARCHAR(128) COMMENT '使用材质',
|
||||
duration VARCHAR(32) COMMENT '工期',
|
||||
price VARCHAR(32) COMMENT '参考价格',
|
||||
views INT DEFAULT 0 COMMENT '浏览量',
|
||||
likes INT DEFAULT 0 COMMENT '点赞数',
|
||||
is_hot TINYINT DEFAULT 0 COMMENT '是否热门 0否 1是',
|
||||
is_recommend TINYINT DEFAULT 0 COMMENT '是否推荐 0否 1是',
|
||||
status TINYINT DEFAULT 1 COMMENT '状态 0下架 1上架',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_category (category_id),
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_hot (is_hot),
|
||||
INDEX idx_created (created_at)
|
||||
);
|
||||
```
|
||||
|
||||
### 2.4 预约表 (bookings)
|
||||
|
||||
```sql
|
||||
CREATE TABLE bookings (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
booking_no VARCHAR(32) UNIQUE NOT NULL COMMENT '预约编号',
|
||||
user_id BIGINT COMMENT '用户ID',
|
||||
user_name VARCHAR(32) NOT NULL COMMENT '客户姓名',
|
||||
phone VARCHAR(20) NOT NULL COMMENT '联系电话',
|
||||
address VARCHAR(255) NOT NULL COMMENT '地址',
|
||||
sofa_type VARCHAR(32) COMMENT '沙发类型',
|
||||
problem TEXT COMMENT '问题描述',
|
||||
images JSON COMMENT '上传图片JSON数组',
|
||||
status TINYINT DEFAULT 0 COMMENT '状态 0待确认 1已确认 2已上门 3已完成 4已取消',
|
||||
remark TEXT COMMENT '备注',
|
||||
assigned_to BIGINT COMMENT '指派给(员工ID)',
|
||||
visited_at DATETIME COMMENT '上门时间',
|
||||
completed_at DATETIME COMMENT '完成时间',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_user (user_id),
|
||||
INDEX idx_phone (phone),
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_created (created_at)
|
||||
);
|
||||
```
|
||||
|
||||
### 2.5 收藏表 (favorites)
|
||||
|
||||
```sql
|
||||
CREATE TABLE favorites (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
user_id BIGINT NOT NULL COMMENT '用户ID',
|
||||
case_id BIGINT NOT NULL COMMENT '案例ID',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_user_case (user_id, case_id),
|
||||
INDEX idx_user (user_id)
|
||||
);
|
||||
```
|
||||
|
||||
### 2.6 轮播图表 (banners)
|
||||
|
||||
```sql
|
||||
CREATE TABLE banners (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
title VARCHAR(64) COMMENT '标题',
|
||||
image VARCHAR(255) NOT NULL COMMENT '图片URL',
|
||||
link VARCHAR(255) COMMENT '跳转链接',
|
||||
sort_order INT DEFAULT 0 COMMENT '排序',
|
||||
status TINYINT DEFAULT 1 COMMENT '状态 0禁用 1正常',
|
||||
start_time DATETIME COMMENT '开始时间',
|
||||
end_time DATETIME COMMENT '结束时间',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
### 2.7 系统配置表 (settings)
|
||||
|
||||
```sql
|
||||
CREATE TABLE settings (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
setting_key VARCHAR(64) UNIQUE NOT NULL COMMENT '配置键',
|
||||
setting_value TEXT COMMENT '配置值',
|
||||
description VARCHAR(255) COMMENT '说明',
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 初始配置
|
||||
INSERT INTO settings (setting_key, setting_value, description) VALUES
|
||||
('company_name', '优艺家沙发翻新', '公司名称'),
|
||||
('company_slogan', '让旧沙发焕发新生', '公司标语'),
|
||||
('company_description', '优艺家是一家专业从事沙发翻新、维修、改色的服务公司...', '公司介绍'),
|
||||
('contact_phone', '400-888-8888', '客服电话'),
|
||||
('contact_wechat', 'youyijia2026', '微信号'),
|
||||
('company_address', '上海市浦东新区张江高科技园区', '公司地址'),
|
||||
('work_time', '周一至周日 9:00-18:00', '营业时间');
|
||||
```
|
||||
|
||||
## 三、API接口设计
|
||||
|
||||
### 3.1 公共接口
|
||||
|
||||
| 接口 | 方法 | 说明 | 认证 |
|
||||
|------|------|------|------|
|
||||
| POST /api/auth/wxlogin | POST | 微信登录 | 否 |
|
||||
| GET /api/banners | GET | 获取轮播图 | 否 |
|
||||
| GET /api/categories | GET | 获取分类列表 | 否 |
|
||||
| GET /api/company/info | GET | 获取公司信息 | 否 |
|
||||
| GET /api/service/process | GET | 获取服务流程 | 否 |
|
||||
|
||||
### 3.2 案例接口
|
||||
|
||||
| 接口 | 方法 | 说明 | 认证 |
|
||||
|------|------|------|------|
|
||||
| GET /api/cases | GET | 获取案例列表 | 否 |
|
||||
| GET /api/cases/hot | GET | 获取热门案例 | 否 |
|
||||
| GET /api/cases/:id | GET | 获取案例详情 | 否 |
|
||||
| POST /api/cases/:id/view | POST | 增加浏览量 | 否 |
|
||||
| POST /api/cases/:id/like | POST | 点赞 | 是 |
|
||||
|
||||
### 3.3 用户接口
|
||||
|
||||
| 接口 | 方法 | 说明 | 认证 |
|
||||
|------|------|------|------|
|
||||
| GET /api/user/info | GET | 获取用户信息 | 是 |
|
||||
| PUT /api/user/info | PUT | 更新用户信息 | 是 |
|
||||
| GET /api/user/favorites | GET | 获取收藏列表 | 是 |
|
||||
| POST /api/user/favorites | POST | 添加收藏 | 是 |
|
||||
| DELETE /api/user/favorites/:caseId | DELETE | 取消收藏 | 是 |
|
||||
|
||||
### 3.4 预约接口
|
||||
|
||||
| 接口 | 方法 | 说明 | 认证 |
|
||||
|------|------|------|------|
|
||||
| POST /api/booking | POST | 提交预约 | 否 |
|
||||
| GET /api/user/bookings | GET | 获取我的预约 | 是 |
|
||||
| GET /api/user/bookings/:id | GET | 获取预约详情 | 是 |
|
||||
| PUT /api/user/bookings/:id/cancel | PUT | 取消预约 | 是 |
|
||||
|
||||
### 3.5 文件接口
|
||||
|
||||
| 接口 | 方法 | 说明 | 认证 |
|
||||
|------|------|------|------|
|
||||
| POST /api/upload/image | POST | 上传图片 | 是 |
|
||||
|
||||
## 四、接口详细定义
|
||||
|
||||
### 4.1 微信登录
|
||||
|
||||
```
|
||||
POST /api/auth/wxlogin
|
||||
|
||||
Request:
|
||||
{
|
||||
"code": "微信登录code",
|
||||
"userInfo": {
|
||||
"nickName": "昵称",
|
||||
"avatarUrl": "头像"
|
||||
}
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"token": "jwt_token_xxx",
|
||||
"userInfo": {
|
||||
"id": "1",
|
||||
"nickName": "昵称",
|
||||
"avatar": "头像URL",
|
||||
"phone": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 获取案例列表
|
||||
|
||||
```
|
||||
GET /api/cases?category=leather&page=1&pageSize=10&keyword=
|
||||
|
||||
Request Params:
|
||||
- category: 分类code,可选
|
||||
- page: 页码,默认1
|
||||
- pageSize: 每页数量,默认10
|
||||
- keyword: 搜索关键词,可选
|
||||
|
||||
Response:
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"list": [
|
||||
{
|
||||
"id": "1",
|
||||
"title": "欧式真皮沙发翻新",
|
||||
"category": "leather",
|
||||
"categoryName": "皮沙发",
|
||||
"coverImage": "https://xxx/cover.jpg",
|
||||
"material": "进口头层牛皮",
|
||||
"duration": "7天",
|
||||
"price": "¥3800",
|
||||
"views": 1256,
|
||||
"likes": 89,
|
||||
"createTime": "2026-01-15"
|
||||
}
|
||||
],
|
||||
"total": 100,
|
||||
"page": 1,
|
||||
"pageSize": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 获取案例详情
|
||||
|
||||
```
|
||||
GET /api/cases/:id
|
||||
|
||||
Response:
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"id": "1",
|
||||
"title": "欧式真皮沙发翻新",
|
||||
"category": "leather",
|
||||
"categoryName": "皮沙发",
|
||||
"beforeImages": ["https://xxx/before1.jpg"],
|
||||
"afterImages": ["https://xxx/after1.jpg"],
|
||||
"description": "案例描述...",
|
||||
"material": "进口头层牛皮",
|
||||
"duration": "7天",
|
||||
"price": "¥3800",
|
||||
"views": 1256,
|
||||
"likes": 89,
|
||||
"createTime": "2026-01-15",
|
||||
"isFavorite": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 提交预约
|
||||
|
||||
```
|
||||
POST /api/booking
|
||||
|
||||
Request:
|
||||
{
|
||||
"userName": "张三",
|
||||
"phone": "13800138000",
|
||||
"address": "上海市浦东新区xxx",
|
||||
"sofaType": "leather",
|
||||
"problem": "皮面开裂、褪色",
|
||||
"images": ["https://xxx/1.jpg", "https://xxx/2.jpg"]
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"bookingId": "BK20260126001",
|
||||
"status": "pending",
|
||||
"message": "预约成功,我们会尽快与您联系"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 五、管理后台功能
|
||||
|
||||
### 5.1 功能模块
|
||||
|
||||
| 模块 | 功能 |
|
||||
|------|------|
|
||||
| 仪表盘 | 数据概览、预约统计、案例统计 |
|
||||
| 案例管理 | 案例列表、新增/编辑/删除、上下架 |
|
||||
| 分类管理 | 分类列表、新增/编辑/删除 |
|
||||
| 预约管理 | 预约列表、状态更新、指派处理 |
|
||||
| 用户管理 | 用户列表、状态管理 |
|
||||
| 轮播图管理 | 轮播图列表、新增/编辑/删除 |
|
||||
| 系统设置 | 公司信息、联系方式、服务流程 |
|
||||
|
||||
### 5.2 角色权限
|
||||
|
||||
| 角色 | 权限 |
|
||||
|------|------|
|
||||
| 超级管理员 | 所有权限 |
|
||||
| 运营人员 | 案例管理、轮播图、系统设置 |
|
||||
| 客服人员 | 预约管理、用户管理 |
|
||||
|
||||
## 六、部署架构
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Nginx │
|
||||
│ (反向代理) │
|
||||
└──────┬──────┘
|
||||
│
|
||||
┌───────────────┼───────────────┐
|
||||
│ │ │
|
||||
┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
|
||||
│ 小程序API │ │ 管理后台 │ │ 静态资源 │
|
||||
│ Server │ │ Server │ │ CDN │
|
||||
└──────┬──────┘ └──────┬──────┘ └─────────────┘
|
||||
│ │
|
||||
└───────┬───────┘
|
||||
│
|
||||
┌───────▼───────┐
|
||||
│ MySQL │
|
||||
│ Redis │
|
||||
└───────────────┘
|
||||
```
|
||||
|
||||
## 七、开发计划
|
||||
|
||||
| 阶段 | 时间 | 任务 |
|
||||
|------|------|------|
|
||||
| 第一阶段 | 3天 | 数据库设计、项目搭建、基础接口 |
|
||||
| 第二阶段 | 3天 | 案例模块、分类模块、轮播图 |
|
||||
| 第三阶段 | 2天 | 用户模块、微信登录、收藏功能 |
|
||||
| 第四阶段 | 2天 | 预约模块、文件上传 |
|
||||
| 第五阶段 | 3天 | 管理后台开发 |
|
||||
| 第六阶段 | 2天 | 联调测试、部署上线 |
|
||||
|
||||
**总计:约15个工作日**
|
||||
|
||||
---
|
||||
|
||||
准备好开始后端开发时,告诉我你选择的技术栈(Node.js/Python/Java),我将为你生成完整的后端代码。
|
||||
82
前端/docs/后端对接报告.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# 后端API对接完成报告
|
||||
|
||||
## 🎯 对接状态
|
||||
✅ **已成功对接后端API服务器**
|
||||
- 后端运行在:http://localhost:3000
|
||||
- API文档地址:http://localhost:3000/docs
|
||||
- API基础路径:http://localhost:3000/api
|
||||
|
||||
## 🔧 已完成的配置修改
|
||||
|
||||
### 1. 关闭Mock数据模式
|
||||
- 文件:`utils/config.uts`
|
||||
- 修改:`useMock = false`
|
||||
- 说明:已切换到真实后端API
|
||||
|
||||
### 2. 更新API接口调用
|
||||
- 文件:`api/index.uts`
|
||||
- 修改内容:
|
||||
- ✅ `getCaseList()` - 适配后端分页参数(page/limit)
|
||||
- ✅ `getHotCases()` - 使用案例列表API获取前4条数据
|
||||
- ✅ `getBanners()` - 临时使用固定数据(后端暂无此API)
|
||||
- ✅ `getServiceProcess()` - 改用服务列表API
|
||||
|
||||
### 3. 适配数据格式
|
||||
- 文件:`pages/cases/list.uvue`, `pages/index/index.uvue`
|
||||
- 修改:适应后端返回的数据结构
|
||||
- 后端格式:`{ items: [], total: 0, page: 1, limit: 10 }`
|
||||
- 前端适配:正确解析 `items` 字段
|
||||
|
||||
## 📊 API端点映射
|
||||
|
||||
| 功能 | 前端调用 | 后端端点 | 状态 |
|
||||
|------|----------|----------|------|
|
||||
| 案例列表 | `getCaseList()` | `GET /api/cases` | ✅ 已对接 |
|
||||
| 案例详情 | `getCaseDetail(id)` | `GET /api/cases/:id` | ✅ 已对接 |
|
||||
| 服务列表 | `getActiveServices()` | `GET /api/services/active` | ✅ 已对接 |
|
||||
| 轮播图 | `getBanners()` | - | ⚠️ 临时使用固定数据 |
|
||||
| 用户相关 | - | `GET /api/users/*` | ✅ 后端已准备 |
|
||||
| 预约管理 | `submitBooking()` | `POST /api/booking` | 🔄 需要确认端点 |
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
### 已验证的API
|
||||
- ✅ `GET /api/cases` - 返回空列表(正常,暂无数据)
|
||||
- ✅ `GET /api/services` - 返回空列表(正常,暂无数据)
|
||||
- ✅ API文档页面 - 可正常访问
|
||||
|
||||
### 临时解决方案
|
||||
1. **轮播图**:使用硬编码数据,后续可添加后端API
|
||||
2. **空数据处理**:前端已正确处理空列表情况
|
||||
|
||||
## 📝 下一步工作
|
||||
|
||||
### 推荐优先级
|
||||
1. **高优先级**
|
||||
- 为后端添加示例数据(案例、服务)
|
||||
- 验证预约提交API端点
|
||||
- 添加轮播图管理API
|
||||
|
||||
2. **中优先级**
|
||||
- 添加用户认证功能测试
|
||||
- 完善错误处理机制
|
||||
- 添加图片上传功能
|
||||
|
||||
3. **低优先级**
|
||||
- API响应缓存机制
|
||||
- 离线数据支持
|
||||
|
||||
## 🚀 启动应用测试
|
||||
|
||||
现在您可以:
|
||||
1. 确保后端服务运行在 `http://localhost:3000`
|
||||
2. 启动前端应用(uni-app)
|
||||
3. 查看控制台API调用日志
|
||||
4. 验证数据正确显示(目前为空列表)
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如遇到问题,请检查:
|
||||
- 后端服务是否正常运行
|
||||
- 网络连接是否正常
|
||||
- 控制台是否有错误信息
|
||||
20
前端/index.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script>
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||
CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
</script>
|
||||
<title></title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/main"></script>
|
||||
</body>
|
||||
</html>
|
||||
9
前端/main.uts
Normal file
@@ -0,0 +1,9 @@
|
||||
import App from './App.uvue'
|
||||
|
||||
import { createSSRApp } from 'vue'
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
return {
|
||||
app
|
||||
}
|
||||
}
|
||||
64
前端/manifest.json
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"name": "优艺家沙发翻新",
|
||||
"appid": "",
|
||||
"description": "",
|
||||
"versionName": "1.0.0",
|
||||
"versionCode": "100",
|
||||
"uni-app-x": {},
|
||||
/* 快应用特有相关 */
|
||||
"quickapp": {},
|
||||
/* 小程序特有相关 */
|
||||
"mp-weixin": {
|
||||
"appid": "wx89f1cd89fbc55f54",
|
||||
"setting": {
|
||||
"urlCheck": false
|
||||
},
|
||||
"usingComponents": true
|
||||
},
|
||||
"mp-alipay": {
|
||||
"usingComponents": true
|
||||
},
|
||||
"mp-baidu": {
|
||||
"usingComponents": true
|
||||
},
|
||||
"mp-toutiao": {
|
||||
"usingComponents": true
|
||||
},
|
||||
"uniStatistics": {
|
||||
"enable": false
|
||||
},
|
||||
"vueVersion": "3",
|
||||
"app": {
|
||||
"distribute": {
|
||||
"icons": {
|
||||
"android": {
|
||||
"hdpi": "",
|
||||
"xhdpi": "",
|
||||
"xxhdpi": "",
|
||||
"xxxhdpi": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"app-android": {
|
||||
"distribute": {
|
||||
"modules": {},
|
||||
"icons": {
|
||||
"hdpi": "",
|
||||
"xhdpi": "",
|
||||
"xxhdpi": "",
|
||||
"xxxhdpi": ""
|
||||
},
|
||||
"splashScreens": {
|
||||
"default": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"app-ios": {
|
||||
"distribute": {
|
||||
"modules": {},
|
||||
"icons": {},
|
||||
"splashScreens": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
87
前端/pages.json
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/index/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "优艺家沙发翻新",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/cases/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "案例展示"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/cases/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "案例详情",
|
||||
"navigationBarBackgroundColor": "#ffffff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/service/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "服务介绍"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/about/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "关于我们"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/booking/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "预约咨询"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "优艺家沙发翻新",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"backgroundColor": "#f5f5f5"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#999999",
|
||||
"selectedColor": "#D4A574",
|
||||
"borderStyle": "black",
|
||||
"backgroundColor": "#ffffff",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/index/index",
|
||||
"iconPath": "static/icons/home.png",
|
||||
"selectedIconPath": "static/icons/home-active.png",
|
||||
"text": "首页"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/cases/list",
|
||||
"iconPath": "static/icons/cases.png",
|
||||
"selectedIconPath": "static/icons/cases-active.png",
|
||||
"text": "案例"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/service/index",
|
||||
"iconPath": "static/icons/service.png",
|
||||
"selectedIconPath": "static/icons/service-active.png",
|
||||
"text": "服务"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/user/index",
|
||||
"iconPath": "static/icons/user.png",
|
||||
"selectedIconPath": "static/icons/user-active.png",
|
||||
"text": "我的"
|
||||
}
|
||||
]
|
||||
},
|
||||
"uniIdRouter": {}
|
||||
}
|
||||
362
前端/pages/about/index.uvue
Normal file
@@ -0,0 +1,362 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<scroll-view class="page-scroll" scroll-y>
|
||||
<!-- 公司Logo和名称 -->
|
||||
<view class="header-section">
|
||||
<view class="company-logo">
|
||||
<text class="logo-text">优艺家</text>
|
||||
</view>
|
||||
<text class="company-name">{{ companyInfo.name }}</text>
|
||||
<text class="company-slogan">{{ companyInfo.slogan }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 公司介绍 -->
|
||||
<view class="section">
|
||||
<section-header title="公司简介"></section-header>
|
||||
<view class="intro-card">
|
||||
<text class="intro-text">{{ companyInfo.description }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 我们的优势 -->
|
||||
<view class="section">
|
||||
<section-header title="我们的优势"></section-header>
|
||||
<view class="features-grid">
|
||||
<view class="feature-item" v-for="item in companyInfo.features" :key="item.title">
|
||||
<view class="feature-icon-bg">
|
||||
<text class="feature-icon">✓</text>
|
||||
</view>
|
||||
<text class="feature-title">{{ item.title }}</text>
|
||||
<text class="feature-desc">{{ item.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 联系方式 -->
|
||||
<view class="section">
|
||||
<section-header title="联系我们"></section-header>
|
||||
<view class="contact-card">
|
||||
<view class="contact-item" @click="callPhone">
|
||||
<view class="contact-icon-bg">
|
||||
<text class="contact-icon">📞</text>
|
||||
</view>
|
||||
<view class="contact-info">
|
||||
<text class="contact-label">客服电话</text>
|
||||
<text class="contact-value">{{ companyInfo.phone }}</text>
|
||||
</view>
|
||||
<text class="contact-arrow">›</text>
|
||||
</view>
|
||||
|
||||
<view class="contact-item" @click="copyWechat">
|
||||
<view class="contact-icon-bg">
|
||||
<text class="contact-icon">💬</text>
|
||||
</view>
|
||||
<view class="contact-info">
|
||||
<text class="contact-label">微信号</text>
|
||||
<text class="contact-value">{{ companyInfo.wechat }}</text>
|
||||
</view>
|
||||
<text class="contact-arrow">›</text>
|
||||
</view>
|
||||
|
||||
<view class="contact-item" @click="openLocation">
|
||||
<view class="contact-icon-bg">
|
||||
<text class="contact-icon">📍</text>
|
||||
</view>
|
||||
<view class="contact-info">
|
||||
<text class="contact-label">公司地址</text>
|
||||
<text class="contact-value">{{ companyInfo.address }}</text>
|
||||
</view>
|
||||
<text class="contact-arrow">›</text>
|
||||
</view>
|
||||
|
||||
<view class="contact-item">
|
||||
<view class="contact-icon-bg">
|
||||
<text class="contact-icon">🕐</text>
|
||||
</view>
|
||||
<view class="contact-info">
|
||||
<text class="contact-label">营业时间</text>
|
||||
<text class="contact-value">{{ companyInfo.workTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部间距 -->
|
||||
<view class="bottom-space"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { getCompanyInfo } from '@/api/index.uts'
|
||||
|
||||
// 特色类型
|
||||
type FeatureItem = {
|
||||
title : string
|
||||
desc : string
|
||||
}
|
||||
|
||||
// 公司信息类型
|
||||
type CompanyInfo = {
|
||||
name : string
|
||||
slogan : string
|
||||
description : string
|
||||
phone : string
|
||||
wechat : string
|
||||
address : string
|
||||
workTime : string
|
||||
features : FeatureItem[]
|
||||
}
|
||||
|
||||
// 公司信息
|
||||
const companyInfo = ref<CompanyInfo>({
|
||||
name: '优艺家沙发翻新',
|
||||
slogan: '让旧沙发焕发新生',
|
||||
description: '',
|
||||
phone: '400-888-8888',
|
||||
wechat: 'youyijia2026',
|
||||
address: '',
|
||||
workTime: '',
|
||||
features: []
|
||||
})
|
||||
|
||||
// 获取公司信息
|
||||
const fetchCompanyInfo = async () => {
|
||||
try {
|
||||
const res = await getCompanyInfo()
|
||||
const data = res.data as UTSJSONObject
|
||||
|
||||
const featuresData = data['features'] as UTSJSONObject[]
|
||||
const features = featuresData.map((item) : FeatureItem => {
|
||||
return {
|
||||
title: item['title'] as string,
|
||||
desc: item['desc'] as string
|
||||
} as FeatureItem
|
||||
})
|
||||
|
||||
companyInfo.value = {
|
||||
name: data['name'] as string,
|
||||
slogan: data['slogan'] as string,
|
||||
description: data['description'] as string,
|
||||
phone: data['phone'] as string,
|
||||
wechat: data['wechat'] as string,
|
||||
address: data['address'] as string,
|
||||
workTime: data['workTime'] as string,
|
||||
features: features
|
||||
} as CompanyInfo
|
||||
} catch (e) {
|
||||
console.error('获取公司信息失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 拨打电话
|
||||
const callPhone = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: companyInfo.value.phone,
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
title: '拨打电话失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 复制微信号
|
||||
const copyWechat = () => {
|
||||
uni.setClipboardData({
|
||||
data: companyInfo.value.wechat,
|
||||
success: () => {
|
||||
uni.showToast({
|
||||
title: '微信号已复制',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 打开位置
|
||||
const openLocation = () => {
|
||||
uni.openLocation({
|
||||
latitude: 31.2,
|
||||
longitude: 121.5,
|
||||
name: companyInfo.value.name,
|
||||
address: companyInfo.value.address,
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
title: '无法打开地图',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
fetchCompanyInfo()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.page {
|
||||
flex: 1;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.page-scroll {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 头部区域 */
|
||||
.header-section {
|
||||
background: linear-gradient(135deg, #D4A574 0%, #B8895A 100%);
|
||||
padding: 60rpx 32rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 50%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #D4A574;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.company-slogan {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
/* 通用section */
|
||||
.section {
|
||||
padding: 0 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
/* 公司介绍 */
|
||||
.intro-card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.intro-text {
|
||||
font-size: 28rpx;
|
||||
color: #606266;
|
||||
line-height: 48rpx;
|
||||
}
|
||||
|
||||
/* 优势 */
|
||||
.features-grid {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 16rpx;
|
||||
}
|
||||
|
||||
.feature-item {
|
||||
width: 50%;
|
||||
padding: 24rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.feature-icon-bg {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background-color: #D4A574;
|
||||
border-radius: 50%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 40rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.feature-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.feature-desc {
|
||||
font-size: 24rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 联系方式 */
|
||||
.contact-card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.contact-item {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 32rpx;
|
||||
border-bottom-width: 1rpx;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #EBEEF5;
|
||||
}
|
||||
|
||||
.contact-item:last-child {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
.contact-icon-bg {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
background-color: #FDF6EE;
|
||||
border-radius: 50%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.contact-icon {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.contact-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.contact-label {
|
||||
font-size: 24rpx;
|
||||
color: #909399;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.contact-value {
|
||||
font-size: 30rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.contact-arrow {
|
||||
font-size: 36rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 底部间距 */
|
||||
.bottom-space {
|
||||
height: 60rpx;
|
||||
}
|
||||
</style>
|
||||
489
前端/pages/booking/index.uvue
Normal file
@@ -0,0 +1,489 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<scroll-view class="page-scroll" scroll-y>
|
||||
<!-- 表单头部 -->
|
||||
<view class="header-section">
|
||||
<text class="header-title">预约翻新服务</text>
|
||||
<text class="header-desc">填写以下信息,我们会尽快与您联系</text>
|
||||
</view>
|
||||
|
||||
<!-- 表单内容 -->
|
||||
<view class="form-section">
|
||||
<!-- 姓名 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">
|
||||
<text class="required">*</text>
|
||||
您的姓名
|
||||
</text>
|
||||
<input
|
||||
class="form-input"
|
||||
type="text"
|
||||
v-model="formData.userName"
|
||||
placeholder="请输入您的姓名"
|
||||
placeholder-class="placeholder"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 电话 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">
|
||||
<text class="required">*</text>
|
||||
联系电话
|
||||
</text>
|
||||
<input
|
||||
class="form-input"
|
||||
type="number"
|
||||
v-model="formData.phone"
|
||||
placeholder="请输入您的手机号"
|
||||
placeholder-class="placeholder"
|
||||
maxlength="11"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 地址 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">
|
||||
<text class="required">*</text>
|
||||
您的地址
|
||||
</text>
|
||||
<input
|
||||
class="form-input"
|
||||
type="text"
|
||||
v-model="formData.address"
|
||||
placeholder="请输入详细地址"
|
||||
placeholder-class="placeholder"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 沙发类型 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">沙发类型</text>
|
||||
<view class="type-grid">
|
||||
<view
|
||||
class="type-item"
|
||||
:class="{ 'type-active': formData.sofaType == item.id }"
|
||||
v-for="item in sofaTypes"
|
||||
:key="item.id"
|
||||
@click="selectSofaType(item.id)"
|
||||
>
|
||||
<text
|
||||
class="type-text"
|
||||
:class="{ 'type-text-active': formData.sofaType == item.id }"
|
||||
>{{ item.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 问题描述 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">问题描述</text>
|
||||
<textarea
|
||||
class="form-textarea"
|
||||
v-model="formData.problem"
|
||||
placeholder="请描述沙发的问题(如:皮面开裂、褪色、塌陷等)"
|
||||
placeholder-class="placeholder"
|
||||
maxlength="500"
|
||||
></textarea>
|
||||
<text class="textarea-count">{{ formData.problem.length }}/500</text>
|
||||
</view>
|
||||
|
||||
<!-- 上传图片 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">上传图片(可选)</text>
|
||||
<view class="upload-grid">
|
||||
<view
|
||||
class="upload-item"
|
||||
v-for="(item, index) in imageList"
|
||||
:key="index"
|
||||
>
|
||||
<image class="upload-image" :src="item" mode="aspectFill"></image>
|
||||
<view class="upload-delete" @click="deleteImage(index)">
|
||||
<text class="delete-icon">×</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="upload-add" v-if="imageList.length < 9" @click="chooseImage">
|
||||
<text class="add-icon">+</text>
|
||||
<text class="add-text">添加图片</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="upload-tip">最多可上传9张图片</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部间距 -->
|
||||
<view class="bottom-space"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<view class="submit-bar">
|
||||
<view class="submit-btn" @click="handleSubmit">
|
||||
<text class="submit-text">提交预约</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { submitBooking } from '@/api/index.uts'
|
||||
import { SOFA_CATEGORIES } from '@/utils/config.uts'
|
||||
|
||||
// 表单类型
|
||||
type FormData = {
|
||||
userName : string
|
||||
phone : string
|
||||
address : string
|
||||
sofaType : string
|
||||
problem : string
|
||||
}
|
||||
|
||||
// 沙发类型
|
||||
type SofaType = {
|
||||
id : string
|
||||
name : string
|
||||
}
|
||||
|
||||
// 表单数据
|
||||
const formData = ref<FormData>({
|
||||
userName: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
sofaType: '',
|
||||
problem: ''
|
||||
})
|
||||
|
||||
// 图片列表
|
||||
const imageList = ref<string[]>([])
|
||||
|
||||
// 沙发类型列表
|
||||
const sofaTypes = ref<SofaType[]>([])
|
||||
|
||||
// 提交中
|
||||
const submitting = ref(false)
|
||||
|
||||
// 初始化沙发类型
|
||||
const initSofaTypes = () => {
|
||||
sofaTypes.value = SOFA_CATEGORIES.filter((item) : boolean => item.id != 'all').map((item) : SofaType => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name
|
||||
} as SofaType
|
||||
})
|
||||
}
|
||||
|
||||
// 选择沙发类型
|
||||
const selectSofaType = (id : string) => {
|
||||
formData.value.sofaType = id
|
||||
}
|
||||
|
||||
// 选择图片
|
||||
const chooseImage = () => {
|
||||
uni.chooseImage({
|
||||
count: 9 - imageList.value.length,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
imageList.value = [...imageList.value, ...res.tempFilePaths]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 删除图片
|
||||
const deleteImage = (index : number) => {
|
||||
imageList.value.splice(index, 1)
|
||||
}
|
||||
|
||||
// 验证表单
|
||||
const validateForm = () : boolean => {
|
||||
if (formData.value.userName.trim() == '') {
|
||||
uni.showToast({
|
||||
title: '请输入您的姓名',
|
||||
icon: 'none'
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
const phone = formData.value.phone.trim()
|
||||
if (phone == '') {
|
||||
uni.showToast({
|
||||
title: '请输入联系电话',
|
||||
icon: 'none'
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
// 简单验证手机号
|
||||
if (phone.length != 11) {
|
||||
uni.showToast({
|
||||
title: '请输入正确的手机号',
|
||||
icon: 'none'
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
if (formData.value.address.trim() == '') {
|
||||
uni.showToast({
|
||||
title: '请输入您的地址',
|
||||
icon: 'none'
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 提交预约
|
||||
const handleSubmit = async () => {
|
||||
if (!validateForm()) return
|
||||
if (submitting.value) return
|
||||
|
||||
submitting.value = true
|
||||
|
||||
try {
|
||||
const data = {
|
||||
userName: formData.value.userName,
|
||||
phone: formData.value.phone,
|
||||
address: formData.value.address,
|
||||
sofaType: formData.value.sofaType,
|
||||
problem: formData.value.problem,
|
||||
images: imageList.value
|
||||
} as UTSJSONObject
|
||||
|
||||
const res = await submitBooking(data)
|
||||
const result = res.data as UTSJSONObject
|
||||
|
||||
uni.showModal({
|
||||
title: '预约成功',
|
||||
content: result['message'] as string,
|
||||
showCancel: false,
|
||||
success: () => {
|
||||
// 返回上一页或首页
|
||||
uni.navigateBack({
|
||||
fail: () => {
|
||||
uni.switchTab({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('提交预约失败', e)
|
||||
uni.showToast({
|
||||
title: '提交失败,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
submitting.value = false
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
initSofaTypes()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.page {
|
||||
flex: 1;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.page-scroll {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 头部 */
|
||||
.header-section {
|
||||
background: linear-gradient(135deg, #D4A574 0%, #B8895A 100%);
|
||||
padding: 48rpx 32rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.header-desc {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
/* 表单区域 */
|
||||
.form-section {
|
||||
background-color: #ffffff;
|
||||
margin: 24rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.required {
|
||||
color: #F56C6C;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
font-size: 28rpx;
|
||||
height:40px;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: #C0C4CC;
|
||||
}
|
||||
|
||||
/* 沙发类型选择 */
|
||||
.type-grid {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.type-item {
|
||||
padding: 16rpx 32rpx;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 8rpx;
|
||||
margin-right: 16rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.type-active {
|
||||
background-color: #D4A574;
|
||||
}
|
||||
|
||||
.type-text {
|
||||
font-size: 26rpx;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.type-text-active {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 文本域 */
|
||||
.form-textarea {
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
font-size: 28rpx;
|
||||
height: 200rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.textarea-count {
|
||||
font-size: 24rpx;
|
||||
color: #909399;
|
||||
text-align: right;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
/* 图片上传 */
|
||||
.upload-grid {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.upload-item {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin-right: 16rpx;
|
||||
margin-bottom: 16rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.upload-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.upload-delete {
|
||||
position: absolute;
|
||||
top: -16rpx;
|
||||
right: -16rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background-color: #F56C6C;
|
||||
border-radius: 50%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.upload-add {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 12rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-width: 2rpx;
|
||||
border-style: dashed;
|
||||
border-color: #DCDFE6;
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
font-size: 48rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.add-text {
|
||||
font-size: 24rpx;
|
||||
color: #909399;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.upload-tip {
|
||||
font-size: 24rpx;
|
||||
color: #909399;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
/* 底部间距 */
|
||||
.bottom-space {
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
/* 提交按钮 */
|
||||
.submit-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #ffffff;
|
||||
padding: 24rpx 32rpx;
|
||||
padding-bottom: 48rpx;
|
||||
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
background-color: #D4A574;
|
||||
border-radius: 999rpx;
|
||||
padding: 28rpx 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.submit-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
438
前端/pages/cases/detail.uvue
Normal file
@@ -0,0 +1,438 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<scroll-view class="page-scroll" scroll-y>
|
||||
<!-- 图片画廊 -->
|
||||
<view class="gallery-section">
|
||||
<swiper
|
||||
class="gallery-swiper"
|
||||
circular
|
||||
indicator-dots
|
||||
indicator-color="rgba(255,255,255,0.5)"
|
||||
indicator-active-color="#ffffff"
|
||||
>
|
||||
<swiper-item v-for="(item, index) in caseDetail.afterImages" :key="index">
|
||||
<image
|
||||
class="gallery-image"
|
||||
:src="item"
|
||||
mode="aspectFill"
|
||||
@click="previewImages(index)"
|
||||
></image>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<view class="gallery-tag">
|
||||
<text class="gallery-tag-text">{{ caseDetail.categoryName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<view class="info-section">
|
||||
<text class="case-title">{{ caseDetail.title }}</text>
|
||||
<view class="case-meta">
|
||||
<text class="case-price">{{ caseDetail.price }}</text>
|
||||
<view class="case-stats">
|
||||
<text class="stat-text">👁 {{ caseDetail.views }}</text>
|
||||
<text class="stat-text">❤ {{ caseDetail.likes }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 翻新前后对比 -->
|
||||
<view class="compare-section">
|
||||
<before-after
|
||||
:beforeImage="caseDetail.beforeImages[0] || ''"
|
||||
:afterImage="caseDetail.afterImages[0] || ''"
|
||||
:showTitle="true"
|
||||
></before-after>
|
||||
</view>
|
||||
|
||||
<!-- 详细信息 -->
|
||||
<view class="detail-section">
|
||||
<view class="detail-header">
|
||||
<text class="detail-title">翻新详情</text>
|
||||
</view>
|
||||
|
||||
<view class="detail-list">
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">使用材质</text>
|
||||
<text class="detail-value">{{ caseDetail.material }}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">翻新工期</text>
|
||||
<text class="detail-value">{{ caseDetail.duration }}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">完成日期</text>
|
||||
<text class="detail-value">{{ caseDetail.createTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="detail-desc">
|
||||
<text class="desc-title">案例描述</text>
|
||||
<text class="desc-content">{{ caseDetail.description }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部间距 -->
|
||||
<view class="bottom-space"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="bar-left">
|
||||
<view class="bar-btn" @click="handleFavorite">
|
||||
<text class="bar-icon">{{ isFavorite ? '❤️' : '🤍' }}</text>
|
||||
<text class="bar-label">收藏</text>
|
||||
</view>
|
||||
<view class="bar-btn" @click="handleShare">
|
||||
<text class="bar-icon">📤</text>
|
||||
<text class="bar-label">分享</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bar-right">
|
||||
<view class="contact-btn" @click="handleContact">
|
||||
<text class="contact-text">在线咨询</text>
|
||||
</view>
|
||||
<view class="booking-btn" @click="goToBooking">
|
||||
<text class="booking-text">立即预约</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { getCaseDetail } from '@/api/index.uts'
|
||||
|
||||
// 案例详情类型
|
||||
type CaseDetail = {
|
||||
id : string
|
||||
title : string
|
||||
category : string
|
||||
categoryName : string
|
||||
beforeImages : string[]
|
||||
afterImages : string[]
|
||||
description : string
|
||||
material : string
|
||||
duration : string
|
||||
price : string
|
||||
views : number
|
||||
likes : number
|
||||
createTime : string
|
||||
}
|
||||
|
||||
// 案例ID
|
||||
const caseId = ref('')
|
||||
|
||||
// 案例详情
|
||||
const caseDetail = ref<CaseDetail>({
|
||||
id: '',
|
||||
title: '',
|
||||
category: '',
|
||||
categoryName: '',
|
||||
beforeImages: [],
|
||||
afterImages: [],
|
||||
description: '',
|
||||
material: '',
|
||||
duration: '',
|
||||
price: '',
|
||||
views: 0,
|
||||
likes: 0,
|
||||
createTime: ''
|
||||
})
|
||||
|
||||
// 是否收藏
|
||||
const isFavorite = ref(false)
|
||||
|
||||
// 获取案例详情
|
||||
const fetchCaseDetail = async () => {
|
||||
try {
|
||||
const res = await getCaseDetail(caseId.value)
|
||||
const data = res.data as UTSJSONObject
|
||||
|
||||
caseDetail.value = {
|
||||
id: data['id'] as string,
|
||||
title: data['title'] as string,
|
||||
category: data['category'] as string,
|
||||
categoryName: data['categoryName'] as string,
|
||||
beforeImages: data['beforeImages'] as string[],
|
||||
afterImages: data['afterImages'] as string[],
|
||||
description: data['description'] as string,
|
||||
material: data['material'] as string,
|
||||
duration: data['duration'] as string,
|
||||
price: data['price'] as string,
|
||||
views: data['views'] as number,
|
||||
likes: data['likes'] as number,
|
||||
createTime: data['createTime'] as string
|
||||
} as CaseDetail
|
||||
|
||||
// 更新标题
|
||||
uni.setNavigationBarTitle({
|
||||
title: caseDetail.value.title
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('获取案例详情失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 预览图片
|
||||
const previewImages = (index : number) => {
|
||||
uni.previewImage({
|
||||
current: index,
|
||||
urls: caseDetail.value.afterImages
|
||||
})
|
||||
}
|
||||
|
||||
// 收藏
|
||||
const handleFavorite = () => {
|
||||
isFavorite.value = !isFavorite.value
|
||||
uni.showToast({
|
||||
title: isFavorite.value ? '已收藏' : '已取消收藏',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 分享
|
||||
const handleShare = () => {
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序触发分享
|
||||
// #endif
|
||||
uni.showToast({
|
||||
title: '分享功能开发中',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 在线咨询
|
||||
const handleContact = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: '400-888-8888',
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
title: '拨打电话失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 预约
|
||||
const goToBooking = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/booking/index'
|
||||
})
|
||||
}
|
||||
|
||||
onLoad((options : OnLoadOptions) => {
|
||||
caseId.value = options['id'] ?? ''
|
||||
if (caseId.value != '') {
|
||||
fetchCaseDetail()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.page {
|
||||
flex: 1;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.page-scroll {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 图片画廊 */
|
||||
.gallery-section {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.gallery-swiper {
|
||||
height: 500rpx;
|
||||
}
|
||||
|
||||
.gallery-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.gallery-tag {
|
||||
position: absolute;
|
||||
top: 24rpx;
|
||||
left: 24rpx;
|
||||
background-color: rgba(212, 165, 116, 0.9);
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.gallery-tag-text {
|
||||
font-size: 24rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 基本信息 */
|
||||
.info-section {
|
||||
background-color: #ffffff;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.case-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.case-meta {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.case-price {
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
color: #D4A574;
|
||||
}
|
||||
|
||||
.case-stats {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.stat-text {
|
||||
font-size: 26rpx;
|
||||
color: #909399;
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
|
||||
/* 对比区域 */
|
||||
.compare-section {
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
/* 详细信息 */
|
||||
.detail-section {
|
||||
background-color: #ffffff;
|
||||
margin: 24rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.detail-list {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 16rpx 0;
|
||||
border-bottom-width: 1rpx;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #EBEEF5;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-size: 28rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.detail-desc {
|
||||
padding-top: 16rpx;
|
||||
}
|
||||
|
||||
.desc-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.desc-content {
|
||||
font-size: 28rpx;
|
||||
color: #606266;
|
||||
line-height: 48rpx;
|
||||
}
|
||||
|
||||
/* 底部间距 */
|
||||
.bottom-space {
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
/* 底部操作栏 */
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: #ffffff;
|
||||
padding: 16rpx 24rpx;
|
||||
padding-bottom: 32rpx;
|
||||
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.bar-left {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.bar-btn {
|
||||
align-items: center;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.bar-icon {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
|
||||
.bar-label {
|
||||
font-size: 22rpx;
|
||||
color: #606266;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.bar-right {
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.contact-btn {
|
||||
background-color: #f5f5f5;
|
||||
padding: 20rpx 40rpx;
|
||||
border-radius: 999rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.contact-text {
|
||||
font-size: 28rpx;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.booking-btn {
|
||||
background-color: #D4A574;
|
||||
padding: 20rpx 48rpx;
|
||||
border-radius: 999rpx;
|
||||
}
|
||||
|
||||
.booking-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
272
前端/pages/cases/list.uvue
Normal file
@@ -0,0 +1,272 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<!-- 分类筛选 -->
|
||||
<view class="category-bar">
|
||||
<scroll-view class="category-scroll" scroll-x>
|
||||
<view class="category-list">
|
||||
<view
|
||||
class="category-item"
|
||||
:class="{ 'category-active': currentCategory == item.id }"
|
||||
v-for="item in categories"
|
||||
:key="item.id"
|
||||
@click="selectCategory(item.id)"
|
||||
>
|
||||
<text
|
||||
class="category-text"
|
||||
:class="{ 'category-text-active': currentCategory == item.id }"
|
||||
>{{ item.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 案例列表 -->
|
||||
<scroll-view
|
||||
class="case-scroll"
|
||||
scroll-y
|
||||
@scrolltolower="loadMore"
|
||||
>
|
||||
<view class="case-list">
|
||||
<case-card
|
||||
v-for="item in caseList"
|
||||
:key="item.id"
|
||||
:caseData="item"
|
||||
@click="goToDetail"
|
||||
></case-card>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view class="load-status">
|
||||
<text class="load-text" v-if="loading">加载中...</text>
|
||||
<text class="load-text" v-else-if="noMore">没有更多了</text>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-state" v-if="!loading && caseList.length == 0">
|
||||
<text class="empty-icon">📭</text>
|
||||
<text class="empty-text">暂无相关案例</text>
|
||||
</view>
|
||||
|
||||
<!-- 底部间距 -->
|
||||
<view class="bottom-space"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { getCaseList } from '@/api/index.uts'
|
||||
import { SOFA_CATEGORIES, PAGE_SIZE } from '@/utils/config.uts'
|
||||
|
||||
// 分类类型
|
||||
type CategoryItem = {
|
||||
id : string
|
||||
name : string
|
||||
}
|
||||
|
||||
// 案例类型
|
||||
type CaseItem = {
|
||||
id : string
|
||||
title : string
|
||||
category : string
|
||||
categoryName : string
|
||||
coverImage : string
|
||||
material : string
|
||||
duration : string
|
||||
price : string
|
||||
views : number
|
||||
likes : number
|
||||
}
|
||||
|
||||
// 分类列表
|
||||
const categories = ref<CategoryItem[]>([])
|
||||
|
||||
// 当前分类
|
||||
const currentCategory = ref('all')
|
||||
|
||||
// 案例列表
|
||||
const caseList = ref<CaseItem[]>([])
|
||||
|
||||
// 分页
|
||||
const page = ref(1)
|
||||
const total = ref(0)
|
||||
|
||||
// 加载状态
|
||||
const loading = ref(false)
|
||||
const noMore = ref(false)
|
||||
|
||||
// 初始化分类
|
||||
const initCategories = () => {
|
||||
categories.value = SOFA_CATEGORIES.map((item) : CategoryItem => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name
|
||||
} as CategoryItem
|
||||
})
|
||||
}
|
||||
|
||||
// 选择分类
|
||||
const selectCategory = (id : string) => {
|
||||
if (currentCategory.value == id) return
|
||||
currentCategory.value = id
|
||||
page.value = 1
|
||||
caseList.value = []
|
||||
noMore.value = false
|
||||
fetchCaseList()
|
||||
}
|
||||
|
||||
// 获取案例列表
|
||||
const fetchCaseList = async () => {
|
||||
if (loading.value || noMore.value) return
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const params = {
|
||||
category: currentCategory.value,
|
||||
page: page.value,
|
||||
pageSize: PAGE_SIZE
|
||||
} as UTSJSONObject
|
||||
|
||||
const res = await getCaseList(params)
|
||||
const data = res.data as UTSJSONObject
|
||||
// 适应后端返回格式:{ items: [], total: 0, page: 1, limit: 10 }
|
||||
const list = data['items'] as UTSJSONObject[] || []
|
||||
total.value = data['total'] as number || 0
|
||||
|
||||
const newList = list.map((item) : CaseItem => {
|
||||
return {
|
||||
id: item['id'] as string,
|
||||
title: item['title'] as string,
|
||||
category: item['category'] as string,
|
||||
categoryName: item['categoryName'] as string,
|
||||
coverImage: item['coverImage'] as string,
|
||||
material: item['material'] as string,
|
||||
duration: item['duration'] as string,
|
||||
price: item['price'] as string,
|
||||
views: item['views'] as number,
|
||||
likes: item['likes'] as number
|
||||
} as CaseItem
|
||||
})
|
||||
|
||||
if (page.value == 1) {
|
||||
caseList.value = newList
|
||||
} else {
|
||||
caseList.value = [...caseList.value, ...newList]
|
||||
}
|
||||
|
||||
if (caseList.value.length >= total.value) {
|
||||
noMore.value = true
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('获取案例列表失败', e)
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
// 加载更多
|
||||
const loadMore = () => {
|
||||
if (!noMore.value && !loading.value) {
|
||||
page.value++
|
||||
fetchCaseList()
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转详情
|
||||
const goToDetail = (id : string) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/cases/detail?id=${id}`
|
||||
})
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
initCategories()
|
||||
fetchCaseList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.page {
|
||||
flex: 1;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 分类栏 */
|
||||
.category-bar {
|
||||
background-color: #ffffff;
|
||||
padding: 24rpx 0;
|
||||
border-bottom-width: 1rpx;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #EBEEF5;
|
||||
}
|
||||
|
||||
.category-scroll {
|
||||
flex-direction: row;
|
||||
}
|
||||
.category-scroll ::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.category-list {
|
||||
flex-direction: row;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
padding: 16rpx 32rpx;
|
||||
margin-right: 16rpx;
|
||||
border-radius: 999rpx;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.category-active {
|
||||
background-color: #D4A574;
|
||||
}
|
||||
|
||||
.category-text {
|
||||
font-size: 28rpx;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.category-text-active {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 案例列表 */
|
||||
.case-scroll {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.case-list {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.load-status {
|
||||
padding: 32rpx 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.load-text {
|
||||
font-size: 26rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
padding: 120rpx 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 80rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 底部间距 */
|
||||
.bottom-space {
|
||||
height: 120rpx;
|
||||
}
|
||||
</style>
|
||||
418
前端/pages/index/index.uvue
Normal file
@@ -0,0 +1,418 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<nav-bar title="优艺家沙发翻新"></nav-bar>
|
||||
|
||||
<scroll-view class="page-content" scroll-y>
|
||||
<!-- 轮播图 -->
|
||||
<view class="banner-section">
|
||||
<swiper
|
||||
class="banner-swiper"
|
||||
circular
|
||||
autoplay
|
||||
indicator-dots
|
||||
indicator-color="rgba(255,255,255,0.5)"
|
||||
indicator-active-color="#ffffff"
|
||||
>
|
||||
<swiper-item v-for="item in bannerList" :key="item.id">
|
||||
<image
|
||||
class="banner-image"
|
||||
:src="item.image"
|
||||
mode="aspectFill"
|
||||
@click="handleBannerClick(item)"
|
||||
></image>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
|
||||
<!-- 服务入口 -->
|
||||
<view class="service-section">
|
||||
<view class="service-grid">
|
||||
<service-card
|
||||
v-for="item in serviceTypes"
|
||||
:key="item.id"
|
||||
:id="item.id"
|
||||
:name="item.name"
|
||||
:icon="item.icon"
|
||||
@click="handleServiceClick"
|
||||
></service-card>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 公司优势 -->
|
||||
<view class="advantage-section">
|
||||
<view class="advantage-list">
|
||||
<view class="advantage-item" v-for="item in advantages" :key="item.title">
|
||||
<text class="advantage-icon">{{ item.icon }}</text>
|
||||
<view class="advantage-info">
|
||||
<text class="advantage-title">{{ item.title }}</text>
|
||||
<text class="advantage-desc">{{ item.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 热门案例 -->
|
||||
<view class="case-section">
|
||||
<section-header
|
||||
title="热门案例"
|
||||
:showMore="true"
|
||||
@more="goToCaseList"
|
||||
></section-header>
|
||||
|
||||
<view class="case-list">
|
||||
<case-card
|
||||
v-for="item in hotCases"
|
||||
:key="item.id"
|
||||
:caseData="item"
|
||||
@click="goToCaseDetail"
|
||||
></case-card>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 预约入口 -->
|
||||
<view class="booking-section" @click="goToBooking">
|
||||
<view class="booking-content">
|
||||
<view class="booking-left">
|
||||
<text class="booking-title">免费上门评估</text>
|
||||
<text class="booking-desc">专业师傅免费上门,为您的沙发量身定制翻新方案</text>
|
||||
</view>
|
||||
<view class="booking-btn">
|
||||
<text class="booking-btn-text">立即预约</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部间距 -->
|
||||
<view class="bottom-space"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { getBanners, getHotCases } from '@/api/index.uts'
|
||||
import { SERVICE_TYPES } from '@/utils/config.uts'
|
||||
|
||||
// 轮播图类型
|
||||
type BannerItem = {
|
||||
id : string
|
||||
image : string
|
||||
title : string
|
||||
link : string
|
||||
}
|
||||
|
||||
// 案例类型
|
||||
type CaseItem = {
|
||||
id : string
|
||||
title : string
|
||||
category : string
|
||||
categoryName : string
|
||||
coverImage : string
|
||||
material : string
|
||||
duration : string
|
||||
price : string
|
||||
views : number
|
||||
likes : number
|
||||
}
|
||||
|
||||
// 服务类型
|
||||
type ServiceType = {
|
||||
id : string
|
||||
name : string
|
||||
icon : string
|
||||
}
|
||||
|
||||
// 优势类型
|
||||
type AdvantageItem = {
|
||||
icon : string
|
||||
title : string
|
||||
desc : string
|
||||
}
|
||||
|
||||
// 轮播图数据
|
||||
const bannerList = ref<BannerItem[]>([])
|
||||
|
||||
// 服务类型
|
||||
const serviceTypes = ref<ServiceType[]>([])
|
||||
|
||||
// 热门案例
|
||||
const hotCases = ref<CaseItem[]>([])
|
||||
|
||||
// 公司优势
|
||||
const advantages = ref<AdvantageItem[]>([
|
||||
{ icon: '👨🔧', title: '专业团队', desc: '10年+经验' },
|
||||
{ icon: '✅', title: '品质保证', desc: '质保2年' },
|
||||
{ icon: '🚗', title: '上门服务', desc: '免费评估' },
|
||||
{ icon: '💰', title: '价格透明', desc: '无隐形消费' }
|
||||
])
|
||||
|
||||
// 初始化服务类型
|
||||
const initServiceTypes = () => {
|
||||
serviceTypes.value = SERVICE_TYPES.map((item) : ServiceType => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
icon: item.icon
|
||||
} as ServiceType
|
||||
})
|
||||
}
|
||||
|
||||
// 获取轮播图
|
||||
const fetchBanners = async () => {
|
||||
try {
|
||||
const res = await getBanners()
|
||||
const data = res.data as UTSJSONObject[]
|
||||
bannerList.value = data.map((item) : BannerItem => {
|
||||
return {
|
||||
id: item['id'] as string,
|
||||
image: item['image'] as string,
|
||||
title: item['title'] as string,
|
||||
link: item['link'] as string
|
||||
} as BannerItem
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('获取轮播图失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取热门案例
|
||||
const fetchHotCases = async () => {
|
||||
try {
|
||||
const res = await getHotCases()
|
||||
const data = res.data as UTSJSONObject
|
||||
// 适应后端返回格式:{ items: [], total: 0 }
|
||||
const list = data['items'] as UTSJSONObject[] || []
|
||||
hotCases.value = list.map((item) : CaseItem => {
|
||||
return {
|
||||
id: item['id'] as string,
|
||||
title: item['title'] as string,
|
||||
category: item['category'] as string,
|
||||
categoryName: item['categoryName'] as string,
|
||||
coverImage: item['coverImage'] as string,
|
||||
material: item['material'] as string,
|
||||
duration: item['duration'] as string,
|
||||
price: item['price'] as string,
|
||||
views: item['views'] as number,
|
||||
likes: item['likes'] as number
|
||||
} as CaseItem
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('获取热门案例失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 轮播图点击
|
||||
const handleBannerClick = (item : BannerItem) => {
|
||||
if (item.link != '') {
|
||||
uni.navigateTo({
|
||||
url: item.link,
|
||||
fail: () => {
|
||||
uni.switchTab({
|
||||
url: item.link
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 服务点击
|
||||
const handleServiceClick = (id : string) => {
|
||||
uni.switchTab({
|
||||
url: '/pages/service/index'
|
||||
})
|
||||
}
|
||||
|
||||
// 查看更多案例
|
||||
const goToCaseList = () => {
|
||||
uni.switchTab({
|
||||
url: '/pages/cases/list'
|
||||
})
|
||||
}
|
||||
|
||||
// 查看案例详情
|
||||
const goToCaseDetail = (id : string) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/cases/detail?id=${id}`
|
||||
})
|
||||
}
|
||||
|
||||
// 去预约
|
||||
const goToBooking = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/booking/index'
|
||||
})
|
||||
}
|
||||
|
||||
// API测试方法
|
||||
const testAPI = async () => {
|
||||
try {
|
||||
console.log('开始API对接测试...')
|
||||
|
||||
// 测试轮播图(使用Mock数据)
|
||||
const bannerRes = await getBanners()
|
||||
console.log('轮播图API响应:', bannerRes)
|
||||
|
||||
// 测试案例列表
|
||||
const caseRes = await getCaseList()
|
||||
console.log('案例列表API响应:', caseRes)
|
||||
|
||||
uni.showToast({
|
||||
title: 'API测试完成,请查看控制台',
|
||||
icon: 'success'
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('API测试失败:', error)
|
||||
uni.showToast({
|
||||
title: 'API测试失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
initServiceTypes()
|
||||
fetchBanners()
|
||||
fetchHotCases()
|
||||
|
||||
// 开发环境下自动进行API测试
|
||||
// testAPI()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.page {
|
||||
flex: 1;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 轮播图 */
|
||||
.banner-section {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.banner-swiper {
|
||||
height: 320rpx;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 服务入口 */
|
||||
.service-section {
|
||||
padding: 0 24rpx 24rpx;
|
||||
}
|
||||
|
||||
.service-grid {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx 16rpx;
|
||||
}
|
||||
|
||||
/* 公司优势 */
|
||||
.advantage-section {
|
||||
padding: 0 24rpx 24rpx;
|
||||
}
|
||||
|
||||
.advantage-list {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.advantage-item {
|
||||
width: 50%;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 16rpx 0;
|
||||
}
|
||||
|
||||
.advantage-icon {
|
||||
font-size: 48rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.advantage-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.advantage-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.advantage-desc {
|
||||
font-size: 24rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 热门案例 */
|
||||
.case-section {
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.case-list {
|
||||
padding-bottom: 24rpx;
|
||||
}
|
||||
|
||||
/* 预约入口 */
|
||||
.booking-section {
|
||||
margin: 0 24rpx 24rpx;
|
||||
background: linear-gradient(135deg, #D4A574 0%, #B8895A 100%);
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.booking-content {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.booking-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.booking-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.booking-desc {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.booking-btn {
|
||||
background-color: #ffffff;
|
||||
padding: 16rpx 32rpx;
|
||||
border-radius: 999rpx;
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
|
||||
.booking-btn-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #D4A574;
|
||||
}
|
||||
|
||||
/* 底部间距 */
|
||||
.bottom-space {
|
||||
height: 120rpx;
|
||||
}
|
||||
</style>
|
||||
537
前端/pages/service/index.uvue
Normal file
@@ -0,0 +1,537 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<scroll-view class="page-scroll" scroll-y>
|
||||
<!-- 顶部横幅 -->
|
||||
<view class="banner">
|
||||
<view class="banner-content">
|
||||
<text class="banner-title">专业沙发翻新服务</text>
|
||||
<text class="banner-desc">让旧沙发焕发新生</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 服务类型 -->
|
||||
<view class="section">
|
||||
<section-header title="服务类型"></section-header>
|
||||
<view class="service-grid">
|
||||
<view
|
||||
class="service-item"
|
||||
v-for="item in serviceTypes"
|
||||
:key="item.id"
|
||||
@click="handleServiceClick(item)"
|
||||
>
|
||||
<view class="service-icon-bg">
|
||||
<text class="service-icon">{{ item.emoji }}</text>
|
||||
</view>
|
||||
<text class="service-name">{{ item.name }}</text>
|
||||
<text class="service-desc">{{ item.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 服务流程 -->
|
||||
<view class="section">
|
||||
<section-header title="服务流程"></section-header>
|
||||
<view class="process-list">
|
||||
<view class="process-item" v-for="(item, index) in processList" :key="item.step">
|
||||
<view class="process-step">
|
||||
<text class="step-num">{{ item.step }}</text>
|
||||
</view>
|
||||
<view class="process-content">
|
||||
<text class="process-title">{{ item.title }}</text>
|
||||
<text class="process-desc">{{ item.description }}</text>
|
||||
</view>
|
||||
<view class="process-line" v-if="index < processList.length - 1"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 材质说明 -->
|
||||
<view class="section">
|
||||
<section-header title="材质说明"></section-header>
|
||||
<view class="material-list">
|
||||
<view class="material-item" v-for="item in materials" :key="item.name">
|
||||
<view class="material-header">
|
||||
<text class="material-name">{{ item.name }}</text>
|
||||
<text class="material-price">{{ item.price }}</text>
|
||||
</view>
|
||||
<text class="material-desc">{{ item.desc }}</text>
|
||||
<view class="material-tags">
|
||||
<text class="material-tag" v-for="tag in item.tags" :key="tag">{{ tag }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 常见问题 -->
|
||||
<view class="section">
|
||||
<section-header title="常见问题"></section-header>
|
||||
<view class="faq-list">
|
||||
<view
|
||||
class="faq-item"
|
||||
v-for="(item, index) in faqList"
|
||||
:key="index"
|
||||
@click="toggleFaq(index)"
|
||||
>
|
||||
<view class="faq-header">
|
||||
<text class="faq-question">{{ item.question }}</text>
|
||||
<text class="faq-arrow">{{ item.expanded ? '−' : '+' }}</text>
|
||||
</view>
|
||||
<view class="faq-answer" v-if="item.expanded">
|
||||
<text class="faq-answer-text">{{ item.answer }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部间距 -->
|
||||
<view class="bottom-space"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部预约按钮 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="bar-info">
|
||||
<text class="bar-title">免费上门评估</text>
|
||||
<text class="bar-desc">专业师傅为您量身定制方案</text>
|
||||
</view>
|
||||
<view class="bar-btn" @click="goToBooking">
|
||||
<text class="bar-btn-text">立即预约</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { getServiceProcess } from '@/api/index.uts'
|
||||
|
||||
// 服务类型
|
||||
type ServiceType = {
|
||||
id : string
|
||||
name : string
|
||||
desc : string
|
||||
emoji : string
|
||||
}
|
||||
|
||||
// 流程类型
|
||||
type ProcessItem = {
|
||||
step : number
|
||||
title : string
|
||||
description : string
|
||||
}
|
||||
|
||||
// 材质类型
|
||||
type MaterialItem = {
|
||||
name : string
|
||||
price : string
|
||||
desc : string
|
||||
tags : string[]
|
||||
}
|
||||
|
||||
// FAQ类型
|
||||
type FaqItem = {
|
||||
question : string
|
||||
answer : string
|
||||
expanded : boolean
|
||||
}
|
||||
|
||||
// 服务类型数据
|
||||
const serviceTypes = ref<ServiceType[]>([
|
||||
{ id: 'repair', name: '局部修复', desc: '破损、划痕修复', emoji: '🔧' },
|
||||
{ id: 'recolor', name: '改色翻新', desc: '皮面改色换新', emoji: '🎨' },
|
||||
{ id: 'refurbish', name: '整体翻新', desc: '全面翻新升级', emoji: '✨' },
|
||||
{ id: 'custom', name: '定制换皮', desc: '个性化定制', emoji: '💎' }
|
||||
])
|
||||
|
||||
// 服务流程
|
||||
const processList = ref<ProcessItem[]>([])
|
||||
|
||||
// 材质说明
|
||||
const materials = ref<MaterialItem[]>([
|
||||
{
|
||||
name: '头层牛皮',
|
||||
price: '¥800-1500/平',
|
||||
desc: '采用进口头层牛皮,质地柔软,透气性好,使用寿命长',
|
||||
tags: ['进口原料', '透气舒适', '耐用耐磨']
|
||||
},
|
||||
{
|
||||
name: '二层牛皮',
|
||||
price: '¥400-800/平',
|
||||
desc: '经济实惠的选择,经过特殊处理后外观与头层相近',
|
||||
tags: ['性价比高', '外观精美', '易打理']
|
||||
},
|
||||
{
|
||||
name: '科技布',
|
||||
price: '¥200-500/平',
|
||||
desc: '新型环保面料,防水防污,清洁方便,触感舒适',
|
||||
tags: ['防水防污', '易清洁', '环保健康']
|
||||
},
|
||||
{
|
||||
name: '棉麻布艺',
|
||||
price: '¥150-400/平',
|
||||
desc: '天然面料,透气舒适,适合追求自然风格的客户',
|
||||
tags: ['天然环保', '透气清爽', '风格多样']
|
||||
}
|
||||
])
|
||||
|
||||
// 常见问题
|
||||
const faqList = ref<FaqItem[]>([
|
||||
{
|
||||
question: '翻新需要多长时间?',
|
||||
answer: '根据沙发的大小和翻新程度不同,一般需要3-10个工作日。局部修复1-3天,整体翻新5-10天。',
|
||||
expanded: false
|
||||
},
|
||||
{
|
||||
question: '翻新后能保证多久?',
|
||||
answer: '我们提供2年质保服务,质保期内如有非人为损坏,免费维修。头层牛皮正常使用可达10年以上。',
|
||||
expanded: false
|
||||
},
|
||||
{
|
||||
question: '可以上门取送吗?',
|
||||
answer: '是的,我们提供免费上门取送服务(市区范围内),郊区会收取少量运输费用。',
|
||||
expanded: false
|
||||
},
|
||||
{
|
||||
question: '如何报价?',
|
||||
answer: '我们提供免费上门评估服务,师傅会根据沙发的实际情况给出详细报价,价格透明无隐形消费。',
|
||||
expanded: false
|
||||
},
|
||||
{
|
||||
question: '定金和尾款如何支付?',
|
||||
answer: '确认订单后支付30%定金,翻新完成验收满意后支付尾款。支持微信、支付宝、银行转账等多种方式。',
|
||||
expanded: false
|
||||
}
|
||||
])
|
||||
|
||||
// 获取服务流程
|
||||
const fetchServiceProcess = async () => {
|
||||
try {
|
||||
const res = await getServiceProcess()
|
||||
const data = res.data as UTSJSONObject[]
|
||||
processList.value = data.map((item) : ProcessItem => {
|
||||
return {
|
||||
step: item['step'] as number,
|
||||
title: item['title'] as string,
|
||||
description: item['description'] as string
|
||||
} as ProcessItem
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('获取服务流程失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 切换FAQ展开
|
||||
const toggleFaq = (index : number) => {
|
||||
faqList.value[index].expanded = !faqList.value[index].expanded
|
||||
}
|
||||
|
||||
// 服务点击
|
||||
const handleServiceClick = (item : ServiceType) => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/booking/index'
|
||||
})
|
||||
}
|
||||
|
||||
// 去预约
|
||||
const goToBooking = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/booking/index'
|
||||
})
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
fetchServiceProcess()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.page {
|
||||
flex: 1;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.page-scroll {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 顶部横幅 */
|
||||
.banner {
|
||||
height: 280rpx;
|
||||
background: linear-gradient(135deg, #D4A574 0%, #B8895A 100%);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.banner-content {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.banner-title {
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.banner-desc {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
/* 通用section */
|
||||
.section {
|
||||
padding: 0 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
/* 服务类型 */
|
||||
.service-grid {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 16rpx;
|
||||
}
|
||||
|
||||
.service-item {
|
||||
width: 50%;
|
||||
padding: 24rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.service-icon-bg {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
background-color: #FDF6EE;
|
||||
border-radius: 50%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.service-icon {
|
||||
font-size: 48rpx;
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.service-desc {
|
||||
font-size: 24rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 服务流程 */
|
||||
.process-list {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.process-item {
|
||||
flex-direction: row;
|
||||
position: relative;
|
||||
padding-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.process-step {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
background-color: #D4A574;
|
||||
border-radius: 50%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 24rpx;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.step-num {
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.process-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.process-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.process-desc {
|
||||
font-size: 26rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.process-line {
|
||||
position: absolute;
|
||||
left: 22rpx;
|
||||
top: 56rpx;
|
||||
width: 4rpx;
|
||||
height: 60rpx;
|
||||
background-color: #E8C9A8;
|
||||
}
|
||||
|
||||
/* 材质说明 */
|
||||
.material-list {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.material-item {
|
||||
padding: 32rpx;
|
||||
border-bottom-width: 1rpx;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #EBEEF5;
|
||||
}
|
||||
|
||||
.material-item:last-child {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
.material-header {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.material-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.material-price {
|
||||
font-size: 28rpx;
|
||||
color: #D4A574;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.material-desc {
|
||||
font-size: 26rpx;
|
||||
color: #606266;
|
||||
margin-bottom: 16rpx;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
.material-tags {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.material-tag {
|
||||
font-size: 22rpx;
|
||||
color: #D4A574;
|
||||
background-color: #FDF6EE;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-right: 16rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
/* 常见问题 */
|
||||
.faq-list {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.faq-item {
|
||||
padding: 32rpx;
|
||||
border-bottom-width: 1rpx;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #EBEEF5;
|
||||
}
|
||||
|
||||
.faq-item:last-child {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
.faq-header {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.faq-question {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.faq-arrow {
|
||||
font-size: 36rpx;
|
||||
color: #909399;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.faq-answer {
|
||||
margin-top: 16rpx;
|
||||
padding-top: 16rpx;
|
||||
border-top-width: 1rpx;
|
||||
border-top-style: dashed;
|
||||
border-top-color: #EBEEF5;
|
||||
}
|
||||
|
||||
.faq-answer-text {
|
||||
font-size: 26rpx;
|
||||
color: #606266;
|
||||
line-height: 44rpx;
|
||||
}
|
||||
|
||||
/* 底部间距 */
|
||||
.bottom-space {
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
/* 底部操作栏 */
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index:2;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: #ffffff;
|
||||
padding: 20rpx 32rpx;
|
||||
padding-bottom: 40rpx;
|
||||
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.bar-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.bar-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.bar-desc {
|
||||
font-size: 24rpx;
|
||||
color: #909399;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.bar-btn {
|
||||
background-color: #D4A574;
|
||||
padding: 24rpx 56rpx;
|
||||
border-radius: 999rpx;
|
||||
}
|
||||
|
||||
.bar-btn-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
488
前端/pages/user/index.uvue
Normal file
@@ -0,0 +1,488 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<scroll-view class="page-scroll" scroll-y>
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-section">
|
||||
<view class="user-card">
|
||||
<view class="avatar-wrapper">
|
||||
<image
|
||||
class="user-avatar"
|
||||
:src="userInfo.avatar || '/static/images/default-avatar.png'"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
</view>
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{ userInfo.nickName || '点击登录' }}</text>
|
||||
<text class="user-phone" v-if="userInfo.phone">{{ userInfo.phone }}</text>
|
||||
</view>
|
||||
<view class="user-action" @click="handleLogin" v-if="!isLoggedIn">
|
||||
<text class="action-text">登录</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能菜单 -->
|
||||
<view class="menu-section">
|
||||
<view class="menu-group">
|
||||
<view class="menu-item" @click="goToBookingList">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">📋</text>
|
||||
<text class="menu-text">我的预约</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-badge" v-if="bookingCount > 0">{{ bookingCount }}</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="goToFavorites">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">❤️</text>
|
||||
<text class="menu-text">我的收藏</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-badge" v-if="favoriteCount > 0">{{ favoriteCount }}</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-group">
|
||||
<view class="menu-item" @click="goToAbout">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">🏠</text>
|
||||
<text class="menu-text">关于我们</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="callService">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">📞</text>
|
||||
<text class="menu-text">联系客服</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-value">400-888-8888</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="openFeedback">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">💬</text>
|
||||
<text class="menu-text">意见反馈</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-group">
|
||||
<view class="menu-item" @click="checkUpdate">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">🔄</text>
|
||||
<text class="menu-text">检查更新</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-value">v1.0.0</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="clearCache">
|
||||
<view class="menu-left">
|
||||
<text class="menu-icon">🧹</text>
|
||||
<text class="menu-text">清除缓存</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-value">{{ cacheSize }}</text>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 退出登录 -->
|
||||
<view class="logout-section" v-if="isLoggedIn">
|
||||
<view class="logout-btn" @click="handleLogout">
|
||||
<text class="logout-text">退出登录</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部间距 -->
|
||||
<view class="bottom-space"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { STORAGE_KEYS } from '@/utils/config.uts'
|
||||
|
||||
// 用户信息类型
|
||||
type UserInfo = {
|
||||
id : string
|
||||
nickName : string
|
||||
avatar : string
|
||||
phone : string
|
||||
}
|
||||
|
||||
// 用户信息
|
||||
const userInfo = ref<UserInfo>({
|
||||
id: '',
|
||||
nickName: '',
|
||||
avatar: '',
|
||||
phone: ''
|
||||
})
|
||||
|
||||
// 是否登录
|
||||
const isLoggedIn = ref(false)
|
||||
|
||||
// 预约数量
|
||||
const bookingCount = ref(0)
|
||||
|
||||
// 收藏数量
|
||||
const favoriteCount = ref(0)
|
||||
|
||||
// 缓存大小
|
||||
const cacheSize = ref('0KB')
|
||||
|
||||
// 检查登录状态
|
||||
const checkLoginStatus = () => {
|
||||
const token = uni.getStorageSync(STORAGE_KEYS.TOKEN) as string
|
||||
isLoggedIn.value = token != ''
|
||||
|
||||
if (isLoggedIn.value) {
|
||||
// 获取用户信息
|
||||
const info = uni.getStorageSync(STORAGE_KEYS.USER_INFO) as UTSJSONObject | null
|
||||
if (info != null) {
|
||||
userInfo.value = {
|
||||
id: (info['id'] ?? '') as string,
|
||||
nickName: (info['nickName'] ?? '') as string,
|
||||
avatar: (info['avatar'] ?? '') as string,
|
||||
phone: (info['phone'] ?? '') as string
|
||||
} as UserInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取缓存大小
|
||||
const getCacheSize = () => {
|
||||
uni.getStorageInfo({
|
||||
success: (res) => {
|
||||
const size = res.currentSize
|
||||
if (size < 1024) {
|
||||
cacheSize.value = `${size}KB`
|
||||
} else {
|
||||
cacheSize.value = `${(size / 1024).toFixed(2)}MB`
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 登录
|
||||
const handleLogin = () => {
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.getUserProfile({
|
||||
desc: '用于完善用户资料',
|
||||
success: (res) => {
|
||||
userInfo.value = {
|
||||
id: '',
|
||||
nickName: res.userInfo.nickName,
|
||||
avatar: res.userInfo.avatarUrl,
|
||||
phone: ''
|
||||
} as UserInfo
|
||||
|
||||
// 保存用户信息
|
||||
uni.setStorageSync(STORAGE_KEYS.USER_INFO, {
|
||||
nickName: res.userInfo.nickName,
|
||||
avatar: res.userInfo.avatarUrl
|
||||
} as UTSJSONObject)
|
||||
uni.setStorageSync(STORAGE_KEYS.TOKEN, 'mock_token_' + Date.now().toString())
|
||||
isLoggedIn.value = true
|
||||
|
||||
uni.showToast({
|
||||
title: '登录成功',
|
||||
icon: 'success'
|
||||
})
|
||||
},
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
title: '登录失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.showToast({
|
||||
title: '请在微信小程序中登录',
|
||||
icon: 'none'
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
const handleLogout = () => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要退出登录吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.removeStorageSync(STORAGE_KEYS.TOKEN)
|
||||
uni.removeStorageSync(STORAGE_KEYS.USER_INFO)
|
||||
isLoggedIn.value = false
|
||||
userInfo.value = {
|
||||
id: '',
|
||||
nickName: '',
|
||||
avatar: '',
|
||||
phone: ''
|
||||
} as UserInfo
|
||||
|
||||
uni.showToast({
|
||||
title: '已退出登录',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 跳转预约列表
|
||||
const goToBookingList = () => {
|
||||
uni.showToast({
|
||||
title: '功能开发中',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 跳转收藏列表
|
||||
const goToFavorites = () => {
|
||||
uni.showToast({
|
||||
title: '功能开发中',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 关于我们
|
||||
const goToAbout = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/about/index'
|
||||
})
|
||||
}
|
||||
|
||||
// 联系客服
|
||||
const callService = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: '400-888-8888',
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
title: '拨打电话失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 意见反馈
|
||||
const openFeedback = () => {
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序可以使用feedback
|
||||
// #endif
|
||||
uni.showToast({
|
||||
title: '功能开发中',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 检查更新
|
||||
const checkUpdate = () => {
|
||||
uni.showToast({
|
||||
title: '已是最新版本',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
|
||||
// 清除缓存
|
||||
const clearCache = () => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要清除缓存吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.clearStorage({
|
||||
success: () => {
|
||||
cacheSize.value = '0KB'
|
||||
uni.showToast({
|
||||
title: '清除成功',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
checkLoginStatus()
|
||||
getCacheSize()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.page {
|
||||
flex: 1;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.page-scroll {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 用户信息 */
|
||||
.user-section {
|
||||
background: linear-gradient(135deg, #D4A574 0%, #B8895A 100%);
|
||||
padding: 48rpx 32rpx 64rpx;
|
||||
}
|
||||
|
||||
.user-card {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
border-width: 4rpx;
|
||||
border-style: solid;
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.user-phone {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.user-action {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
padding: 16rpx 32rpx;
|
||||
border-radius: 999rpx;
|
||||
}
|
||||
|
||||
.action-text {
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 菜单区域 */
|
||||
.menu-section {
|
||||
margin-top: -32rpx;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.menu-group {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 32rpx;
|
||||
border-bottom-width: 1rpx;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #EBEEF5;
|
||||
}
|
||||
|
||||
.menu-item:last-child {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
.menu-left {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
font-size: 40rpx;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.menu-text {
|
||||
font-size: 30rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.menu-right {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.menu-value {
|
||||
font-size: 26rpx;
|
||||
color: #909399;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.menu-badge {
|
||||
background-color: #F56C6C;
|
||||
color: #ffffff;
|
||||
font-size: 22rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 999rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.menu-arrow {
|
||||
font-size: 32rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 退出登录 */
|
||||
.logout-section {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 28rpx 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logout-text {
|
||||
font-size: 30rpx;
|
||||
color: #F56C6C;
|
||||
}
|
||||
|
||||
/* 底部间距 */
|
||||
.bottom-space {
|
||||
height: 120rpx;
|
||||
}
|
||||
</style>
|
||||
48
前端/static/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# 静态资源说明
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
static/
|
||||
├── icons/ # 图标文件
|
||||
│ ├── home.svg # 首页图标(未选中)
|
||||
│ ├── home-active.svg # 首页图标(选中)
|
||||
│ ├── cases.svg # 案例图标(未选中)
|
||||
│ ├── cases-active.svg # 案例图标(选中)
|
||||
│ ├── service.svg # 服务图标(未选中)
|
||||
│ ├── service-active.svg # 服务图标(选中)
|
||||
│ ├── user.svg # 我的图标(未选中)
|
||||
│ ├── user-active.svg # 我的图标(选中)
|
||||
│ └── README.md # 图标转换说明
|
||||
├── images/ # 图片资源
|
||||
│ ├── default-avatar.svg # 默认头像
|
||||
│ └── logo.svg # 公司Logo
|
||||
└── mock/ # Mock数据图片
|
||||
├── banner1.svg # 轮播图1
|
||||
├── banner2.svg # 轮播图2
|
||||
├── banner3.svg # 轮播图3
|
||||
├── case1-before.svg # 案例1翻新前
|
||||
├── case1-after.svg # 案例1翻新后
|
||||
└── ... # 更多案例图片
|
||||
```
|
||||
|
||||
## 重要说明
|
||||
|
||||
### TabBar 图标
|
||||
微信小程序 TabBar **只支持 PNG 格式**,请参考 `icons/README.md` 将 SVG 转换为 PNG。
|
||||
|
||||
### 图片尺寸规范
|
||||
|
||||
1. **轮播图**:建议尺寸 750x320 像素
|
||||
2. **案例图片**:建议尺寸 750x500 像素
|
||||
3. **图标**:建议使用 PNG 格式,支持透明背景
|
||||
4. **颜色规范**:
|
||||
- 主色:#D4A574
|
||||
- 选中色:#D4A574
|
||||
- 未选中色:#999999
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 图片需要进行压缩优化,减小文件体积
|
||||
- 图标建议使用纯色,便于主题切换
|
||||
- 正式上线前需替换 Mock 图片为真实图片
|
||||
54
前端/static/icons/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# 图标转换说明
|
||||
|
||||
## 问题
|
||||
微信小程序 TabBar 图标只支持 PNG 格式,不支持 SVG。
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 方法一:在线转换(推荐)
|
||||
1. 访问 https://svgtopng.com/ 或 https://cloudconvert.com/svg-to-png
|
||||
2. 上传 SVG 文件
|
||||
3. 设置输出尺寸为 81x81 像素
|
||||
4. 下载 PNG 文件并替换到 static/icons/ 目录
|
||||
|
||||
### 方法二:使用设计工具
|
||||
1. 使用 Figma / Sketch / PS 打开 SVG
|
||||
2. 导出为 81x81 像素的 PNG
|
||||
3. 保存到 static/icons/ 目录
|
||||
|
||||
### 方法三:使用命令行工具
|
||||
安装 sharp 或 imagemagick:
|
||||
|
||||
```bash
|
||||
# 使用 Node.js sharp
|
||||
npm install sharp
|
||||
node convert-icons.js
|
||||
|
||||
# 或使用 ImageMagick
|
||||
convert home.svg -resize 81x81 home.png
|
||||
```
|
||||
|
||||
## 需要转换的文件
|
||||
|
||||
| SVG 文件 | 输出 PNG | 尺寸 |
|
||||
|----------|----------|------|
|
||||
| home.svg | home.png | 81x81 |
|
||||
| home-active.svg | home-active.png | 81x81 |
|
||||
| cases.svg | cases.png | 81x81 |
|
||||
| cases-active.svg | cases-active.png | 81x81 |
|
||||
| service.svg | service.png | 81x81 |
|
||||
| service-active.svg | service-active.png | 81x81 |
|
||||
| user.svg | user.png | 81x81 |
|
||||
| user-active.svg | user-active.png | 81x81 |
|
||||
|
||||
## 配色参考
|
||||
|
||||
- 未选中颜色:#999999
|
||||
- 选中颜色:#D4A574(主题色)
|
||||
|
||||
## 图标设计规范
|
||||
|
||||
1. 图标尺寸:81x81 像素
|
||||
2. 安全区域:边距 10 像素
|
||||
3. 图标风格:线性或填充图标
|
||||
4. 背景:透明
|
||||
BIN
前端/static/icons/cases-active.png
Normal file
|
After Width: | Height: | Size: 481 B |
BIN
前端/static/icons/cases.png
Normal file
|
After Width: | Height: | Size: 481 B |
BIN
前端/static/icons/home-active.png
Normal file
|
After Width: | Height: | Size: 378 B |
BIN
前端/static/icons/home.png
Normal file
|
After Width: | Height: | Size: 375 B |
BIN
前端/static/icons/service-active.png
Normal file
|
After Width: | Height: | Size: 833 B |
BIN
前端/static/icons/service.png
Normal file
|
After Width: | Height: | Size: 830 B |
BIN
前端/static/icons/user-active.png
Normal file
|
After Width: | Height: | Size: 533 B |
BIN
前端/static/icons/user.png
Normal file
|
After Width: | Height: | Size: 530 B |
5
前端/static/images/default-avatar.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
|
||||
<circle cx="100" cy="100" r="100" fill="#E8E8E8"/>
|
||||
<circle cx="100" cy="75" r="35" fill="#BDBDBD"/>
|
||||
<ellipse cx="100" cy="155" rx="50" ry="35" fill="#BDBDBD"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 261 B |
10
前端/static/images/logo.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
|
||||
<defs>
|
||||
<linearGradient id="logoGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#D4A574"/>
|
||||
<stop offset="100%" style="stop-color:#B8895A"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<circle cx="100" cy="100" r="95" fill="url(#logoGradient)"/>
|
||||
<text x="100" y="115" text-anchor="middle" font-size="48" font-weight="bold" fill="white" font-family="Arial">优艺家</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 511 B |
BIN
前端/static/logo.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
11
前端/static/mock/banner1.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 320" width="750" height="320">
|
||||
<defs>
|
||||
<linearGradient id="bannerGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#D4A574"/>
|
||||
<stop offset="100%" style="stop-color:#B8895A"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="750" height="320" fill="url(#bannerGradient)"/>
|
||||
<text x="375" y="140" text-anchor="middle" font-size="48" font-weight="bold" fill="white" font-family="Arial">专业沙发翻新服务</text>
|
||||
<text x="375" y="200" text-anchor="middle" font-size="28" fill="rgba(255,255,255,0.8)" font-family="Arial">让旧沙发焕发新生</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 669 B |
11
前端/static/mock/banner2.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 320" width="750" height="320">
|
||||
<defs>
|
||||
<linearGradient id="bannerGradient2" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#67C23A"/>
|
||||
<stop offset="100%" style="stop-color:#529b2e"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="750" height="320" fill="url(#bannerGradient2)"/>
|
||||
<text x="375" y="140" text-anchor="middle" font-size="48" font-weight="bold" fill="white" font-family="Arial">十年品质保证</text>
|
||||
<text x="375" y="200" text-anchor="middle" font-size="28" fill="rgba(255,255,255,0.8)" font-family="Arial">质保期内免费维护</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 665 B |
11
前端/static/mock/banner3.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 320" width="750" height="320">
|
||||
<defs>
|
||||
<linearGradient id="bannerGradient3" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#409EFF"/>
|
||||
<stop offset="100%" style="stop-color:#337ecc"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="750" height="320" fill="url(#bannerGradient3)"/>
|
||||
<text x="375" y="140" text-anchor="middle" font-size="48" font-weight="bold" fill="white" font-family="Arial">免费上门评估</text>
|
||||
<text x="375" y="200" text-anchor="middle" font-size="28" fill="rgba(255,255,255,0.8)" font-family="Arial">专业师傅为您量身定制方案</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 677 B |
5
前端/static/mock/case1-after.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 500" width="750" height="500">
|
||||
<rect width="750" height="500" fill="#D4A574"/>
|
||||
<text x="375" y="230" text-anchor="middle" font-size="36" fill="rgba(255,255,255,0.8)" font-family="Arial">翻新后</text>
|
||||
<text x="375" y="280" text-anchor="middle" font-size="24" fill="rgba(255,255,255,0.6)" font-family="Arial">欧式真皮沙发</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 406 B |
5
前端/static/mock/case1-before.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 500" width="750" height="500">
|
||||
<rect width="750" height="500" fill="#8B7355"/>
|
||||
<text x="375" y="230" text-anchor="middle" font-size="36" fill="rgba(255,255,255,0.6)" font-family="Arial">翻新前</text>
|
||||
<text x="375" y="280" text-anchor="middle" font-size="24" fill="rgba(255,255,255,0.4)" font-family="Arial">欧式真皮沙发</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 406 B |
5
前端/static/mock/case2-after.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 500" width="750" height="500">
|
||||
<rect width="750" height="500" fill="#67C23A"/>
|
||||
<text x="375" y="230" text-anchor="middle" font-size="36" fill="rgba(255,255,255,0.8)" font-family="Arial">翻新后</text>
|
||||
<text x="375" y="280" text-anchor="middle" font-size="24" fill="rgba(255,255,255,0.6)" font-family="Arial">现代简约布艺沙发</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 412 B |
5
前端/static/mock/case2-before.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 500" width="750" height="500">
|
||||
<rect width="750" height="500" fill="#6B8E6B"/>
|
||||
<text x="375" y="230" text-anchor="middle" font-size="36" fill="rgba(255,255,255,0.6)" font-family="Arial">翻新前</text>
|
||||
<text x="375" y="280" text-anchor="middle" font-size="24" fill="rgba(255,255,255,0.4)" font-family="Arial">现代简约布艺沙发</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 412 B |
5
前端/static/mock/case3-after.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 500" width="750" height="500">
|
||||
<rect width="750" height="500" fill="#F5F5DC"/>
|
||||
<text x="375" y="230" text-anchor="middle" font-size="36" fill="rgba(0,0,0,0.5)" font-family="Arial">翻新后</text>
|
||||
<text x="375" y="280" text-anchor="middle" font-size="24" fill="rgba(0,0,0,0.3)" font-family="Arial">美式复古沙发</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 394 B |
5
前端/static/mock/case3-before.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 500" width="750" height="500">
|
||||
<rect width="750" height="500" fill="#5D4037"/>
|
||||
<text x="375" y="230" text-anchor="middle" font-size="36" fill="rgba(255,255,255,0.6)" font-family="Arial">翻新前</text>
|
||||
<text x="375" y="280" text-anchor="middle" font-size="24" fill="rgba(255,255,255,0.4)" font-family="Arial">美式复古沙发</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 406 B |
5
前端/static/mock/case4-after.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 500" width="750" height="500">
|
||||
<rect width="750" height="500" fill="#607D8B"/>
|
||||
<text x="375" y="230" text-anchor="middle" font-size="36" fill="rgba(255,255,255,0.8)" font-family="Arial">翻新后</text>
|
||||
<text x="375" y="280" text-anchor="middle" font-size="24" fill="rgba(255,255,255,0.6)" font-family="Arial">功能沙发</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 400 B |
5
前端/static/mock/case4-before.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 500" width="750" height="500">
|
||||
<rect width="750" height="500" fill="#455A64"/>
|
||||
<text x="375" y="230" text-anchor="middle" font-size="36" fill="rgba(255,255,255,0.6)" font-family="Arial">翻新前</text>
|
||||
<text x="375" y="280" text-anchor="middle" font-size="24" fill="rgba(255,255,255,0.4)" font-family="Arial">功能沙发</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 400 B |
5
前端/static/mock/case5-after.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 500" width="750" height="500">
|
||||
<rect width="750" height="500" fill="#A1887F"/>
|
||||
<text x="375" y="230" text-anchor="middle" font-size="36" fill="rgba(255,255,255,0.8)" font-family="Arial">翻新后</text>
|
||||
<text x="375" y="280" text-anchor="middle" font-size="24" fill="rgba(255,255,255,0.6)" font-family="Arial">中式红木沙发垫</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 409 B |
5
前端/static/mock/case5-before.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 500" width="750" height="500">
|
||||
<rect width="750" height="500" fill="#8D6E63"/>
|
||||
<text x="375" y="230" text-anchor="middle" font-size="36" fill="rgba(255,255,255,0.6)" font-family="Arial">翻新前</text>
|
||||
<text x="375" y="280" text-anchor="middle" font-size="24" fill="rgba(255,255,255,0.4)" font-family="Arial">中式红木沙发垫</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 409 B |
5
前端/static/mock/case6-after.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 500" width="750" height="500">
|
||||
<rect width="750" height="500" fill="#263238"/>
|
||||
<text x="375" y="230" text-anchor="middle" font-size="36" fill="rgba(255,255,255,0.8)" font-family="Arial">翻新后</text>
|
||||
<text x="375" y="280" text-anchor="middle" font-size="24" fill="rgba(255,255,255,0.6)" font-family="Arial">办公室皮沙发组合</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 412 B |
5
前端/static/mock/case6-before.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 500" width="750" height="500">
|
||||
<rect width="750" height="500" fill="#37474F"/>
|
||||
<text x="375" y="230" text-anchor="middle" font-size="36" fill="rgba(255,255,255,0.6)" font-family="Arial">翻新前</text>
|
||||
<text x="375" y="280" text-anchor="middle" font-size="24" fill="rgba(255,255,255,0.4)" font-family="Arial">办公室皮沙发组合</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 412 B |
110
前端/uni.scss
Normal file
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* 优艺家沙发翻新 - 全局样式变量
|
||||
*/
|
||||
|
||||
/* ========== 项目主题色 ========== */
|
||||
$primary-color: #D4A574;
|
||||
$primary-light: #E8C9A8;
|
||||
$primary-dark: #B8895A;
|
||||
|
||||
/* 辅助色 */
|
||||
$success-color: #67C23A;
|
||||
$warning-color: #E6A23C;
|
||||
$danger-color: #F56C6C;
|
||||
$info-color: #909399;
|
||||
|
||||
/* 文字颜色 */
|
||||
$text-primary: #333333;
|
||||
$text-regular: #606266;
|
||||
$text-secondary: #909399;
|
||||
$text-placeholder: #C0C4CC;
|
||||
|
||||
/* 背景色 */
|
||||
$bg-color: #f5f5f5;
|
||||
$bg-white: #ffffff;
|
||||
$bg-gray: #fafafa;
|
||||
|
||||
/* 边框颜色 */
|
||||
$border-color: #EBEEF5;
|
||||
$border-light: #F2F6FC;
|
||||
|
||||
/* 间距 */
|
||||
$spacing-xs: 8rpx;
|
||||
$spacing-sm: 16rpx;
|
||||
$spacing-md: 24rpx;
|
||||
$spacing-lg: 32rpx;
|
||||
$spacing-xl: 48rpx;
|
||||
|
||||
/* 圆角 */
|
||||
$radius-sm: 8rpx;
|
||||
$radius-md: 16rpx;
|
||||
$radius-lg: 24rpx;
|
||||
$radius-round: 999rpx;
|
||||
|
||||
/* 阴影 */
|
||||
$shadow-sm: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
|
||||
$shadow-md: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
|
||||
$shadow-lg: 0 8rpx 32rpx rgba(0, 0, 0, 0.12);
|
||||
|
||||
/* ========== uni-app 内置变量 ========== */
|
||||
|
||||
/* 行为相关颜色 */
|
||||
$uni-color-primary: #D4A574;
|
||||
$uni-color-success: #4cd964;
|
||||
$uni-color-warning: #f0ad4e;
|
||||
$uni-color-error: #dd524d;
|
||||
|
||||
/* 文字基本颜色 */
|
||||
$uni-text-color:#333;//基本色
|
||||
$uni-text-color-inverse:#fff;//反色
|
||||
$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
|
||||
$uni-text-color-placeholder: #808080;
|
||||
$uni-text-color-disable:#c0c0c0;
|
||||
|
||||
/* 背景颜色 */
|
||||
$uni-bg-color:#ffffff;
|
||||
$uni-bg-color-grey:#f8f8f8;
|
||||
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
|
||||
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
|
||||
|
||||
/* 边框颜色 */
|
||||
$uni-border-color:#c8c7cc;
|
||||
|
||||
/* 尺寸变量 */
|
||||
|
||||
/* 文字尺寸 */
|
||||
$uni-font-size-sm:12px;
|
||||
$uni-font-size-base:14px;
|
||||
$uni-font-size-lg:16px;
|
||||
|
||||
/* 图片尺寸 */
|
||||
$uni-img-size-sm:20px;
|
||||
$uni-img-size-base:26px;
|
||||
$uni-img-size-lg:40px;
|
||||
|
||||
/* Border Radius */
|
||||
$uni-border-radius-sm: 2px;
|
||||
$uni-border-radius-base: 3px;
|
||||
$uni-border-radius-lg: 6px;
|
||||
$uni-border-radius-circle: 50%;
|
||||
|
||||
/* 水平间距 */
|
||||
$uni-spacing-row-sm: 5px;
|
||||
$uni-spacing-row-base: 10px;
|
||||
$uni-spacing-row-lg: 15px;
|
||||
|
||||
/* 垂直间距 */
|
||||
$uni-spacing-col-sm: 4px;
|
||||
$uni-spacing-col-base: 8px;
|
||||
$uni-spacing-col-lg: 12px;
|
||||
|
||||
/* 透明度 */
|
||||
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
|
||||
|
||||
/* 文章场景相关 */
|
||||
$uni-color-title: #2C405A; // 文章标题颜色
|
||||
$uni-font-size-title:20px;
|
||||
$uni-color-subtitle: #555555; // 二级标题颜色
|
||||
$uni-font-size-subtitle:26px;
|
||||
$uni-color-paragraph: #3F536E; // 文章段落颜色
|
||||
$uni-font-size-paragraph:15px;
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"import { defineComponent as _defineComponent } from 'vue';\nimport { o as _o, gei as _gei, sei as _sei, e as _e } from \"vue\";\nexport default /*#__PURE__*/ _defineComponent({\n __name: 'before-after',\n props: {\n beforeImage: {},\n afterImage: {},\n showTitle: { type: Boolean }\n },\n setup(__props) {\n const props = __props;\n // 预览图片\n const previewImage = (url, type) => {\n const urls = type == 'before' ? [props.beforeImage] : [props.afterImage];\n uni.previewImage({\n current: url,\n urls: urls\n });\n };\n return (_ctx, _cache) => {\n \"raw js\";\n const __returned__ = _e({\n a: _ctx.showTitle\n }, _ctx.showTitle ? {} : {}, {\n b: _ctx.beforeImage,\n c: _o($event => { return previewImage(_ctx.beforeImage, 'before'); }),\n d: _ctx.afterImage,\n e: _o($event => { return previewImage(_ctx.afterImage, 'after'); }),\n f: _sei(_gei(_ctx, ''), 'view')\n });\n return __returned__;\n };\n }\n});\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/components/before-after/before-after.uvue?vue&type=script&setup=true&lang.uts.js.map","references":["D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts","D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts"],"uniExtApis":["uni.previewImage"],"map":"{\"version\":3,\"file\":\"before-after.uvue?vue&type=script&setup=true&lang.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"before-after.uvue?vue&type=script&setup=true&lang.uts\"],\"names\":[],\"mappings\":\"AAAA,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,MAAM,KAAK,CAAA;AACzD,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,MAAM,KAAK,CAAA;AAGhE,eAAe,aAAa,CAAA,gBAAgB,CAAC;IAC3C,MAAM,EAAE,cAAc;IACtB,KAAK,EAAE;QACL,WAAW,EAAE,EAAE;QACf,UAAU,EAAE,EAAE;QACd,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;KAC7B;IACD,KAAK,CAAC,OAAY;QAEnB,MAAM,KAAK,GAAG,OAAO,CAAA;QAErB,OAAO;QACP,MAAM,YAAY,GAAG,CAAC,GAAY,EAAE,IAAa;YAChD,MAAM,IAAI,GAAG,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;YACxE,GAAG,CAAC,YAAY,CAAC;gBAChB,OAAO,EAAE,GAAG;gBACZ,IAAI,EAAE,IAAI;aACV,CAAC,CAAA;QACH,CAAC,CAAA;QAEF,OAAO,CAAC,IAAI,EAAE,MAAM;YAAO,QAAQ,CAAA;YACjC,MAAM,YAAY,GAAG,EAAE,CAAC;gBACxB,CAAC,EAAE,IAAI,CAAC,SAAS;aAClB,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC3B,CAAC,EAAE,IAAI,CAAC,WAAW;gBACnB,CAAC,EAAE,EAAE,CAAC,MAAM,MAAI,OAAA,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,EAAxC,CAAwC,CAAC;gBACzD,CAAC,EAAE,IAAI,CAAC,UAAU;gBAClB,CAAC,EAAE,EAAE,CAAC,MAAM,MAAI,OAAA,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EAAtC,CAAsC,CAAC;gBACvD,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC;aAChC,CAAC,CAAA;YACA,OAAO,YAAY,CAAA;QACrB,CAAC,CAAA;IACD,CAAC;CAEA,CAAC,CAAA\"}"}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"import {} from \"vue\";\nexport default defineComponent({\n onLaunch() {\n uni.__f__('log', 'at App.uvue:7', 'App Launch');\n },\n onShow() {\n uni.__f__('log', 'at App.uvue:10', 'App Show');\n },\n onHide() {\n uni.__f__('log', 'at App.uvue:13', 'App Hide');\n },\n onExit() {\n uni.__f__('log', 'at App.uvue:34', 'App Exit');\n },\n});\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/App.uvue?vue&type=script&lang.uts.js.map","references":[],"uniExtApis":["uni.__f__"],"map":"{\"version\":3,\"file\":\"App.uvue?vue&type=script&lang.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"App.uvue?vue&type=script&lang.uts\"],\"names\":[],\"mappings\":\";AAIC,+BAAe;IACd,QAAQ;QACP,GAAG,CAAC,KAAK,CAAC,KAAK,EAAC,eAAe,EAAC,YAAY,CAAC,CAAA;IAC9C,CAAC;IACD,MAAM;QACL,GAAG,CAAC,KAAK,CAAC,KAAK,EAAC,gBAAgB,EAAC,UAAU,CAAC,CAAA;IAC7C,CAAC;IACD,MAAM;QACL,GAAG,CAAC,KAAK,CAAC,KAAK,EAAC,gBAAgB,EAAC,UAAU,CAAC,CAAA;IAC7C,CAAC;IAmBD,MAAM;QACL,GAAG,CAAC,KAAK,CAAC,KAAK,EAAC,gBAAgB,EAAC,UAAU,CAAC,CAAA;IAC7C,CAAC;CACD,EAAA\"}"}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"import { defineComponent as _defineComponent } from 'vue';\nimport { toDisplayString as _toDisplayString, t as _t, o as _o, gei as _gei, sei as _sei } from \"vue\";\nexport default /*#__PURE__*/ _defineComponent({\n __name: 'service-card',\n props: {\n id: {},\n name: {},\n icon: {}\n },\n emits: [\"click\"],\n setup(__props, _a) {\n var __emit = _a.emit;\n const props = __props;\n const emit = __emit;\n const handleClick = () => {\n emit('click', props.id);\n };\n return (_ctx, _cache) => {\n \"raw js\";\n const __returned__ = {\n a: _ctx.icon,\n b: _t(_ctx.name),\n c: _sei(_gei(_ctx, ''), 'view'),\n d: _o(handleClick)\n };\n return __returned__;\n };\n }\n});\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/components/service-card/service-card.uvue?vue&type=script&setup=true&lang.uts.js.map","references":["D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts","D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts"],"uniExtApis":[],"map":"{\"version\":3,\"file\":\"service-card.uvue?vue&type=script&setup=true&lang.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"service-card.uvue?vue&type=script&setup=true&lang.uts\"],\"names\":[],\"mappings\":\"AAAA,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,MAAM,KAAK,CAAA;AACzD,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,CAAA;AAGrG,eAAe,aAAa,CAAA,gBAAgB,CAAC;IAC3C,MAAM,EAAE,cAAc;IACtB,KAAK,EAAE;QACL,EAAE,EAAE,EAAE;QACN,IAAI,EAAE,EAAE;QACR,IAAI,EAAE,EAAE;KACT;IACD,KAAK,EAAE,CAAC,OAAO,CAAC;IAChB,KAAK,CAAC,OAAY,EAAE,EAAgB;YAAR,MAAM,UAAA;QAEnC,MAAM,KAAK,GAAG,OAAO,CAAA;QAErB,MAAM,IAAI,GAAG,MAAM,CAAA;QAEnB,MAAM,WAAW,GAAG;YACnB,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,CAAA;QACxB,CAAC,CAAA;QAEF,OAAO,CAAC,IAAI,EAAE,MAAM;YAAO,QAAQ,CAAA;YACjC,MAAM,YAAY,GAAG;gBACrB,CAAC,EAAE,IAAI,CAAC,IAAI;gBACZ,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;gBAChB,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC;gBAC/B,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC;aACnB,CAAA;YACC,OAAO,YAAY,CAAA;QACrB,CAAC,CAAA;IACD,CAAC;CAEA,CAAC,CAAA\"}"}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"import { defineComponent as _defineComponent } from 'vue';\nimport { unref as _unref, o as _o, toDisplayString as _toDisplayString, t as _t, e as _e } from \"vue\";\nexport default /*#__PURE__*/ _defineComponent({\n __name: 'nav-bar',\n props: {\n title: {},\n showBack: { type: Boolean },\n titleColor: {},\n bgColor: {}\n },\n setup(__props) {\n const props = __props;\n // 状态栏高度\n const statusBarHeight = ref(20);\n // 导航栏高度\n const navBarHeight = ref(44);\n onMounted(() => {\n const sysInfo = uni.getSystemInfoSync();\n statusBarHeight.value = sysInfo.statusBarHeight;\n const menuButtonInfo = uni.getMenuButtonBoundingClientRect();\n navBarHeight.value = (menuButtonInfo.top - sysInfo.statusBarHeight) * 2 + menuButtonInfo.height;\n });\n const handleBack = () => {\n uni.navigateBack(new UTSJSONObject({\n fail: () => {\n uni.switchTab({\n url: '/pages/index/index'\n });\n }\n }));\n };\n return (_ctx, _cache) => {\n \"raw js\";\n const __returned__ = _e({\n a: _ctx.showBack\n }, _ctx.showBack ? {\n b: _o(handleBack)\n } : {}, {\n c: _t(_ctx.title),\n d: _ctx.titleColor,\n e: _unref(navBarHeight) + 'px',\n f: _unref(statusBarHeight) + 'px',\n g: _unref(statusBarHeight) + _unref(navBarHeight) + 'px'\n });\n return __returned__;\n };\n }\n});\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/components/nav-bar/nav-bar.uvue?vue&type=script&setup=true&lang.uts.js.map","references":["D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts","D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts"],"uniExtApis":["uni.getSystemInfoSync","uni.getMenuButtonBoundingClientRect","uni.switchTab","uni.navigateBack"],"map":"{\"version\":3,\"file\":\"nav-bar.uvue?vue&type=script&setup=true&lang.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"nav-bar.uvue?vue&type=script&setup=true&lang.uts\"],\"names\":[],\"mappings\":\"AAAA,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,MAAM,KAAK,CAAA;AACzD,OAAO,EAAE,KAAK,IAAI,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,eAAe,IAAI,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,MAAM,KAAK,CAAA;AAGrG,eAAe,aAAa,CAAA,gBAAgB,CAAC;IAC3C,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE;QACL,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;QAC3B,UAAU,EAAE,EAAE;QACd,OAAO,EAAE,EAAE;KACZ;IACD,KAAK,CAAC,OAAY;QAEnB,MAAM,KAAK,GAAG,OAAO,CAAA;QAErB,QAAQ;QACR,MAAM,eAAe,GAAG,GAAG,CAAC,EAAE,CAAC,CAAA;QAC/B,QAAQ;QACR,MAAM,YAAY,GAAG,GAAG,CAAC,EAAE,CAAC,CAAA;QAE5B,SAAS,CAAC;YACT,MAAM,OAAO,GAAG,GAAG,CAAC,iBAAiB,EAAE,CAAA;YACvC,eAAe,CAAC,KAAK,GAAG,OAAO,CAAC,eAAe,CAAA;YAE/C,MAAM,cAAc,GAAG,GAAG,CAAC,+BAA+B,EAAE,CAAA;YAC5D,YAAY,CAAC,KAAK,GAAG,CAAC,cAAc,CAAC,GAAG,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,MAAM,CAAA;QAEhG,CAAC,CAAC,CAAA;QAEF,MAAM,UAAU,GAAG;YAClB,GAAG,CAAC,YAAY,mBAAC;gBAChB,IAAI,EAAE;oBACL,GAAG,CAAC,SAAS,CAAC;wBACb,GAAG,EAAE,oBAAoB;qBACzB,CAAC,CAAA;gBACH,CAAC;aACD,EAAC,CAAA;QACH,CAAC,CAAA;QAEF,OAAO,CAAC,IAAI,EAAE,MAAM;YAAO,QAAQ,CAAA;YACjC,MAAM,YAAY,GAAG,EAAE,CAAC;gBACxB,CAAC,EAAE,IAAI,CAAC,QAAQ;aACjB,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACjB,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC;aAClB,CAAC,CAAC,CAAC,EAAE,EAAE;gBACN,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;gBACjB,CAAC,EAAE,IAAI,CAAC,UAAU;gBAClB,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI;gBAC9B,CAAC,EAAE,MAAM,CAAC,eAAe,CAAC,GAAG,IAAI;gBACjC,CAAC,EAAE,MAAM,CAAC,eAAe,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI;aACzD,CAAC,CAAA;YACA,OAAO,YAAY,CAAA;QACrB,CAAC,CAAA;IACD,CAAC;CAEA,CAAC,CAAA\"}"}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"import { defineComponent as _defineComponent } from 'vue';\nimport { toDisplayString as _toDisplayString, t as _t, o as _o, gei as _gei, sei as _sei } from \"vue\";\nclass CaseItem extends UTS.UTSType {\n static get$UTSMetadata$() {\n return {\n kind: 2,\n get fields() {\n return {\n id: { type: String, optional: false },\n title: { type: String, optional: false },\n category: { type: String, optional: false },\n categoryName: { type: String, optional: false },\n coverImage: { type: String, optional: false },\n material: { type: String, optional: false },\n duration: { type: String, optional: false },\n price: { type: String, optional: false },\n views: { type: Number, optional: false },\n likes: { type: Number, optional: false }\n };\n },\n name: \"CaseItem\"\n };\n }\n constructor(options, metadata = CaseItem.get$UTSMetadata$(), isJSONParse = false) {\n super();\n this.__props__ = UTS.UTSType.initProps(options, metadata, isJSONParse);\n this.id = this.__props__.id;\n this.title = this.__props__.title;\n this.category = this.__props__.category;\n this.categoryName = this.__props__.categoryName;\n this.coverImage = this.__props__.coverImage;\n this.material = this.__props__.material;\n this.duration = this.__props__.duration;\n this.price = this.__props__.price;\n this.views = this.__props__.views;\n this.likes = this.__props__.likes;\n delete this.__props__;\n }\n}\nexport default /*#__PURE__*/ _defineComponent({\n __name: 'case-card',\n props: {\n caseData: {}\n },\n emits: [\"click\"],\n setup(__props, _a) {\n var __emit = _a.emit;\n // 定义Props类型\n const props = __props;\n const emit = __emit;\n const handleClick = () => {\n emit('click', props.caseData.id);\n };\n return (_ctx, _cache) => {\n \"raw js\";\n const __returned__ = {\n a: _ctx.caseData.coverImage,\n b: _t(_ctx.caseData.categoryName),\n c: _t(_ctx.caseData.title),\n d: _t(_ctx.caseData.material),\n e: _t(_ctx.caseData.duration),\n f: _t(_ctx.caseData.price),\n g: _t(_ctx.caseData.views),\n h: _t(_ctx.caseData.likes),\n i: _sei(_gei(_ctx, ''), 'view'),\n j: _o(handleClick)\n };\n return __returned__;\n };\n }\n});\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/components/case-card/case-card.uvue?vue&type=script&setup=true&lang.uts.js.map","references":["D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts","D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts"],"uniExtApis":[],"map":"{\"version\":3,\"file\":\"case-card.uvue?vue&type=script&setup=true&lang.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"case-card.uvue?vue&type=script&setup=true&lang.uts\"],\"names\":[],\"mappings\":\"AAAA,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,MAAM,KAAK,CAAA;AACzD,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,CAAA;MAEhG,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcb,eAAe,aAAa,CAAA,gBAAgB,CAAC;IAC3C,MAAM,EAAE,WAAW;IACnB,KAAK,EAAE;QACL,QAAQ,EAAE,EAAE;KACb;IACD,KAAK,EAAE,CAAC,OAAO,CAAC;IAChB,KAAK,CAAC,OAAY,EAAE,EAAgB;YAAR,MAAM,UAAA;QAEnC,YAAY;QACZ,MAAM,KAAK,GAAG,OAAO,CAAA;QAErB,MAAM,IAAI,GAAG,MAAM,CAAA;QAEnB,MAAM,WAAW,GAAG;YACnB,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;QACjC,CAAC,CAAA;QAEF,OAAO,CAAC,IAAI,EAAE,MAAM;YAAO,QAAQ,CAAA;YACjC,MAAM,YAAY,GAAG;gBACrB,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU;gBAC3B,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;gBACjC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAC1B,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAC7B,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAC7B,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAC1B,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAC1B,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAC1B,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC;gBAC/B,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC;aACnB,CAAA;YACC,OAAO,YAAY,CAAA;QACrB,CAAC,CAAA;IACD,CAAC;CAEA,CAAC,CAAA\"}"}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"import { defineComponent as _defineComponent } from 'vue';\nimport { toDisplayString as _toDisplayString, t as _t, o as _o, gei as _gei, sei as _sei, e as _e } from \"vue\";\nexport default /*#__PURE__*/ _defineComponent({\n __name: 'section-header',\n props: {\n title: {},\n showMore: { type: Boolean }\n },\n emits: [\"more\"],\n setup(__props, _a) {\n var __emit = _a.emit;\n const props = __props;\n const emit = __emit;\n const handleMore = () => {\n emit('more');\n };\n return (_ctx, _cache) => {\n \"raw js\";\n const __returned__ = _e({\n a: _t(_ctx.title),\n b: _ctx.showMore\n }, _ctx.showMore ? {\n c: _o(handleMore)\n } : {}, {\n d: _sei(_gei(_ctx, ''), 'view')\n });\n return __returned__;\n };\n }\n});\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/components/section-header/section-header.uvue?vue&type=script&setup=true&lang.uts.js.map","references":["D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts","D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts"],"uniExtApis":[],"map":"{\"version\":3,\"file\":\"section-header.uvue?vue&type=script&setup=true&lang.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"section-header.uvue?vue&type=script&setup=true&lang.uts\"],\"names\":[],\"mappings\":\"AAAA,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,MAAM,KAAK,CAAA;AACzD,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,MAAM,KAAK,CAAA;AAG9G,eAAe,aAAa,CAAA,gBAAgB,CAAC;IAC3C,MAAM,EAAE,gBAAgB;IACxB,KAAK,EAAE;QACL,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;KAC5B;IACD,KAAK,EAAE,CAAC,MAAM,CAAC;IACf,KAAK,CAAC,OAAY,EAAE,EAAgB;YAAR,MAAM,UAAA;QAEnC,MAAM,KAAK,GAAG,OAAO,CAAA;QAErB,MAAM,IAAI,GAAG,MAAM,CAAA;QAEnB,MAAM,UAAU,GAAG;YAClB,IAAI,CAAC,MAAM,CAAC,CAAA;QACb,CAAC,CAAA;QAEF,OAAO,CAAC,IAAI,EAAE,MAAM;YAAO,QAAQ,CAAA;YACjC,MAAM,YAAY,GAAG,EAAE,CAAC;gBACxB,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;gBACjB,CAAC,EAAE,IAAI,CAAC,QAAQ;aACjB,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACjB,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC;aAClB,CAAC,CAAC,CAAC,EAAE,EAAE;gBACN,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC;aAChC,CAAC,CAAA;YACA,OAAO,YAAY,CAAA;QACrB,CAAC,CAAA;IACD,CAAC;CAEA,CAAC,CAAA\"}"}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"import { get, post } from \"../utils/request\";\n/**\n * 获取轮播图\n */\nexport const getBanners = () => {\n return get('/banners');\n};\n/**\n * 获取案例列表\n */\nexport const getCaseList = (params) => {\n return get('/cases', params);\n};\n/**\n * 获取案例详情\n */\nexport const getCaseDetail = (id) => {\n return get(`/cases/${id}`);\n};\n/**\n * 获取热门案例\n */\nexport const getHotCases = () => {\n return get('/cases/hot');\n};\n/**\n * 获取服务流程\n */\nexport const getServiceProcess = () => {\n return get('/service/process');\n};\n/**\n * 获取公司信息\n */\nexport const getCompanyInfo = () => {\n return get('/company/info');\n};\n/**\n * 提交预约\n */\nexport const submitBooking = (data) => {\n return post('/booking', data);\n};\n/**\n * 获取用户信息\n */\nexport const getUserInfo = () => {\n return get('/user/info');\n};\n/**\n * 获取用户收藏列表\n */\nexport const getFavorites = () => {\n return get('/user/favorites');\n};\n/**\n * 添加收藏\n */\nexport const addFavorite = (caseId) => {\n return post('/user/favorites', new UTSJSONObject({ caseId: caseId }));\n};\n/**\n * 取消收藏\n */\nexport const removeFavorite = (caseId) => {\n return post('/user/favorites/remove', new UTSJSONObject({ caseId: caseId }));\n};\n/**\n * 获取预约记录\n */\nexport const getBookingList = () => {\n return get('/user/bookings');\n};\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/api/index.uts.js.map","references":[],"uniExtApis":[],"map":"{\"version\":3,\"file\":\"index.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"index.uts\"],\"names\":[],\"mappings\":\"OAGO,EAAE,GAAG,EAAE,IAAI,EAAE;AAEpB;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACzB,OAAO,GAAG,CAAC,UAAU,CAAC,CAAA;AACvB,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAsB;IACjD,OAAO,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;AAC7B,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAAW;IACxC,OAAO,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;AAC3B,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IAC1B,OAAO,GAAG,CAAC,YAAY,CAAC,CAAA;AACzB,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAChC,OAAO,GAAG,CAAC,kBAAkB,CAAC,CAAA;AAC/B,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC7B,OAAO,GAAG,CAAC,eAAe,CAAC,CAAA;AAC5B,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAoB;IACjD,OAAO,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;AAC9B,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IAC1B,OAAO,GAAG,CAAC,YAAY,CAAC,CAAA;AACzB,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC3B,OAAO,GAAG,CAAC,iBAAiB,CAAC,CAAA;AAC9B,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAe;IAC1C,OAAO,IAAI,CAAC,iBAAiB,oBAAE,EAAE,MAAM,EAAE,MAAM,EAAmB,EAAC,CAAA;AACpE,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAe;IAC7C,OAAO,IAAI,CAAC,wBAAwB,oBAAE,EAAE,MAAM,EAAE,MAAM,EAAmB,EAAC,CAAA;AAC3E,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC7B,OAAO,GAAG,CAAC,gBAAgB,CAAC,CAAA;AAC7B,CAAC,CAAA\"}"}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"import { defineComponent as _defineComponent } from 'vue';\nimport { o as _o, gei as _gei, sei as _sei, e as _e } from \"vue\";\nexport default /*#__PURE__*/ _defineComponent({\n __name: 'before-after',\n props: {\n beforeImage: {},\n afterImage: {},\n showTitle: { type: Boolean }\n },\n setup(__props) {\n const props = __props;\n // 预览图片\n const previewImage = (url, type) => {\n const urls = type == 'before' ? [props.beforeImage] : [props.afterImage];\n uni.previewImage({\n current: url,\n urls: urls\n });\n };\n return (_ctx, _cache) => {\n \"raw js\";\n const __returned__ = _e({\n a: _ctx.showTitle\n }, _ctx.showTitle ? {} : {}, {\n b: _ctx.beforeImage,\n c: _o($event => { return previewImage(_ctx.beforeImage, 'before'); }),\n d: _ctx.afterImage,\n e: _o($event => { return previewImage(_ctx.afterImage, 'after'); }),\n f: _sei(_gei(_ctx, ''), 'view')\n });\n return __returned__;\n };\n }\n});\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/components/before-after/before-after.uvue?vue&type=script&setup=true&lang.uts.js.map","references":["D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts","D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts"],"uniExtApis":["uni.previewImage"],"map":"{\"version\":3,\"file\":\"before-after.uvue?vue&type=script&setup=true&lang.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"before-after.uvue?vue&type=script&setup=true&lang.uts\"],\"names\":[],\"mappings\":\"AAAA,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,MAAM,KAAK,CAAA;AACzD,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,MAAM,KAAK,CAAA;AAGhE,eAAe,aAAa,CAAA,gBAAgB,CAAC;IAC3C,MAAM,EAAE,cAAc;IACtB,KAAK,EAAE;QACL,WAAW,EAAE,EAAE;QACf,UAAU,EAAE,EAAE;QACd,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;KAC7B;IACD,KAAK,CAAC,OAAY;QAEnB,MAAM,KAAK,GAAG,OAAO,CAAA;QAErB,OAAO;QACP,MAAM,YAAY,GAAG,CAAC,GAAY,EAAE,IAAa;YAChD,MAAM,IAAI,GAAG,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;YACxE,GAAG,CAAC,YAAY,CAAC;gBAChB,OAAO,EAAE,GAAG;gBACZ,IAAI,EAAE,IAAI;aACV,CAAC,CAAA;QACH,CAAC,CAAA;QAEF,OAAO,CAAC,IAAI,EAAE,MAAM;YAAO,QAAQ,CAAA;YACjC,MAAM,YAAY,GAAG,EAAE,CAAC;gBACxB,CAAC,EAAE,IAAI,CAAC,SAAS;aAClB,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC3B,CAAC,EAAE,IAAI,CAAC,WAAW;gBACnB,CAAC,EAAE,EAAE,CAAC,MAAM,MAAI,OAAA,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,EAAxC,CAAwC,CAAC;gBACzD,CAAC,EAAE,IAAI,CAAC,UAAU;gBAClB,CAAC,EAAE,EAAE,CAAC,MAAM,MAAI,OAAA,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EAAtC,CAAsC,CAAC;gBACvD,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC;aAChC,CAAC,CAAA;YACA,OAAO,YAAY,CAAA;QACrB,CAAC,CAAA;IACD,CAAC;CAEA,CAAC,CAAA\"}"}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"/**\n * 项目配置文件\n */\n// 环境配置\nexport const ENV = new UTSJSONObject({\n // 开发环境\n development: new UTSJSONObject({\n baseUrl: 'http://localhost:3000/api',\n imageBaseUrl: 'http://localhost:3000'\n }),\n // 生产环境\n production: new UTSJSONObject({\n baseUrl: 'https://api.youyijia.com/api',\n imageBaseUrl: 'https://api.youyijia.com'\n })\n}\n// 当前环境 - 切换为 'production' 上线\n);\n// 当前环境 - 切换为 'production' 上线\nexport const currentEnv = 'development';\n// 获取当前环境配置\nexport const getEnvConfig = () => {\n if (currentEnv == 'production') {\n return new UTSJSONObject({\n baseUrl: ENV.production.baseUrl,\n imageBaseUrl: ENV.production.imageBaseUrl\n });\n }\n return new UTSJSONObject({\n baseUrl: ENV.development.baseUrl,\n imageBaseUrl: ENV.development.imageBaseUrl\n });\n};\n// 是否使用Mock数据\nexport const useMock = true;\n// 分页配置\nexport const PAGE_SIZE = 10;\n// 图片上传配置\nexport const UPLOAD_CONFIG = new UTSJSONObject({\n maxSize: 5 * 1024 * 1024,\n maxCount: 9,\n accept: ['image/jpeg', 'image/png', 'image/gif']\n}\n// 缓存Key\n);\n// 缓存Key\nexport const STORAGE_KEYS = new UTSJSONObject({\n TOKEN: 'user_token',\n USER_INFO: 'user_info',\n FAVORITES: 'user_favorites',\n SEARCH_HISTORY: 'search_history'\n}\n// 沙发分类\n);\n// 沙发分类\nexport const SOFA_CATEGORIES = [\n new UTSJSONObject({ id: 'all', name: '全部' }),\n new UTSJSONObject({ id: 'leather', name: '皮沙发' }),\n new UTSJSONObject({ id: 'fabric', name: '布艺沙发' }),\n new UTSJSONObject({ id: 'functional', name: '功能沙发' }),\n new UTSJSONObject({ id: 'antique', name: '古典沙发' }),\n new UTSJSONObject({ id: 'office', name: '办公沙发' })\n];\n// 翻新服务类型\nexport const SERVICE_TYPES = [\n new UTSJSONObject({ id: 'repair', name: '局部修复', icon: '/static/icons/repair.png' }),\n new UTSJSONObject({ id: 'recolor', name: '改色翻新', icon: '/static/icons/recolor.png' }),\n new UTSJSONObject({ id: 'refurbish', name: '整体翻新', icon: '/static/icons/refurbish.png' }),\n new UTSJSONObject({ id: 'custom', name: '定制换皮', icon: '/static/icons/custom.png' })\n];\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/utils/config.uts.js.map","references":[],"uniExtApis":[],"map":"{\"version\":3,\"file\":\"config.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"config.uts\"],\"names\":[],\"mappings\":\"AAAA;;GAEG;AAEH,OAAO;AACP,MAAM,CAAC,MAAM,GAAG,qBAAG;IAClB,OAAO;IACP,WAAW,oBAAE;QACZ,OAAO,EAAE,2BAA2B;QACpC,YAAY,EAAE,uBAAuB;KACrC,CAAA;IACD,OAAO;IACP,UAAU,oBAAE;QACX,OAAO,EAAE,8BAA8B;QACvC,YAAY,EAAE,0BAA0B;KACxC,CAAA;CACD;AAED,6BAA6B;CAF5B,CAAA;AAED,6BAA6B;AAC7B,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAA;AAEvC,WAAW;AACX,MAAM,CAAC,MAAM,YAAY,GAAG;IAC3B,IAAI,UAAU,IAAI,YAAY,EAAE;QAC/B,yBAAO;YACN,OAAO,EAAE,GAAG,CAAC,UAAU,CAAC,OAAO;YAC/B,YAAY,EAAE,GAAG,CAAC,UAAU,CAAC,YAAY;SACxB,EAAA;KAClB;IACD,yBAAO;QACN,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC,OAAO;QAChC,YAAY,EAAE,GAAG,CAAC,WAAW,CAAC,YAAY;KACzB,EAAA;AACnB,CAAC,CAAA;AAED,aAAa;AACb,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,CAAA;AAE3B,OAAO;AACP,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,CAAA;AAE3B,SAAS;AACT,MAAM,CAAC,MAAM,aAAa,qBAAG;IAC5B,OAAO,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI;IACxB,QAAQ,EAAE,CAAC;IACX,MAAM,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,WAAW,CAAC;CAChD;AAED,QAAQ;CAFP,CAAA;AAED,QAAQ;AACR,MAAM,CAAC,MAAM,YAAY,qBAAG;IAC3B,KAAK,EAAE,YAAY;IACnB,SAAS,EAAE,WAAW;IACtB,SAAS,EAAE,gBAAgB;IAC3B,cAAc,EAAE,gBAAgB;CAChC;AAED,OAAO;CAFN,CAAA;AAED,OAAO;AACP,MAAM,CAAC,MAAM,eAAe,GAAG;sBAC9B,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE;sBACzB,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE;sBAC9B,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE;sBAC9B,EAAE,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE;sBAClC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE;sBAC/B,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE;CAC9B,CAAA;AAED,SAAS;AACT,MAAM,CAAC,MAAM,aAAa,GAAG;sBAC5B,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,EAAE;sBAChE,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,2BAA2B,EAAE;sBAClE,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,6BAA6B,EAAE;sBACtE,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,EAAE;CAChE,CAAA\"}"}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"import { defineComponent as _defineComponent } from 'vue';\nimport { toDisplayString as _toDisplayString, t as _t, o as _o, gei as _gei, sei as _sei } from \"vue\";\nexport default /*#__PURE__*/ _defineComponent({\n __name: 'service-card',\n props: {\n id: {},\n name: {},\n icon: {}\n },\n emits: [\"click\"],\n setup(__props, _a) {\n var __emit = _a.emit;\n const props = __props;\n const emit = __emit;\n const handleClick = () => {\n emit('click', props.id);\n };\n return (_ctx, _cache) => {\n \"raw js\";\n const __returned__ = {\n a: _ctx.icon,\n b: _t(_ctx.name),\n c: _sei(_gei(_ctx, ''), 'view'),\n d: _o(handleClick)\n };\n return __returned__;\n };\n }\n});\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/components/service-card/service-card.uvue?vue&type=script&setup=true&lang.uts.js.map","references":["D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts","D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts"],"uniExtApis":[],"map":"{\"version\":3,\"file\":\"service-card.uvue?vue&type=script&setup=true&lang.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"service-card.uvue?vue&type=script&setup=true&lang.uts\"],\"names\":[],\"mappings\":\"AAAA,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,MAAM,KAAK,CAAA;AACzD,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,CAAA;AAGrG,eAAe,aAAa,CAAA,gBAAgB,CAAC;IAC3C,MAAM,EAAE,cAAc;IACtB,KAAK,EAAE;QACL,EAAE,EAAE,EAAE;QACN,IAAI,EAAE,EAAE;QACR,IAAI,EAAE,EAAE;KACT;IACD,KAAK,EAAE,CAAC,OAAO,CAAC;IAChB,KAAK,CAAC,OAAY,EAAE,EAAgB;YAAR,MAAM,UAAA;QAEnC,MAAM,KAAK,GAAG,OAAO,CAAA;QAErB,MAAM,IAAI,GAAG,MAAM,CAAA;QAEnB,MAAM,WAAW,GAAG;YACnB,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,CAAA;QACxB,CAAC,CAAA;QAEF,OAAO,CAAC,IAAI,EAAE,MAAM;YAAO,QAAQ,CAAA;YACjC,MAAM,YAAY,GAAG;gBACrB,CAAC,EAAE,IAAI,CAAC,IAAI;gBACZ,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;gBAChB,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC;gBAC/B,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC;aACnB,CAAA;YACC,OAAO,YAAY,CAAA;QACrB,CAAC,CAAA;IACD,CAAC;CAEA,CAAC,CAAA\"}"}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"import {} from \"vue\";\nexport default defineComponent({\n onLaunch() {\n uni.__f__('log', 'at App.uvue:7', 'App Launch');\n },\n onShow() {\n uni.__f__('log', 'at App.uvue:10', 'App Show');\n },\n onHide() {\n uni.__f__('log', 'at App.uvue:13', 'App Hide');\n },\n onExit() {\n uni.__f__('log', 'at App.uvue:34', 'App Exit');\n },\n});\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/App.uvue?vue&type=script&lang.uts.js.map","references":[],"uniExtApis":["uni.__f__"],"map":"{\"version\":3,\"file\":\"App.uvue?vue&type=script&lang.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"App.uvue?vue&type=script&lang.uts\"],\"names\":[],\"mappings\":\";AAIC,+BAAe;IACd,QAAQ;QACP,GAAG,CAAC,KAAK,CAAC,KAAK,EAAC,eAAe,EAAC,YAAY,CAAC,CAAA;IAC9C,CAAC;IACD,MAAM;QACL,GAAG,CAAC,KAAK,CAAC,KAAK,EAAC,gBAAgB,EAAC,UAAU,CAAC,CAAA;IAC7C,CAAC;IACD,MAAM;QACL,GAAG,CAAC,KAAK,CAAC,KAAK,EAAC,gBAAgB,EAAC,UAAU,CAAC,CAAA;IAC7C,CAAC;IAmBD,MAAM;QACL,GAAG,CAAC,KAAK,CAAC,KAAK,EAAC,gBAAgB,EAAC,UAAU,CAAC,CAAA;IAC7C,CAAC;CACD,EAAA\"}"}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"import { defineComponent as _defineComponent } from 'vue';\nimport { unref as _unref, o as _o, toDisplayString as _toDisplayString, t as _t, e as _e } from \"vue\";\nexport default /*#__PURE__*/ _defineComponent({\n __name: 'nav-bar',\n props: {\n title: {},\n showBack: { type: Boolean },\n titleColor: {},\n bgColor: {}\n },\n setup(__props) {\n const props = __props;\n // 状态栏高度\n const statusBarHeight = ref(20);\n // 导航栏高度\n const navBarHeight = ref(44);\n onMounted(() => {\n const sysInfo = uni.getSystemInfoSync();\n statusBarHeight.value = sysInfo.statusBarHeight;\n const menuButtonInfo = uni.getMenuButtonBoundingClientRect();\n navBarHeight.value = (menuButtonInfo.top - sysInfo.statusBarHeight) * 2 + menuButtonInfo.height;\n });\n const handleBack = () => {\n uni.navigateBack(new UTSJSONObject({\n fail: () => {\n uni.switchTab({\n url: '/pages/index/index'\n });\n }\n }));\n };\n return (_ctx, _cache) => {\n \"raw js\";\n const __returned__ = _e({\n a: _ctx.showBack\n }, _ctx.showBack ? {\n b: _o(handleBack)\n } : {}, {\n c: _t(_ctx.title),\n d: _ctx.titleColor,\n e: _unref(navBarHeight) + 'px',\n f: _unref(statusBarHeight) + 'px',\n g: _unref(statusBarHeight) + _unref(navBarHeight) + 'px'\n });\n return __returned__;\n };\n }\n});\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/components/nav-bar/nav-bar.uvue?vue&type=script&setup=true&lang.uts.js.map","references":["D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts","D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts"],"uniExtApis":["uni.getSystemInfoSync","uni.getMenuButtonBoundingClientRect","uni.switchTab","uni.navigateBack"],"map":"{\"version\":3,\"file\":\"nav-bar.uvue?vue&type=script&setup=true&lang.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"nav-bar.uvue?vue&type=script&setup=true&lang.uts\"],\"names\":[],\"mappings\":\"AAAA,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,MAAM,KAAK,CAAA;AACzD,OAAO,EAAE,KAAK,IAAI,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,eAAe,IAAI,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,MAAM,KAAK,CAAA;AAGrG,eAAe,aAAa,CAAA,gBAAgB,CAAC;IAC3C,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE;QACL,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;QAC3B,UAAU,EAAE,EAAE;QACd,OAAO,EAAE,EAAE;KACZ;IACD,KAAK,CAAC,OAAY;QAEnB,MAAM,KAAK,GAAG,OAAO,CAAA;QAErB,QAAQ;QACR,MAAM,eAAe,GAAG,GAAG,CAAC,EAAE,CAAC,CAAA;QAC/B,QAAQ;QACR,MAAM,YAAY,GAAG,GAAG,CAAC,EAAE,CAAC,CAAA;QAE5B,SAAS,CAAC;YACT,MAAM,OAAO,GAAG,GAAG,CAAC,iBAAiB,EAAE,CAAA;YACvC,eAAe,CAAC,KAAK,GAAG,OAAO,CAAC,eAAe,CAAA;YAE/C,MAAM,cAAc,GAAG,GAAG,CAAC,+BAA+B,EAAE,CAAA;YAC5D,YAAY,CAAC,KAAK,GAAG,CAAC,cAAc,CAAC,GAAG,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,MAAM,CAAA;QAEhG,CAAC,CAAC,CAAA;QAEF,MAAM,UAAU,GAAG;YAClB,GAAG,CAAC,YAAY,mBAAC;gBAChB,IAAI,EAAE;oBACL,GAAG,CAAC,SAAS,CAAC;wBACb,GAAG,EAAE,oBAAoB;qBACzB,CAAC,CAAA;gBACH,CAAC;aACD,EAAC,CAAA;QACH,CAAC,CAAA;QAEF,OAAO,CAAC,IAAI,EAAE,MAAM;YAAO,QAAQ,CAAA;YACjC,MAAM,YAAY,GAAG,EAAE,CAAC;gBACxB,CAAC,EAAE,IAAI,CAAC,QAAQ;aACjB,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACjB,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC;aAClB,CAAC,CAAC,CAAC,EAAE,EAAE;gBACN,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;gBACjB,CAAC,EAAE,IAAI,CAAC,UAAU;gBAClB,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI;gBAC9B,CAAC,EAAE,MAAM,CAAC,eAAe,CAAC,GAAG,IAAI;gBACjC,CAAC,EAAE,MAAM,CAAC,eAAe,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI;aACzD,CAAC,CAAA;YACA,OAAO,YAAY,CAAA;QACrB,CAAC,CAAA;IACD,CAAC;CAEA,CAAC,CAAA\"}"}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"import { defineComponent as _defineComponent } from 'vue';\nimport { toDisplayString as _toDisplayString, t as _t, o as _o, gei as _gei, sei as _sei } from \"vue\";\nclass CaseItem extends UTS.UTSType {\n static get$UTSMetadata$() {\n return {\n kind: 2,\n get fields() {\n return {\n id: { type: String, optional: false },\n title: { type: String, optional: false },\n category: { type: String, optional: false },\n categoryName: { type: String, optional: false },\n coverImage: { type: String, optional: false },\n material: { type: String, optional: false },\n duration: { type: String, optional: false },\n price: { type: String, optional: false },\n views: { type: Number, optional: false },\n likes: { type: Number, optional: false }\n };\n },\n name: \"CaseItem\"\n };\n }\n constructor(options, metadata = CaseItem.get$UTSMetadata$(), isJSONParse = false) {\n super();\n this.__props__ = UTS.UTSType.initProps(options, metadata, isJSONParse);\n this.id = this.__props__.id;\n this.title = this.__props__.title;\n this.category = this.__props__.category;\n this.categoryName = this.__props__.categoryName;\n this.coverImage = this.__props__.coverImage;\n this.material = this.__props__.material;\n this.duration = this.__props__.duration;\n this.price = this.__props__.price;\n this.views = this.__props__.views;\n this.likes = this.__props__.likes;\n delete this.__props__;\n }\n}\nexport default /*#__PURE__*/ _defineComponent({\n __name: 'case-card',\n props: {\n caseData: {}\n },\n emits: [\"click\"],\n setup(__props, _a) {\n var __emit = _a.emit;\n // 定义Props类型\n const props = __props;\n const emit = __emit;\n const handleClick = () => {\n emit('click', props.caseData.id);\n };\n return (_ctx, _cache) => {\n \"raw js\";\n const __returned__ = {\n a: _ctx.caseData.coverImage,\n b: _t(_ctx.caseData.categoryName),\n c: _t(_ctx.caseData.title),\n d: _t(_ctx.caseData.material),\n e: _t(_ctx.caseData.duration),\n f: _t(_ctx.caseData.price),\n g: _t(_ctx.caseData.views),\n h: _t(_ctx.caseData.likes),\n i: _sei(_gei(_ctx, ''), 'view'),\n j: _o(handleClick)\n };\n return __returned__;\n };\n }\n});\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/components/case-card/case-card.uvue?vue&type=script&setup=true&lang.uts.js.map","references":["D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts","D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts"],"uniExtApis":[],"map":"{\"version\":3,\"file\":\"case-card.uvue?vue&type=script&setup=true&lang.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"case-card.uvue?vue&type=script&setup=true&lang.uts\"],\"names\":[],\"mappings\":\"AAAA,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,MAAM,KAAK,CAAA;AACzD,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,CAAA;MAEhG,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcb,eAAe,aAAa,CAAA,gBAAgB,CAAC;IAC3C,MAAM,EAAE,WAAW;IACnB,KAAK,EAAE;QACL,QAAQ,EAAE,EAAE;KACb;IACD,KAAK,EAAE,CAAC,OAAO,CAAC;IAChB,KAAK,CAAC,OAAY,EAAE,EAAgB;YAAR,MAAM,UAAA;QAEnC,YAAY;QACZ,MAAM,KAAK,GAAG,OAAO,CAAA;QAErB,MAAM,IAAI,GAAG,MAAM,CAAA;QAEnB,MAAM,WAAW,GAAG;YACnB,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;QACjC,CAAC,CAAA;QAEF,OAAO,CAAC,IAAI,EAAE,MAAM;YAAO,QAAQ,CAAA;YACjC,MAAM,YAAY,GAAG;gBACrB,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU;gBAC3B,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;gBACjC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAC1B,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAC7B,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAC7B,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAC1B,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAC1B,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAC1B,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC;gBAC/B,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC;aACnB,CAAA;YACC,OAAO,YAAY,CAAA;QACrB,CAAC,CAAA;IACD,CAAC;CAEA,CAAC,CAAA\"}"}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"import '\u0000plugin-vue:export-helper';\nimport 'uni-mp-runtime';\nimport './pages-json-js';\nimport App from './App.uvue';\nimport { createSSRApp } from 'vue';\nexport function createApp() {\n const app = createSSRApp(App);\n return {\n app\n };\n}\n;\ncreateApp().app.mount(\"#app\");\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/main.uts.js.map","references":["D:/Project/Self/优艺家沙发翻新/优艺家沙发翻新/App.uvue.ts","D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts"],"uniExtApis":[],"map":"{\"version\":3,\"file\":\"main.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"main.uts\"],\"names\":[],\"mappings\":\"AAAA,OAAO,2BAA2B,CAAC;AAAA,OAAO,gBAAgB,CAAC;AAAA,OAAO,iBAAiB,CAAC;AAAA,OAAO,GAAG,MAAM,YAAY,CAAA;AAEhH,OAAO,EAAE,YAAY,EAAE,MAAM,KAAK,CAAA;AAClC,MAAM,UAAU,SAAS;IACxB,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,CAAA;IAC7B,OAAO;QACN,GAAG;KACH,CAAA;AACF,CAAC;AAAA,CAAC;AACF,SAAS,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC\"}"}
|
||||
@@ -0,0 +1 @@
|
||||
{"code":"import { defineComponent as _defineComponent } from 'vue';\nimport { toDisplayString as _toDisplayString, t as _t, o as _o, gei as _gei, sei as _sei, e as _e } from \"vue\";\nexport default /*#__PURE__*/ _defineComponent({\n __name: 'section-header',\n props: {\n title: {},\n showMore: { type: Boolean }\n },\n emits: [\"more\"],\n setup(__props, _a) {\n var __emit = _a.emit;\n const props = __props;\n const emit = __emit;\n const handleMore = () => {\n emit('more');\n };\n return (_ctx, _cache) => {\n \"raw js\";\n const __returned__ = _e({\n a: _t(_ctx.title),\n b: _ctx.showMore\n }, _ctx.showMore ? {\n c: _o(handleMore)\n } : {}, {\n d: _sei(_gei(_ctx, ''), 'view')\n });\n return __returned__;\n };\n }\n});\n//# sourceMappingURL=D:/Project/Self/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/%E4%BC%98%E8%89%BA%E5%AE%B6%E6%B2%99%E5%8F%91%E7%BF%BB%E6%96%B0/components/section-header/section-header.uvue?vue&type=script&setup=true&lang.uts.js.map","references":["D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts","D:/Soft/HBuilderX/plugins/uniapp-cli-vite/node_modules/@vue/runtime-core/dist/runtime-core.d.ts"],"uniExtApis":[],"map":"{\"version\":3,\"file\":\"section-header.uvue?vue&type=script&setup=true&lang.uts.js\",\"sourceRoot\":\"\",\"sources\":[\"section-header.uvue?vue&type=script&setup=true&lang.uts\"],\"names\":[],\"mappings\":\"AAAA,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,MAAM,KAAK,CAAA;AACzD,OAAO,EAAE,eAAe,IAAI,gBAAgB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,IAAI,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,MAAM,KAAK,CAAA;AAG9G,eAAe,aAAa,CAAA,gBAAgB,CAAC;IAC3C,MAAM,EAAE,gBAAgB;IACxB,KAAK,EAAE;QACL,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;KAC5B;IACD,KAAK,EAAE,CAAC,MAAM,CAAC;IACf,KAAK,CAAC,OAAY,EAAE,EAAgB;YAAR,MAAM,UAAA;QAEnC,MAAM,KAAK,GAAG,OAAO,CAAA;QAErB,MAAM,IAAI,GAAG,MAAM,CAAA;QAEnB,MAAM,UAAU,GAAG;YAClB,IAAI,CAAC,MAAM,CAAC,CAAA;QACb,CAAC,CAAA;QAEF,OAAO,CAAC,IAAI,EAAE,MAAM;YAAO,QAAQ,CAAA;YACjC,MAAM,YAAY,GAAG,EAAE,CAAC;gBACxB,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;gBACjB,CAAC,EAAE,IAAI,CAAC,QAAQ;aACjB,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACjB,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC;aAClB,CAAC,CAAC,CAAC,EAAE,EAAE;gBACN,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC;aAChC,CAAC,CAAA;YACA,OAAO,YAAY,CAAA;QACrB,CAAC,CAAA;IACD,CAAC;CAEA,CAAC,CAAA\"}"}
|
||||
1
前端/unpackage/dist/dev/.sourcemap/mp-weixin/api/index.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sources":["api/index.uts"],"sourcesContent":["/**\r\n * API接口统一管理\r\n */\r\nimport { get, post } from '../utils/request.uts'\r\n\r\n/**\r\n * 获取轮播图 (临时从配置获取,后续可从后端获取)\r\n */\r\nexport const getBanners = () => {\r\n\t// 由于后端暂时没有轮播图API,先返回固定数据\r\n\treturn Promise.resolve({\r\n\t\tcode: 0,\r\n\t\tmessage: 'success',\r\n\t\tdata: [\r\n\t\t\t{\r\n\t\t\t\tid: '1',\r\n\t\t\t\timage: '/static/mock/banner1.svg',\r\n\t\t\t\ttitle: '专业沙发翻新服务',\r\n\t\t\t\tlink: '/pages/service/index'\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tid: '2',\r\n\t\t\t\timage: '/static/mock/banner2.svg', \r\n\t\t\t\ttitle: '十年品质保证',\r\n\t\t\t\tlink: '/pages/about/index'\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tid: '3',\r\n\t\t\t\timage: '/static/mock/banner3.svg',\r\n\t\t\t\ttitle: '免费上门评估', \r\n\t\t\t\tlink: '/pages/booking/index'\r\n\t\t\t}\r\n\t\t]\r\n\t})\r\n}\r\n\r\n/**\r\n * 获取案例列表\r\n */\r\nexport const getCaseList = (params ?: UTSJSONObject) => {\r\n\t// 后端使用page和limit参数,需要转换\r\n\tconst queryParams = params ? {\r\n\t\tpage: params['page'] || 1,\r\n\t\tlimit: params['pageSize'] || params['limit'] || 10,\r\n\t\tserviceType: params['category'], // 后端使用serviceType\r\n\t\tstatus: 'published' // 只获取已发布的案例\r\n\t} : {}\r\n\treturn get('/cases', queryParams as UTSJSONObject)\r\n}\r\n\r\n/**\r\n * 获取案例详情\r\n */\r\nexport const getCaseDetail = (id : string) => {\r\n\treturn get(`/cases/${id}`)\r\n}\r\n\r\n/**\r\n * 获取热门案例 (临时使用前4条案例)\r\n */\r\nexport const getHotCases = () => {\r\n\treturn get('/cases', { limit: 4, status: 'published' } as UTSJSONObject)\r\n}\r\n\r\n/**\r\n * 获取服务流程 (使用服务列表替代)\r\n */\r\nexport const getServiceProcess = () => {\r\n\treturn get('/services')\r\n}\r\n\r\n/**\r\n * 获取有效服务列表\r\n */\r\nexport const getActiveServices = () => {\r\n\treturn get('/services/active')\r\n}\r\n\r\n/**\r\n * 获取公司信息\r\n */\r\nexport const getCompanyInfo = () => {\r\n\treturn get('/company/info')\r\n}\r\n\r\n/**\r\n * 提交预约\r\n */\r\nexport const submitBooking = (data : UTSJSONObject) => {\r\n\treturn post('/booking', data)\r\n}\r\n\r\n/**\r\n * 获取用户信息\r\n */\r\nexport const getUserInfo = () => {\r\n\treturn get('/user/info')\r\n}\r\n\r\n/**\r\n * 获取用户收藏列表\r\n */\r\nexport const getFavorites = () => {\r\n\treturn get('/user/favorites')\r\n}\r\n\r\n/**\r\n * 添加收藏\r\n */\r\nexport const addFavorite = (caseId : string) => {\r\n\treturn post('/user/favorites', { caseId: caseId } as UTSJSONObject)\r\n}\r\n\r\n/**\r\n * 取消收藏\r\n */\r\nexport const removeFavorite = (caseId : string) => {\r\n\treturn post('/user/favorites/remove', { caseId: caseId } as UTSJSONObject)\r\n}\r\n\r\n/**\r\n * 获取预约记录\r\n */\r\nexport const getBookingList = () => {\r\n\treturn get('/user/bookings')\r\n}\r\n"],"names":["get","post"],"mappings":";;AAQa,MAAA,aAAa,MAAA;AAEzB,SAAO,QAAQ,QAAQ,IAAA,cAAA;AAAA,IACtB,MAAM;AAAA,IACN,SAAS;AAAA,IACT,MAAM;AAAA,MACL,IAAA,cAAA;AAAA,QACC,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,OAAO;AAAA,QACP,MAAM;AAAA,MACN,CAAA;AAAA,MACD,IAAA,cAAA;AAAA,QACC,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,OAAO;AAAA,QACP,MAAM;AAAA,MACN,CAAA;AAAA,MACD,IAAA,cAAA;AAAA,QACC,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,OAAO;AAAA,QACP,MAAM;AAAA,MACN,CAAA;AAAA,IACD;AAAA,EACD,CAAA,CAAA;AACF;AAKO,MAAM,cAAc,CAAC,SAAuB,SAAA;AAElD,QAAM,cAAc,SAAS,IAAA,cAAA;AAAA,IAC5B,MAAM,OAAO,MAAM,KAAK;AAAA,IACxB,OAAO,OAAO,UAAU,KAAK,OAAO,OAAO,KAAK;AAAA,IAChD,aAAa,OAAO,UAAU;AAAA,IAC9B,QAAQ;AAAA;AAAA,EACR,CAAA,IAAE,IAAA,cAAC,CAAA,CAAE;AACN,SAAOA,cAAG,IAAC,UAAU,WAA4B;AAClD;AAKO,MAAM,gBAAgB,CAAC,OAAW;AACxC,SAAOA,cAAG,IAAC,UAAU,EAAE,EAAE;AAC1B;AAKa,MAAA,cAAc,MAAA;AAC1B,SAAOA,cAAG,IAAC,UAAQ,IAAA,cAAE,EAAE,OAAO,GAAG,QAAQ,YAA8B,CAAA;AACxE;AAKa,MAAA,oBAAoB,MAAA;AAChC,SAAOA,cAAAA,IAAI,WAAW;AACvB;AAYa,MAAA,iBAAiB,MAAA;AAC7B,SAAOA,cAAAA,IAAI,eAAe;AAC3B;AAKO,MAAM,gBAAgB,CAAC,SAAoB;AACjD,SAAOC,cAAI,KAAC,YAAY,IAAI;AAC7B;;;;;;;;"}
|
||||
1
前端/unpackage/dist/dev/.sourcemap/mp-weixin/app.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"app.js","sources":["App.uvue","main.uts"],"sourcesContent":["<script lang=\"uts\">\r\n\r\n\r\n\r\n\texport default {\r\n\t\tonLaunch() {\r\n\t\t\tuni.__f__('log','at App.uvue:7','App Launch')\r\n\t\t},\r\n\t\tonShow() {\r\n\t\t\tuni.__f__('log','at App.uvue:10','App Show')\r\n\t\t},\r\n\t\tonHide() {\r\n\t\t\tuni.__f__('log','at App.uvue:13','App Hide')\r\n\t\t},\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\t\tonExit() {\r\n\t\t\tuni.__f__('log','at App.uvue:34','App Exit')\r\n\t\t},\r\n\t}\r\n</script>\r\n\r\n<style>\r\n\t/*每个页面公共css */\r\n\t.uni-row {\r\n\t\tflex-direction: row;\r\n\t}\r\n\r\n\t.uni-column {\r\n\t\tflex-direction: column;\r\n\t}\r\n</style>","import App from './App.uvue'\r\n\r\nimport { createSSRApp } from 'vue'\r\nexport function createApp() {\r\n\tconst app = createSSRApp(App)\r\n\treturn {\r\n\t\tapp\r\n\t}\r\n}"],"names":["defineComponent","uni","createSSRApp","App"],"mappings":";;;;;;;;;;;;AAIC,MAAA,YAAeA,8BAAA;AAAA,EACd,WAAQ;AACPC,kBAAAA,MAAI,MAAM,OAAM,iBAAgB,YAAY;AAAA,EAC5C;AAAA,EACD,SAAM;AACLA,kBAAAA,MAAI,MAAM,OAAM,kBAAiB,UAAU;AAAA,EAC3C;AAAA,EACD,SAAM;AACLA,kBAAAA,MAAI,MAAM,OAAM,kBAAiB,UAAU;AAAA,EAC3C;AAAA,EAmBD,SAAM;AACLA,kBAAAA,MAAI,MAAM,OAAM,kBAAiB,UAAU;AAAA,EAC3C;CACF;SChCe,YAAS;AACxB,QAAM,MAAMC,2BAAaC,SAAG;AAC5B,SAAO;AAAA,IACN;AAAA;AAEF;AACA,YAAY,IAAI,MAAM,MAAM;;"}
|
||||
1
前端/unpackage/dist/dev/.sourcemap/mp-weixin/common/assets.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"assets.js","sources":["static/logo.png"],"sourcesContent":["export default \"__VITE_ASSET__46719607__\""],"names":[],"mappings":";AAAA,MAAe,aAAA;;"}
|
||||
1
前端/unpackage/dist/dev/.sourcemap/mp-weixin/common/vendor.js.map
vendored
Normal file
1
前端/unpackage/dist/dev/.sourcemap/mp-weixin/components/before-after/before-after.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"before-after.js","sources":["components/before-after/before-after.uvue","../../../../Soft/HBuilderX/plugins/uniapp-cli-vite/uniComponent:/RDovUHJvamVjdC9TZWxmL-S8mOiJuuWutuaymeWPkee_u-aWsC_kvJjoibrlrrbmspnlj5Hnv7vmlrAvY29tcG9uZW50cy9iZWZvcmUtYWZ0ZXIvYmVmb3JlLWFmdGVyLnV2dWU"],"sourcesContent":["<template>\r\n\t<view class=\"before-after\">\r\n\t\t<view class=\"ba-title\" v-if=\"showTitle\">\r\n\t\t\t<text class=\"ba-title-text\">翻新前后对比</text>\r\n\t\t</view>\r\n\t\t\r\n\t\t<view class=\"ba-container\">\r\n\t\t\t<!-- 翻新前 -->\r\n\t\t\t<view class=\"ba-item\" @click=\"previewImage(beforeImage, 'before')\">\r\n\t\t\t\t<view class=\"ba-label before-label\">\r\n\t\t\t\t\t<text class=\"ba-label-text\">翻新前</text>\r\n\t\t\t\t</view>\r\n\t\t\t\t<image \r\n\t\t\t\t\tclass=\"ba-image\" \r\n\t\t\t\t\t:src=\"beforeImage\" \r\n\t\t\t\t\tmode=\"aspectFill\"\r\n\t\t\t\t></image>\r\n\t\t\t</view>\r\n\t\t\t\r\n\t\t\t<!-- 箭头 -->\r\n\t\t\t<view class=\"ba-arrow\">\r\n\t\t\t\t<text class=\"ba-arrow-text\">→</text>\r\n\t\t\t</view>\r\n\t\t\t\r\n\t\t\t<!-- 翻新后 -->\r\n\t\t\t<view class=\"ba-item\" @click=\"previewImage(afterImage, 'after')\">\r\n\t\t\t\t<view class=\"ba-label after-label\">\r\n\t\t\t\t\t<text class=\"ba-label-text\">翻新后</text>\r\n\t\t\t\t</view>\r\n\t\t\t\t<image \r\n\t\t\t\t\tclass=\"ba-image\" \r\n\t\t\t\t\t:src=\"afterImage\" \r\n\t\t\t\t\tmode=\"aspectFill\"\r\n\t\t\t\t></image>\r\n\t\t\t</view>\r\n\t\t</view>\r\n\t</view>\r\n</template>\r\n\r\n<script setup lang=\"uts\">\r\n\tconst props = defineProps<{\r\n\t\tbeforeImage : string\r\n\t\tafterImage : string\r\n\t\tshowTitle ?: boolean\r\n\t}>()\r\n\t\r\n\t// 预览图片\r\n\tconst previewImage = (url : string, type : string) => {\r\n\t\tconst urls = type == 'before' ? [props.beforeImage] : [props.afterImage]\r\n\t\tuni.previewImage({\r\n\t\t\tcurrent: url,\r\n\t\t\turls: urls\r\n\t\t})\r\n\t}\r\n</script>\r\n\r\n<style lang=\"scss\">\r\n\t.before-after {\r\n\t\tbackground-color: #ffffff;\r\n\t\tborder-radius: 16rpx;\r\n\t\tpadding: 24rpx;\r\n\t\tmargin-bottom: 24rpx;\r\n\t}\r\n\t\r\n\t.ba-title {\r\n\t\tmargin-bottom: 24rpx;\r\n\t}\r\n\t\r\n\t.ba-title-text {\r\n\t\tfont-size: 32rpx;\r\n\t\tfont-weight: 600;\r\n\t\tcolor: #333333;\r\n\t}\r\n\t\r\n\t.ba-container {\r\n\t\tflex-direction: row;\r\n\t\talign-items: center;\r\n\t\tjustify-content: space-between;\r\n\t}\r\n\t\r\n\t.ba-item {\r\n\t\tflex: 1;\r\n\t\tposition: relative;\r\n\t\tborder-radius: 12rpx;\r\n\t\toverflow: hidden;\r\n\t}\r\n\t\r\n\t.ba-image {\r\n\t\twidth: 100%;\r\n\t\theight: 280rpx;\r\n\t\tborder-radius: 12rpx;\r\n\t}\r\n\t\r\n\t.ba-label {\r\n\t\tposition: absolute;\r\n\t\tbottom: 16rpx;\r\n\t\tleft: 16rpx;\r\n\t\tpadding: 8rpx 16rpx;\r\n\t\tborder-radius: 8rpx;\r\n\t\tz-index: 1;\r\n\t}\r\n\t\r\n\t.before-label {\r\n\t\tbackground-color: rgba(144, 147, 153, 0.9);\r\n\t}\r\n\t\r\n\t.after-label {\r\n\t\tbackground-color: rgba(212, 165, 116, 0.9);\r\n\t}\r\n\t\r\n\t.ba-label-text {\r\n\t\tfont-size: 22rpx;\r\n\t\tcolor: #ffffff;\r\n\t}\r\n\t\r\n\t.ba-arrow {\r\n\t\tpadding: 0 16rpx;\r\n\t}\r\n\t\r\n\t.ba-arrow-text {\r\n\t\tfont-size: 40rpx;\r\n\t\tcolor: #D4A574;\r\n\t\tfont-weight: bold;\r\n\t}\r\n</style>\r\n","import Component from 'D:/Project/Self/优艺家沙发翻新/优艺家沙发翻新/components/before-after/before-after.uvue'\nwx.createComponent(Component)"],"names":["uni","Component"],"mappings":";;;;;;;;;;AAwCC,UAAM,QAAQ;AAOd,UAAM,eAAe,CAAC,KAAc,SAAa;AAChD,YAAM,OAAO,QAAQ,WAAW,CAAC,MAAM,WAAW,IAAI,CAAC,MAAM,UAAU;AACvEA,oBAAAA,MAAI,aAAa;AAAA,QAChB,SAAS;AAAA,QACT;AAAA,MACA,CAAA;AAAA,IACF;;;;;;;;;;;;;;;;;;;;ACpDD,GAAG,gBAAgBC,SAAS;"}
|
||||
1
前端/unpackage/dist/dev/.sourcemap/mp-weixin/components/case-card/case-card.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"case-card.js","sources":["components/case-card/case-card.uvue","../../../../Soft/HBuilderX/plugins/uniapp-cli-vite/uniComponent:/RDovUHJvamVjdC9TZWxmL-S8mOiJuuWutuaymeWPkee_u-aWsC_kvJjoibrlrrbmspnlj5Hnv7vmlrAvY29tcG9uZW50cy9jYXNlLWNhcmQvY2FzZS1jYXJkLnV2dWU"],"sourcesContent":["<template>\r\n\t<view class=\"case-card\" @click=\"handleClick\">\r\n\t\t<!-- 封面图 -->\r\n\t\t<view class=\"card-image-wrapper\">\r\n\t\t\t<image \r\n\t\t\t\tclass=\"card-image\" \r\n\t\t\t\t:src=\"caseData.coverImage\" \r\n\t\t\t\tmode=\"aspectFill\"\r\n\t\t\t></image>\r\n\t\t\t<view class=\"card-category\">{{ caseData.categoryName }}</view>\r\n\t\t</view>\r\n\t\t\r\n\t\t<!-- 内容区域 -->\r\n\t\t<view class=\"card-content\">\r\n\t\t\t<text class=\"card-title\">{{ caseData.title }}</text>\r\n\t\t\t<view class=\"card-info\">\r\n\t\t\t\t<view class=\"info-item\">\r\n\t\t\t\t\t<text class=\"info-label\">材质:</text>\r\n\t\t\t\t\t<text class=\"info-value\">{{ caseData.material }}</text>\r\n\t\t\t\t</view>\r\n\t\t\t\t<view class=\"info-item\">\r\n\t\t\t\t\t<text class=\"info-label\">工期:</text>\r\n\t\t\t\t\t<text class=\"info-value\">{{ caseData.duration }}</text>\r\n\t\t\t\t</view>\r\n\t\t\t</view>\r\n\t\t\t<view class=\"card-footer\">\r\n\t\t\t\t<text class=\"card-price\">{{ caseData.price }}</text>\r\n\t\t\t\t<view class=\"card-stats\">\r\n\t\t\t\t\t<text class=\"stat-item\">👁 {{ caseData.views }}</text>\r\n\t\t\t\t\t<text class=\"stat-item\">❤ {{ caseData.likes }}</text>\r\n\t\t\t\t</view>\r\n\t\t\t</view>\r\n\t\t</view>\r\n\t</view>\r\n</template>\r\n\r\n<script setup lang=\"uts\">\r\n\t// 定义Props类型\r\n\ttype CaseItem = {\r\n\t\tid : string\r\n\t\ttitle : string\r\n\t\tcategory : string\r\n\t\tcategoryName : string\r\n\t\tcoverImage : string\r\n\t\tmaterial : string\r\n\t\tduration : string\r\n\t\tprice : string\r\n\t\tviews : number\r\n\t\tlikes : number\r\n\t}\r\n\t\r\n\tconst props = defineProps<{\r\n\t\tcaseData : CaseItem\r\n\t}>()\r\n\t\r\n\tconst emit = defineEmits<{\r\n\t\t(e : 'click', id : string) : void\r\n\t}>()\r\n\t\r\n\tconst handleClick = () => {\r\n\t\temit('click', props.caseData.id)\r\n\t}\r\n</script>\r\n\r\n<style lang=\"scss\">\r\n\t.case-card {\r\n\t\tbackground-color: #ffffff;\r\n\t\tborder-radius: 16rpx;\r\n\t\toverflow: hidden;\r\n\t\tbox-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);\r\n\t\tmargin-bottom: 24rpx;\r\n\t}\r\n\t\r\n\t.card-image-wrapper {\r\n\t\tposition: relative;\r\n\t\twidth: 100%;\r\n\t\theight: 360rpx;\r\n\t}\r\n\t\r\n\t.card-image {\r\n\t\twidth: 100%;\r\n\t\theight: 100%;\r\n\t}\r\n\t\r\n\t.card-category {\r\n\t\tposition: absolute;\r\n\t\ttop: 16rpx;\r\n\t\tleft: 16rpx;\r\n\t\tbackground-color: rgba(212, 165, 116, 0.9);\r\n\t\tcolor: #ffffff;\r\n\t\tfont-size: 22rpx;\r\n\t\tpadding: 8rpx 16rpx;\r\n\t\tborder-radius: 8rpx;\r\n\t}\r\n\t\r\n\t.card-content {\r\n\t\tpadding: 24rpx;\r\n\t}\r\n\t\r\n\t.card-title {\r\n\t\tfont-size: 32rpx;\r\n\t\tfont-weight: 600;\r\n\t\tcolor: #333333;\r\n\t\tmargin-bottom: 16rpx;\r\n\t\tlines: 1;\r\n\t\ttext-overflow: ellipsis;\r\n\t}\r\n\t\r\n\t.card-info {\r\n\t\tmargin-bottom: 16rpx;\r\n\t}\r\n\t\r\n\t.info-item {\r\n\t\tflex-direction: row;\r\n\t\tmargin-bottom: 8rpx;\r\n\t}\r\n\t\r\n\t.info-label {\r\n\t\tfont-size: 26rpx;\r\n\t\tcolor: #909399;\r\n\t}\r\n\t\r\n\t.info-value {\r\n\t\tfont-size: 26rpx;\r\n\t\tcolor: #606266;\r\n\t}\r\n\t\r\n\t.card-footer {\r\n\t\tflex-direction: row;\r\n\t\tjustify-content: space-between;\r\n\t\talign-items: center;\r\n\t\tmargin-top: 16rpx;\r\n\t\tpadding-top: 16rpx;\r\n\t\tborder-top-width: 1rpx;\r\n\t\tborder-top-style: solid;\r\n\t\tborder-top-color: #EBEEF5;\r\n\t}\r\n\t\r\n\t.card-price {\r\n\t\tfont-size: 36rpx;\r\n\t\tfont-weight: 600;\r\n\t\tcolor: #D4A574;\r\n\t}\r\n\t\r\n\t.card-stats {\r\n\t\tflex-direction: row;\r\n\t}\r\n\t\r\n\t.stat-item {\r\n\t\tfont-size: 24rpx;\r\n\t\tcolor: #909399;\r\n\t\tmargin-left: 24rpx;\r\n\t}\r\n</style>\r\n","import Component from 'D:/Project/Self/优艺家沙发翻新/优艺家沙发翻新/components/case-card/case-card.uvue'\nwx.createComponent(Component)"],"names":["Component"],"mappings":";;MAsCM,iBAAQ,IAAA,QAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAab,UAAM,QAAQ;AAId,UAAM,OAAO;AAIb,UAAM,cAAc,MAAA;AACnB,WAAK,SAAS,MAAM,SAAS,EAAE;AAAA,IAChC;;;;;;;;;;;;;;;;;;;AC5DD,GAAG,gBAAgBA,SAAS;"}
|
||||