feat(report): 添加报告相关组件和 API
- 新增报告 API 文件,定义了多个后端请求方法 - 实现了多个报告相关组件,包括体型选择、基因风格、面部色彩维度等 - 添加了自定义 Swiper 组件和标签页组件 - 新增了一些工具函数,如数字转中文、获取最大元素等
This commit is contained in:
@@ -6,3 +6,8 @@ https://doc.iocoder.cn/quick-start-front/#_2-uni-app-%E5%95%86%E5%9F%8E%E7%A7%BB
|
|||||||
|
|
||||||
|
|
||||||
开源地址: https://github.com/yudaocode/yudao-mall-uniapp?tab=readme-ov-file
|
开源地址: https://github.com/yudaocode/yudao-mall-uniapp?tab=readme-ov-file
|
||||||
|
|
||||||
|
|
||||||
|
群公告
|
||||||
|
【金山文档 | WPS云文档】 【美搭小程序模板】
|
||||||
|
https://www.kdocs.cn/l/cbQW5YSrFJP7
|
||||||
|
|||||||
+12
@@ -32,6 +32,18 @@
|
|||||||
"group": "商城"
|
"group": "商城"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/report/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "报告",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"sync": true,
|
||||||
|
"title": "报告",
|
||||||
|
"group": "商城"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/index/category",
|
"path": "pages/index/category",
|
||||||
"style": {
|
"style": {
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { request } from "../../http/index.js";
|
||||||
|
let baseUrl = "https://llshop.zglvling.com/soapi/";
|
||||||
|
// let baseUrl = "https://test.zglvling.com/soapi/";
|
||||||
|
// const praiseTextApi = (params) =>
|
||||||
|
// request.post(baseUrl + "ai/praiseTexttwo", params);
|
||||||
|
const userFaceInoApi = (params) =>
|
||||||
|
request.post(baseUrl + "userinfo/get_user_face_info", params);
|
||||||
|
const hairstyleListApi = (params) =>
|
||||||
|
request.post(
|
||||||
|
"https://puton.meidaojia.com/api/v1/test/getHairDetails",
|
||||||
|
params
|
||||||
|
);
|
||||||
|
const exchangeHairApi = (params) =>
|
||||||
|
request.post("https://puton.meidaojia.com/api/v1/test/exchangeHair", params);
|
||||||
|
const exchangeHairHistoryByTaskId = (params) =>
|
||||||
|
request.post(
|
||||||
|
"https://puton.meidaojia.com/api/v1/test/exchangeHairHistoryByTaskId",
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
|
const wardrobeListApi = (params) =>
|
||||||
|
request.post(baseUrl + "wardrobe/clothes_list", params);
|
||||||
|
|
||||||
|
const getStyleApi = (params) =>
|
||||||
|
request.post(baseUrl + "userinfo/get_user_face_info", params);
|
||||||
|
|
||||||
|
const hairStyleListApi = (params) =>
|
||||||
|
request.post(
|
||||||
|
"https://puton.meidaojia.com/api/v1/test/getHairDetails",
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
|
const addHairApi = (params) =>
|
||||||
|
request.post(baseUrl + "userinfo/add_hair_data", params);
|
||||||
|
|
||||||
|
const getHairApi = (params) =>
|
||||||
|
request.post(baseUrl + "userinfo/get_hair_data", params);
|
||||||
|
|
||||||
|
export {
|
||||||
|
praiseTextApi,
|
||||||
|
userFaceInoApi,
|
||||||
|
hairstyleListApi,
|
||||||
|
exchangeHairApi,
|
||||||
|
exchangeHairHistoryByTaskId,
|
||||||
|
wardrobeListApi,
|
||||||
|
getStyleApi,
|
||||||
|
// goWXtApi,
|
||||||
|
// clothesListApi,
|
||||||
|
hairStyleListApi,
|
||||||
|
addHairApi,
|
||||||
|
getHairApi,
|
||||||
|
};
|
||||||
@@ -0,0 +1,363 @@
|
|||||||
|
<template>
|
||||||
|
<view class="uni-margin-wrap">
|
||||||
|
<view class="flex">
|
||||||
|
<text>搭配趋势</text>
|
||||||
|
|
||||||
|
<img src="./arrow-icon.png" class="arrow-icon" />
|
||||||
|
</view>
|
||||||
|
<swiper
|
||||||
|
@animationfinish="animationfinish"
|
||||||
|
class="swiper"
|
||||||
|
circular
|
||||||
|
:indicator-dots="false"
|
||||||
|
:autoplay="true"
|
||||||
|
>
|
||||||
|
<swiper-item v-for="item in props.items" :key="item.id">
|
||||||
|
<view class="swiper-item">
|
||||||
|
<view class="left" @click="toGoodsInfo(item[0])">
|
||||||
|
<img :src="item[0].file_name" class="left-img" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<div class="right">
|
||||||
|
<view class="good-img">
|
||||||
|
<div class="good-img-box good-img-top"></div>
|
||||||
|
<div
|
||||||
|
class="good-img-box good-img-bottom"
|
||||||
|
@click="toGoodsInfo(item[4])"
|
||||||
|
>
|
||||||
|
<img :src="item[4].file_name" class="good-img-detail" />
|
||||||
|
</div>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="btn" @click="toMini(item[4])">一键试穿</view>
|
||||||
|
</div>
|
||||||
|
</view>
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
|
|
||||||
|
<view class="dots">
|
||||||
|
<view
|
||||||
|
class="dot"
|
||||||
|
:class="{ active: index == current }"
|
||||||
|
v-for="(item, index) in props.items"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineProps, ref, watchEffect } from "vue";
|
||||||
|
|
||||||
|
const autoplay = ref(true);
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
items: {
|
||||||
|
type: Array,
|
||||||
|
default: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const current = ref(0);
|
||||||
|
|
||||||
|
const animationfinish = (e) => {
|
||||||
|
// console.log('e: ', e.detail.current);
|
||||||
|
current.value = e.detail.current;
|
||||||
|
};
|
||||||
|
|
||||||
|
// weixin://dl/business/?appid=wx32d8423809b8d7d&path=/pages/fitRoom/index&query=id=
|
||||||
|
const toMini = (item) => {
|
||||||
|
console.log(wx, "微信miniProgram");
|
||||||
|
wx.miniProgram.navigateTo({
|
||||||
|
url: `/pages/fitRoom/index?id=${item.id}`,
|
||||||
|
success: function () {
|
||||||
|
// alert('成功')
|
||||||
|
},
|
||||||
|
fail(error) {
|
||||||
|
// alert('错误', error)
|
||||||
|
console.log("navigateTo error: ", error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
/**小程序内跳转 */
|
||||||
|
// uni.webView.navigateTo({ url: `/pages/fitRoom/index&id=${item.id}` });
|
||||||
|
// location.href = `weixin://dl/business/?appid=wx32d8423809b8d7d&path=/pages/fitRoom/index&id=${item.id}`
|
||||||
|
};
|
||||||
|
const toGoodsInfo = (item) => {
|
||||||
|
wx.miniProgram.navigateTo({
|
||||||
|
url: "/pages/goods/goodsDetail/index?goods_id=" + item.goods_id,
|
||||||
|
});
|
||||||
|
//先去请求后端接口获取数据
|
||||||
|
// uni.request({
|
||||||
|
// url: "https://llshop.zglvling.com/soapi/text/ceshi1",
|
||||||
|
// method: "POST",
|
||||||
|
// data: {
|
||||||
|
// id: 123
|
||||||
|
// },
|
||||||
|
// header: {
|
||||||
|
// "Content-Type": "application/json"
|
||||||
|
// },
|
||||||
|
// success: (res) => {
|
||||||
|
// const config = res.data;
|
||||||
|
|
||||||
|
// // #ifdef H5
|
||||||
|
// // 1. 检查是否在微信浏览器中
|
||||||
|
// const isWechatBrowser = /MicroMessenger/i.test(navigator.userAgent);
|
||||||
|
// console.log("是否在微信浏览器", isWechatBrowser);
|
||||||
|
|
||||||
|
// if (!isWechatBrowser) {
|
||||||
|
// uni.showToast({
|
||||||
|
// title: "请在微信中打开",
|
||||||
|
// icon: "none"
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// wx.config({
|
||||||
|
// debug: true, // 调试模式
|
||||||
|
// appId: config.appId,
|
||||||
|
// timestamp: config.timestamp,
|
||||||
|
// nonceStr: config.nonceStr,
|
||||||
|
// signature: config.signature,
|
||||||
|
// jsApiList: ["miniProgram"], // 需要用到的 API
|
||||||
|
// });
|
||||||
|
// wx.ready(() => {
|
||||||
|
// wx.miniProgram.navigateTo({
|
||||||
|
// path: "/pages/goods/goodsDetail/index",
|
||||||
|
// success: () => console.log("跳转成功"),
|
||||||
|
// fail: (err) => {
|
||||||
|
// console.error("跳转失败:", err);
|
||||||
|
// uni.showToast({
|
||||||
|
// title: "跳转失败",
|
||||||
|
// icon: "none"
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // #endif
|
||||||
|
// },
|
||||||
|
// fail: (err) => {
|
||||||
|
// console.error("请求失败:", err);
|
||||||
|
// uni.showToast({
|
||||||
|
// title: "网络错误",
|
||||||
|
// icon: "none"
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// 初始化微信 JS-SDK
|
||||||
|
// function initWechatSDK(config) {
|
||||||
|
// console.log("阿拉啦啦", wx);
|
||||||
|
// wx.config({
|
||||||
|
// debug: true, // 调试模式
|
||||||
|
// appId: config.appId,
|
||||||
|
// timestamp: config.timestamp,
|
||||||
|
// nonceStr: config.nonceStr,
|
||||||
|
// signature: config.signature,
|
||||||
|
// jsApiList: ["miniProgram"], // 需要用到的 API
|
||||||
|
// });
|
||||||
|
|
||||||
|
// wx.ready(() => {
|
||||||
|
// wx.miniProgram.navigateTo({
|
||||||
|
// path: "/pages/goods/goodsDetail/index",
|
||||||
|
// success: () => console.log("跳转成功"),
|
||||||
|
// fail: (err) => {
|
||||||
|
// console.error("跳转失败:", err);
|
||||||
|
// uni.showToast({
|
||||||
|
// title: "跳转失败",
|
||||||
|
// icon: "none"
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// wx.error((err) => {
|
||||||
|
// console.error("微信 SDK 配置失败:", err);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// uni.request({
|
||||||
|
// url: 'https://llshop.zglvling.com/soapi/text/ceshi1', // 接口地址
|
||||||
|
// method: 'POST', // 请求方式(GET/POST/PUT/DELETE)
|
||||||
|
// data: { id: 123 }, // 请求参数(GET 用 params,POST 用 data)
|
||||||
|
// header: {
|
||||||
|
// 'Content-Type': 'application/json', // 请求头
|
||||||
|
// },
|
||||||
|
// success: (res) => {
|
||||||
|
// console.log('请求成功:', res.data);
|
||||||
|
// var config = res.data;
|
||||||
|
|
||||||
|
// const wx = window.wx;
|
||||||
|
// // 动态加载微信 JS-SDK
|
||||||
|
// if (typeof wx === 'undefined') {
|
||||||
|
// const script = document.createElement('script');
|
||||||
|
// script.src = 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js';
|
||||||
|
// script.onload = () => {
|
||||||
|
// initWechatSDK(config); // 加载完成后初始化
|
||||||
|
// };
|
||||||
|
// document.body.appendChild(script);
|
||||||
|
// } else {
|
||||||
|
// initWechatSDK(config); // 如果已存在,直接初始化
|
||||||
|
// }
|
||||||
|
// wx.config({
|
||||||
|
// debug: true, // 调试模式
|
||||||
|
// appId: config.appId,
|
||||||
|
// timestamp: config.timestamp,
|
||||||
|
// nonceStr: config.nonceStr,
|
||||||
|
// signature: config.signature,
|
||||||
|
// jsApiList: ['miniProgram'], // 需要用到的 API
|
||||||
|
// });
|
||||||
|
// // 4. 配置完成后跳转小程序
|
||||||
|
// wx.ready(() => {
|
||||||
|
// wx.miniProgram.navigateTo({
|
||||||
|
// path: '/pages/home/index?id=123', // 小程序路径 + 参数
|
||||||
|
// success: () => console.log('跳转成功'),
|
||||||
|
// fail: (err) => {
|
||||||
|
// console.error('跳转失败:', err);
|
||||||
|
// uni.showToast({ title: '跳转失败,请在微信中打开', icon: 'none' });
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// fail: (err) => {
|
||||||
|
// console.error('请求失败:', err);
|
||||||
|
// uni.showToast({ title: '网络错误', icon: 'none' });
|
||||||
|
// },
|
||||||
|
// complete: () => {
|
||||||
|
// console.log('请求完成');
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const appid = 'wx32d8423809b8d7d4';
|
||||||
|
// const path = `/pages/goods/goodsDetail/index`;
|
||||||
|
// const scheme = `weixin://dl/business/?appid=${appid}&path=${encodeURIComponent(path)}`;
|
||||||
|
// console.log('地址: ', scheme);
|
||||||
|
// // 方法1:动态创建 <a> 标签(兼容 iOS)
|
||||||
|
// const a = document.createElement('a');
|
||||||
|
// a.href = scheme;
|
||||||
|
// a.style.display = 'none';
|
||||||
|
// document.body.appendChild(a);
|
||||||
|
// a.click();
|
||||||
|
// document.body.removeChild(a);
|
||||||
|
// location.href = scheme;
|
||||||
|
|
||||||
|
// // 可添加失败回调(通过监听页面跳转失败)
|
||||||
|
// setTimeout(() => {
|
||||||
|
// alert('跳转失败,请手动打开小程序');
|
||||||
|
// }, 1000);
|
||||||
|
|
||||||
|
// location.href = `weixin://dl/business/?appid=wx32d8423809b8d7d&path=/pages/fitRoom/index&id=${item.id}`
|
||||||
|
};
|
||||||
|
|
||||||
|
// }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.uni-margin-wrap {
|
||||||
|
width: 100%;
|
||||||
|
/* height: 330px; */
|
||||||
|
padding: 16px 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #031c24;
|
||||||
|
width: 336px;
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex .arrow-icon {
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper {
|
||||||
|
width: 336px;
|
||||||
|
height: 330px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-item {
|
||||||
|
width: 336px;
|
||||||
|
height: 330px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-item .left {
|
||||||
|
width: 223px;
|
||||||
|
height: 330px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-item .left .left-img {
|
||||||
|
height: 330px;
|
||||||
|
/* width: 100%; */
|
||||||
|
object-fit: scale-down;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-item .right {
|
||||||
|
width: 102px;
|
||||||
|
height: 330px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-item .right .good-img {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-item .right .good-img .good-img-box {
|
||||||
|
height: 134px;
|
||||||
|
overflow: hidden;
|
||||||
|
/* background-color: red; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-item .right .good-img .good-img-box .good-img-detail {
|
||||||
|
height: 100%;
|
||||||
|
object-fit: scale-down;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-item .right .btn {
|
||||||
|
height: 24px;
|
||||||
|
background-color: #031c24;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dots {
|
||||||
|
height: 22px;
|
||||||
|
margin-top: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dots .dot {
|
||||||
|
width: 5px;
|
||||||
|
height: 2px;
|
||||||
|
background-color: #edf0f0;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dots .dot:first-of-type {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dots .active {
|
||||||
|
width: 10px;
|
||||||
|
background-color: #031c24;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 343 B |
@@ -0,0 +1,113 @@
|
|||||||
|
<script setup>
|
||||||
|
import CheckBox from "./Checkbox.vue";
|
||||||
|
import { defineProps, ref, watch } from "vue";
|
||||||
|
const props = defineProps({
|
||||||
|
bodyType: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
|
||||||
|
gender: {
|
||||||
|
type: String,
|
||||||
|
default: "女",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const imageUrl = ref("");
|
||||||
|
const jsonWoman = ref([
|
||||||
|
{
|
||||||
|
value: "苹",
|
||||||
|
type: "苹果型(O型)",
|
||||||
|
pic: "https://oss.yz.zglvling.com/user/image/2025-07-24/1753325126711833.png"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
value: "梨",
|
||||||
|
type: "梨型(A型)",
|
||||||
|
pic: "https://oss.yz.zglvling.com/user/image/2025-07-24/1753325141105794.png"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
value: "沙",
|
||||||
|
type: "沙漏型(X型)",
|
||||||
|
pic: "https://oss.yz.zglvling.com/user/image/2025-07-24/1753325153784393.png"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
value: "倒三角形(T型)",
|
||||||
|
type: "倒三角形(T型)",
|
||||||
|
pic: "https://oss.yz.zglvling.com/user/image/2025-07-24/1753325170869093.png"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
value: "H",
|
||||||
|
type: "矩形(H型)",
|
||||||
|
pic: "https://oss.yz.zglvling.com/user/image/2025-07-24/1753325184784878.png"
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const jsonMan = ref([
|
||||||
|
{
|
||||||
|
value: "苹",
|
||||||
|
type: "苹果型(O型)",
|
||||||
|
pic: "https://oss.yz.zglvling.com/user/image/2025-07-24/1753324878640920.png",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
value: "苹",
|
||||||
|
type: "梨型(A型)",
|
||||||
|
pic: "https://oss.yz.zglvling.com/user/image/2025-07-24/1753324934565088.png",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
value: "H",
|
||||||
|
type: "矩形(H型)",
|
||||||
|
pic: "https://oss.yz.zglvling.com/user/image/2025-07-24/1753324951511967.png"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
value: "倒",
|
||||||
|
type: "倒三角形(T型)",
|
||||||
|
pic: "https://oss.yz.zglvling.com/user/image/2025-07-24/1753324969423848.png"
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const json = ref([]);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
[() => props.bodyType, () => props.gender],
|
||||||
|
([newVal, gender]) => {
|
||||||
|
console.log("props.bodyType newVal: xxxxxxxxxxxxx", newVal);
|
||||||
|
|
||||||
|
json.value = gender === "男" ? jsonMan.value : jsonWoman.value;
|
||||||
|
console.log("json.value: ", json.value);
|
||||||
|
|
||||||
|
json.value = json.value.map((item) => ({
|
||||||
|
...item,
|
||||||
|
checked: newVal.includes(item.type),
|
||||||
|
}));
|
||||||
|
|
||||||
|
imageUrl.value = json.value.find((item) => newVal.includes(item.type))?.pic;
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
体型:
|
||||||
|
<template v-for="item of json">
|
||||||
|
<CheckBox :checked="item.checked">{{ item.type }}</CheckBox>
|
||||||
|
</template>
|
||||||
|
<image class="figure" :src="imageUrl" mode="widthFix"></image>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
padding: 20px 26px;
|
||||||
|
}
|
||||||
|
.figure {
|
||||||
|
margin-top: 26px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<script setup>
|
||||||
|
import { defineProps, ref, watch } from "vue";
|
||||||
|
import { styleJson } from "./json/style";
|
||||||
|
const styleImage = ref("");
|
||||||
|
const psychologicalStyleImage = ref("");
|
||||||
|
const props = defineProps({
|
||||||
|
styleType: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
|
||||||
|
psychologicalStyle: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
|
||||||
|
gender: {
|
||||||
|
type: String,
|
||||||
|
default: "女",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
watch(
|
||||||
|
() => props.styleType,
|
||||||
|
(newVal) => {
|
||||||
|
styleImage.value = styleJson.find((item) => item.type === newVal)?.pic;
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => props.psychologicalStyle,
|
||||||
|
(newVal) => {
|
||||||
|
psychologicalStyleImage.value = styleJson.find(
|
||||||
|
(item) => item.type === newVal
|
||||||
|
)?.pic;
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="stylePicture">
|
||||||
|
<!-- 第一张和第二张都是9选一展示,如果一样就显示一张就行-->
|
||||||
|
<template
|
||||||
|
v-if="styleImage === psychologicalStyleImage && props.gender == '女'"
|
||||||
|
>
|
||||||
|
<image class="bottomMap" :src="styleImage" mode="widthFix"></image>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<template v-if="props.gender == '女'">
|
||||||
|
<image class="bottomMap" :src="styleImage" mode="widthFix"></image>
|
||||||
|
<view>
|
||||||
|
<view class="psychologicalStyle">心理风格</view>
|
||||||
|
<image
|
||||||
|
class="bottomMap"
|
||||||
|
:src="psychologicalStyleImage"
|
||||||
|
mode="widthFix"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
<view class="">
|
||||||
|
<image
|
||||||
|
class="bottomMap"
|
||||||
|
:src="'https://llyz.oss-cn-beijing.aliyuncs.com/diagnostic-report-h5/bottomMap.png'"
|
||||||
|
mode="widthFix"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
<view style="padding-left: 20px; margin-top: 56px">
|
||||||
|
注:人工智能大模型在计算中可能存在0.5%的误差。
|
||||||
|
</view>
|
||||||
|
<view class="reportCompleted">
|
||||||
|
<image
|
||||||
|
class="bottomMap"
|
||||||
|
:src="'/static/reportCompleted.png'"
|
||||||
|
mode="widthFix"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.psychologicalStyle {
|
||||||
|
color: #031c24;
|
||||||
|
padding-top: 23px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
.stylePicture {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.bottomMap {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reportCompleted {
|
||||||
|
padding: 0 20px;
|
||||||
|
padding-bottom: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "CheckBox",
|
||||||
|
props: {
|
||||||
|
checked: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="checkbox">
|
||||||
|
<image
|
||||||
|
class="selectBox"
|
||||||
|
v-if="checked"
|
||||||
|
:src="'https://llyz.oss-cn-beijing.aliyuncs.com/diagnostic-report-h5/选中.svg'"
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
class="selectBox"
|
||||||
|
v-else
|
||||||
|
src="https://llyz.oss-cn-beijing.aliyuncs.com/diagnostic-report-h5/未选中.svg"
|
||||||
|
/>
|
||||||
|
<slot></slot>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.checkbox {
|
||||||
|
display: inline;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: 21px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectBox {
|
||||||
|
width: 13px;
|
||||||
|
height: 13px;
|
||||||
|
padding-right: 1px;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
<script setup>
|
||||||
|
import {defineProps} from "vue";
|
||||||
|
import ProgressBar from "./ProgressBar.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
hue: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
// 色度
|
||||||
|
chroma: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
// 亮度
|
||||||
|
luminance: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<view class="title">面部色彩维度</view>
|
||||||
|
<view class="title-en">COLOUR DIMENSIONS</view>
|
||||||
|
<view>
|
||||||
|
<view class="title">色相</view>
|
||||||
|
<view class="text-en">HUE</view>
|
||||||
|
<ProgressBar :step="Number(hue)"/>
|
||||||
|
<view class="desc">
|
||||||
|
<view class="flex">
|
||||||
|
<view>冷色</view>
|
||||||
|
<view>暖色</view>
|
||||||
|
</view>
|
||||||
|
<view class="flex flex-en">
|
||||||
|
<view>Cool</view>
|
||||||
|
<view>Warn</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view>
|
||||||
|
<view class="title">亮度</view>
|
||||||
|
<view class="text-en">VALUE</view>
|
||||||
|
<ProgressBar :step="Number(chroma)"/>
|
||||||
|
<view class="desc">
|
||||||
|
<view class="flex">
|
||||||
|
<view>暗沉</view>
|
||||||
|
<view>白皙</view>
|
||||||
|
</view>
|
||||||
|
<view class="flex flex-en">
|
||||||
|
<view>Dark</view>
|
||||||
|
<view>White</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view>
|
||||||
|
<view class="title">色度</view>
|
||||||
|
<view class="text-en">CHROMA</view>
|
||||||
|
<ProgressBar :step="Number(luminance)"/>
|
||||||
|
<view class="desc">
|
||||||
|
<view class="flex">
|
||||||
|
<view>柔和</view>
|
||||||
|
<view>鲜艳</view>
|
||||||
|
</view>
|
||||||
|
<view class="flex flex-en">
|
||||||
|
<view>Soft</view>
|
||||||
|
<view>Bright</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- <ProgressBar :step="1" />-->
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: #031C24;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-en {
|
||||||
|
margin-top: 4px;
|
||||||
|
color: #D8D8D8;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-en {
|
||||||
|
margin-top: 6px;
|
||||||
|
color: #D8D8D8;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-en {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #D8D8D8;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<script setup>
|
||||||
|
import { defineProps, ref, watch } from "vue";
|
||||||
|
import CheckBox from "./Checkbox.vue";
|
||||||
|
const props = defineProps({
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: "静态型",
|
||||||
|
},
|
||||||
|
aiDesc: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const json = ref( [
|
||||||
|
{
|
||||||
|
type: '0',
|
||||||
|
value: '动态型',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: '1',
|
||||||
|
value: '动静适中',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: '2',
|
||||||
|
value: '静态型',
|
||||||
|
}
|
||||||
|
])
|
||||||
|
watch(
|
||||||
|
() => props.type,
|
||||||
|
(newVal) => {
|
||||||
|
console.log('动静类型: props.type: ', newVal);
|
||||||
|
json.value = json.value.map((item) => ({
|
||||||
|
...item,
|
||||||
|
checked: item.value == newVal,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<view>
|
||||||
|
动静类型:
|
||||||
|
<template v-for="item of json">
|
||||||
|
<CheckBox :checked="item.checked">{{ item.value }}</CheckBox>
|
||||||
|
</template>
|
||||||
|
<view class="desc">
|
||||||
|
{{ aiDesc }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
padding: 0 26px;
|
||||||
|
}
|
||||||
|
.desc {
|
||||||
|
color: #4D4D4D;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
<script setup>
|
||||||
|
import { defineProps, ref, watch } from "vue";
|
||||||
|
|
||||||
|
const map = {
|
||||||
|
"-5": 0,
|
||||||
|
"-4": 1,
|
||||||
|
"-3": 2,
|
||||||
|
"-2": 3,
|
||||||
|
"-1": 4,
|
||||||
|
0: 5,
|
||||||
|
1: 6,
|
||||||
|
2: 7,
|
||||||
|
3: 8,
|
||||||
|
4: 9,
|
||||||
|
5: 10,
|
||||||
|
};
|
||||||
|
const currentStep = ref(0);
|
||||||
|
const props = defineProps({
|
||||||
|
step: {
|
||||||
|
type: Number,
|
||||||
|
default: "0",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
watch(
|
||||||
|
() => props.step,
|
||||||
|
(newVal) => {
|
||||||
|
console.log(map[newVal]);
|
||||||
|
currentStep.value = map[newVal];
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="progress">
|
||||||
|
<image
|
||||||
|
:src="'https://llyz.oss-cn-beijing.aliyuncs.com/diagnostic-report-h5/progress.svg'"
|
||||||
|
class="selectBox"
|
||||||
|
/>
|
||||||
|
<view :class="['bolus', `p${currentStep}`]"></view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.progress {
|
||||||
|
width: 100%;
|
||||||
|
height: 14px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectBox {
|
||||||
|
width: 100%;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bolus {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 0;
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p0 {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p1 {
|
||||||
|
left: calc((100% - 12px) / 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
.p2 {
|
||||||
|
left: calc((100% - 12px) / 10 * 2);
|
||||||
|
}
|
||||||
|
.p3 {
|
||||||
|
left: calc((100% - 12px) / 10 * 3);
|
||||||
|
}
|
||||||
|
.p4 {
|
||||||
|
left: calc((100% - 12px) / 10 * 4);
|
||||||
|
}
|
||||||
|
.p5 {
|
||||||
|
left: calc((100% - 12px) / 10 * 5);
|
||||||
|
}
|
||||||
|
.p6 {
|
||||||
|
left: calc((100% - 12px) / 10 * 6);
|
||||||
|
}
|
||||||
|
.p7 {
|
||||||
|
left: calc((100% - 12px) / 10 * 7);
|
||||||
|
}
|
||||||
|
.p8 {
|
||||||
|
left: calc((100% - 12px) / 10 * 8);
|
||||||
|
}
|
||||||
|
.p9 {
|
||||||
|
left: calc((100% - 12px) / 10 * 9);
|
||||||
|
}
|
||||||
|
.p10 {
|
||||||
|
right: 0;
|
||||||
|
left: unset;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<script setup>
|
||||||
|
import { defineProps, ref, watch } from "vue";
|
||||||
|
import CheckBox from "./Checkbox.vue";
|
||||||
|
const props = defineProps({
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: "曲直适中",
|
||||||
|
},
|
||||||
|
aiDesc: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const json = ref( [
|
||||||
|
{
|
||||||
|
type: '0',
|
||||||
|
value: '曲线型',
|
||||||
|
desc: '面部五官线条(眉眼、嘴唇等)没有特别强烈的曲线感或直线硬朗感,面部轮廓、发型线条以及穿着的服装(V 领、简洁款式 )也呈现出既非极端曲线柔和,也非极端直线利落的状态,是较为平衡的直曲适中风格 。'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: '1',
|
||||||
|
value: '曲直适中',
|
||||||
|
desc: '面部五官线条(眉眼、嘴唇等)没有特别强烈的曲线感或直线硬朗感,面部轮廓、发型线条以及穿着的服装(V 领、简洁款式 )也呈现出既非极端曲线柔和,也非极端直线利落的状态,是较为平衡的直曲适中风格 。'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: '2',
|
||||||
|
value: '直线型',
|
||||||
|
desc: '面部五官线条(眉眼、嘴唇等)没有特别强烈的曲线感或直线硬朗感,面部轮廓、发型线条以及穿着的服装(V 领、简洁款式 )也呈现出既非极端曲线柔和,也非极端直线利落的状态,是较为平衡的直曲适中风格 。'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
watch(
|
||||||
|
() => props.type,
|
||||||
|
(newVal) => {
|
||||||
|
json.value = json.value.map((item) => ({
|
||||||
|
...item,
|
||||||
|
checked: item.value === newVal,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<view>
|
||||||
|
轮廓类型:
|
||||||
|
<template v-for="item of json">
|
||||||
|
<CheckBox :checked="item.checked">{{ item.value }}</CheckBox>
|
||||||
|
</template>
|
||||||
|
<view class="desc">
|
||||||
|
{{ aiDesc }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
padding: 0 26px;
|
||||||
|
}
|
||||||
|
.desc {
|
||||||
|
color: #4D4D4D;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<script setup>
|
||||||
|
import {defineProps, ref, watch} from "vue";
|
||||||
|
import CheckBox from "./Checkbox.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
skinColor: {
|
||||||
|
type: String,
|
||||||
|
default: '黄一白',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const json = ref( [
|
||||||
|
{
|
||||||
|
type: '0',
|
||||||
|
value: '粉一白'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: '1',
|
||||||
|
value: '粉二白'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: '2',
|
||||||
|
value: '粉三白'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: '3',
|
||||||
|
value: '黄一白'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: '4',
|
||||||
|
value: '黄二白'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: '5',
|
||||||
|
value: '黄黑皮'
|
||||||
|
},
|
||||||
|
])
|
||||||
|
watch(
|
||||||
|
() => props.skinColor,
|
||||||
|
(newVal) => {
|
||||||
|
// console.log(json.value.find(item => item.value === newVal), '22');
|
||||||
|
if(!json.value.find(item => item.value === newVal)) {
|
||||||
|
// alert(`当前接口返回肤色与UI不一致${newVal}`)
|
||||||
|
}
|
||||||
|
json.value = json.value.map(item => ({...item, checked: item.value === newVal}));
|
||||||
|
},
|
||||||
|
{deep: true, immediate: true}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
肤色:
|
||||||
|
<template v-for="item of json">
|
||||||
|
<CheckBox :checked="item.checked">{{ item.value }}</CheckBox>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
padding: 20px 26px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup>
|
||||||
|
import {defineProps, watchEffect} from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
html: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<rich-text :nodes="props.html"></rich-text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<script setup>
|
||||||
|
import { defineProps, ref, watch } from "vue";
|
||||||
|
import CheckBox from "./Checkbox.vue";
|
||||||
|
const props = defineProps({
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: "中量感",
|
||||||
|
},
|
||||||
|
aiDesc: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const json = ref( [
|
||||||
|
{
|
||||||
|
type: '0',
|
||||||
|
value: '小量感',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: '1',
|
||||||
|
value: '中量感',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: '2',
|
||||||
|
value: '大量感',
|
||||||
|
}
|
||||||
|
])
|
||||||
|
watch(
|
||||||
|
() => props.type,
|
||||||
|
(newVal) => {
|
||||||
|
console.log('量感类型:-----', props.type);
|
||||||
|
|
||||||
|
json.value = json.value.map((item) => ({
|
||||||
|
...item,
|
||||||
|
checked: item.value === newVal,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<view>
|
||||||
|
量感类型:
|
||||||
|
<template v-for="item of json">
|
||||||
|
<CheckBox :checked="item.checked">{{ item.value }}</CheckBox>
|
||||||
|
</template>
|
||||||
|
<view class="desc">
|
||||||
|
{{ aiDesc }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
padding: 20px 26px;
|
||||||
|
}
|
||||||
|
.desc {
|
||||||
|
color: #4D4D4D;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
<script setup>
|
||||||
|
import { defineProps, ref, watch } from "vue";
|
||||||
|
import CheckBox from "./Checkbox.vue";
|
||||||
|
import { styleJson, manStyleJson } from "./json/style";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
styleType: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
picList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [], // 使用函数返回默认值
|
||||||
|
},
|
||||||
|
gender: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const styleTypeDesc = ref("");
|
||||||
|
const styleTypeJson = ref();
|
||||||
|
const showPreview = ref(false);
|
||||||
|
const currentPreviewImage = ref("");
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.styleType,
|
||||||
|
(newVal) => {
|
||||||
|
const json = props.gender === "男" ? manStyleJson : styleJson;
|
||||||
|
styleTypeJson.value = json.map((item) => ({
|
||||||
|
...item,
|
||||||
|
checked: item.type === newVal,
|
||||||
|
}));
|
||||||
|
styleTypeDesc.value = styleTypeJson.value.find(
|
||||||
|
(item) => item.type === props.styleType
|
||||||
|
)?.value;
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const previewImage = (imageUrl) => {
|
||||||
|
|
||||||
|
currentPreviewImage.value = imageUrl;
|
||||||
|
showPreview.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const closePreview = (e) => {
|
||||||
|
// 阻止事件冒泡
|
||||||
|
e.stopPropagation();
|
||||||
|
showPreview.value = false;
|
||||||
|
currentPreviewImage.value = "";
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template v-if="styleTypeJson">
|
||||||
|
<view class="title">基因风格</view>
|
||||||
|
<view class="container">
|
||||||
|
<template v-for="item of styleTypeJson">
|
||||||
|
<CheckBox :checked="item.checked">{{ item.type }}</CheckBox>
|
||||||
|
</template>
|
||||||
|
<view class="desc">
|
||||||
|
{{ styleTypeDesc }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="pic-container">
|
||||||
|
<image
|
||||||
|
v-for="(pic, index) in picList"
|
||||||
|
:key="index"
|
||||||
|
:src="pic"
|
||||||
|
class="pic"
|
||||||
|
mode="aspectFill"
|
||||||
|
@click="previewImage(pic)"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 图片预览遮罩层 -->
|
||||||
|
<view v-if="showPreview" class="preview-mask" @click="closePreview">
|
||||||
|
<view class="preview-container" @click.stop>
|
||||||
|
<image
|
||||||
|
:src="currentPreviewImage"
|
||||||
|
class="preview-image"
|
||||||
|
mode="widthFix"
|
||||||
|
@click="closePreview"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.title {
|
||||||
|
padding-left: 20px;
|
||||||
|
line-height: 21px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 0 26px;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
color: #4e4e4e;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pic-container {
|
||||||
|
display: flex;
|
||||||
|
padding: 20px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .pic {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
background-color: #eee;
|
||||||
|
border-radius: 4px;
|
||||||
|
} */
|
||||||
|
|
||||||
|
/* 预览遮罩层样式 */
|
||||||
|
.preview-mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-container {
|
||||||
|
width: 90%;
|
||||||
|
height: 90%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-image {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
export const styleJson = [
|
||||||
|
{
|
||||||
|
type: "戏剧型",
|
||||||
|
gender: '女',
|
||||||
|
value:
|
||||||
|
"戏剧型风格以强烈的视觉冲击力为核心,强调夸张与大气。线条多为硬朗直线,剪裁夸张且富有张力,如 oversize 轮廓、立体结构。色彩偏好高对比度搭配,面料多选挺括或光泽感强的材质。装饰大胆醒目,如大色块、夸张配饰,整体传递出强烈的存在感与气场,适合追求个性与舞台感的形象。",
|
||||||
|
pic: '/static/dramaType.png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "睿智型",
|
||||||
|
gender: '女',
|
||||||
|
value:
|
||||||
|
"睿智型风格突出知性与理性,注重细节的精致与思维的锐利感。线条简洁利落,剪裁合身不拖沓,多选用中性色或低饱和色调。面料偏好质感细腻的天然材质,装饰以简约金属饰件或几何图案为主。整体造型克制而不失格调,传递出冷静、聪慧且专业的气质,适合职场或需要展现思辨能力的场合。",
|
||||||
|
pic: '/static/wise.png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "自然型",
|
||||||
|
gender: '女',
|
||||||
|
value:
|
||||||
|
"自然型风格强调随性与舒适,贴近生活本真。线条多为柔和的直线或自然曲线,剪裁宽松舒展,避免刻意修饰。色彩偏好大地色系、莫兰迪色等贴近自然的色调,面料多选棉、麻、针织等透气舒适的材质。装饰简约甚至无装饰,整体造型松弛自在,传递出亲切、质朴且不造作的气质。",
|
||||||
|
pic: '/static/natural.png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "古典型",
|
||||||
|
gender: '女',
|
||||||
|
value:
|
||||||
|
"古典型风格以端庄得体为核心,注重规整与经典。线条多为直线或轻微曲线,剪裁合身严谨,无多余设计。色彩偏好黑白灰、藏蓝等中性色,搭配简洁有序。面料多选挺括细腻的羊毛、真丝等,装饰以小巧精致的金属扣、珍珠等为主。整体造型成熟稳重,传递出优雅、可靠且富有分寸感的形象,是经典与规范的代名词。",
|
||||||
|
pic: '/static/classical.png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "优雅型",
|
||||||
|
gender: '女',
|
||||||
|
value:
|
||||||
|
"优雅型风格强调柔美的线条与温婉的气质,以曲线为主要视觉语言。剪裁贴合身体曲线但不紧绷,多为收腰、圆领等柔和设计。色彩偏好柔和的莫兰迪色、浅粉、米白等,面料多选真丝、雪纺等垂坠感强的材质。装饰以蕾丝、刺绣等精致元素为主,整体造型温柔婉约,传递出含蓄、高贵的女性魅力。",
|
||||||
|
pic: '/static/elegant.png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "浪漫型",
|
||||||
|
gender: '女',
|
||||||
|
value:
|
||||||
|
"浪漫型风格以浓郁的女性化特质为核心,突出曲线与华丽感。线条多为夸张的 S 型曲线,剪裁紧身或蓬松,如鱼尾裙、泡泡袖。色彩偏好浓郁饱和的红、紫、酒红等,面料多选丝绒、缎面等光泽感强的材质。装饰繁复华丽,如蕾丝、水钻、大朵花卉图案,整体造型热情妩媚,传递出性感与奢华的气质。",
|
||||||
|
pic: '/static/romantic.png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "前卫型",
|
||||||
|
gender: '女',
|
||||||
|
value:
|
||||||
|
"前卫型风格以创新与突破为核心,追求标新立异的设计。线条多为不规则几何形,剪裁大胆解构,如不对称、拼接、镂空等。色彩偏好撞色、荧光色或黑白极端对比,面料多选科技感材质或混搭组合。装饰充满未来感,如金属、PVC、夸张廓形配饰,整体造型颠覆传统,传递出叛逆、先锋的个性。",
|
||||||
|
pic: '/static/avantGarde.png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "少女型",
|
||||||
|
gender: '女',
|
||||||
|
value:
|
||||||
|
"少女型风格突出甜美与活泼,充满青春朝气。线条多为圆润的曲线,剪裁短款、收腰,如娃娃领、A 字裙。色彩偏好马卡龙色、浅粉、鹅黄等明亮色调,面料多选纯棉、蕾丝、泡泡纱等轻盈材质。装饰以蝴蝶结、波点、卡通图案为主,整体造型娇俏可爱,传递出纯真、灵动的少女感。",
|
||||||
|
pic: '/static/girlShaped.png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "少年型",
|
||||||
|
gender: '女',
|
||||||
|
value:
|
||||||
|
"少年型风格强调清爽与干练,带有中性化的利落感。线条多为直线,剪裁宽松平直,如衬衫、直筒裤、小西装。色彩偏好黑白灰、牛仔蓝等中性色调,面料多选牛仔、帆布、薄棉等挺括材质。装饰简约甚至无装饰,整体造型帅气洒脱,传递出率真、爽朗的中性气质,弱化女性化特质。",
|
||||||
|
pic: '/static/teenagerType.png'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
export const manStyleJson = [
|
||||||
|
{
|
||||||
|
type: '戏剧型',
|
||||||
|
gender: '男',
|
||||||
|
value:'戏剧型风格以极具张力的视觉表现力为核心,凸显强悍气场与王者风范。线条多为极具力量感的硬朗直线,剪裁夸张且充满结构化设计,如超大廓形西装、立体剪裁的风衣。色彩偏好高饱和撞色或浓郁深沉的色调,面料多选挺括的厚羊毛、亮面皮革等富有质感的材质。装饰以金属质感的夸张配饰、独特纹样为主,整体传递出睥睨一切的霸气与掌控全场的魄力,是天生的焦点型人物。',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: '睿智型',
|
||||||
|
gender: '男',
|
||||||
|
value:'睿智型风格彰显智慧与思辨,强调沉稳的气场与理性的锋芒。线条简洁利落且富有逻辑性,剪裁精准合体,杜绝冗余设计,如修身西装、简约衬衫。色彩以深灰、藏蓝、卡其等沉稳色调为主,搭配克制有序。面料多选高支棉、羊毛等质感上乘的天然材质,装饰仅以极简的金属袖口、几何纹领带点缀。整体造型散发着冷静、深邃的智者气息,是职场精英与思想者的标志性风格。',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: '自然型',
|
||||||
|
gender: '男',
|
||||||
|
value:'自然型风格追求自在本真的状态,拒绝刻意雕琢。线条多为自然流畅的直线,剪裁宽松有度,如休闲款衬衫、直筒牛仔裤、棉麻质地的外套。色彩偏爱原木色、浅卡其、米白、牛仔蓝等贴近自然的色调,面料以棉、麻、粗针织、水洗牛仔为主,强调透气舒适。装饰极少甚至无装饰,整体传递出随性不羁的洒脱感,像行走的生活哲学家般亲切而有温度。',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: '古典型',
|
||||||
|
gender: '男',
|
||||||
|
value:'古典型风格以经典庄重为核心,坚守永不过时的品味准则。线条以利落的直线为主,剪裁严谨合体,如标准款西装、牛津纺衬衫、合身西裤,处处体现传统男装的精髓。色彩以黑白灰、藏蓝、深棕等经典色系为主,搭配一丝不苟。面料多选顶级羊毛、纯棉、真丝等挺括细腻的材质,装饰仅以精致的袖扣、低调的领带夹点缀。整体造型成熟稳重,是可靠与教养的代名词。',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: '优雅型',
|
||||||
|
gender: '男',
|
||||||
|
value:'优雅型风格散发从容温润的绅士风度,以内敛的魅力打动人心。线条多为流畅的弧线与适度的直线结合,柔和而不软弱,如微廓形西装、收腰款风衣。剪裁合体但不紧绷,强调舒适的廓形感。色彩偏爱柔和的米白、浅灰、豆沙绿等低饱和度色调,面料多选柔软垂坠的羊绒、天丝、细棉等。装饰以精致低调的格纹、暗纹或刺绣为主,整体传递出温润如玉的谦和气质,不经意间流露高级感。',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: '浪漫型',
|
||||||
|
gender: '男',
|
||||||
|
value:'浪漫型风格充满艺术化的男性魅力,以深情与不羁为核心。线条多为流畅的曲线与柔和的直线交织,剪裁略带随性的宽松或贴合身形的流畅线条,如丝质垂感衬衫、微阔形丝绒西装。色彩偏好酒红、墨绿、宝蓝等富有故事感的浓郁色调,或柔和的暖棕色系。面料多选丝绒、桑蚕丝、高级针织等柔软且富有光泽的材质,装饰以复古纹样、刺绣细节、丝质围巾为主,整体散发着致命的吸引力与诗意情怀。',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: '前卫型',
|
||||||
|
gender: '男',
|
||||||
|
value:'前卫型风格以颠覆与革新为灵魂,彰显先锋态度与实验精神。线条多为打破常规的不规则剪裁与几何线条,充满强烈的视觉冲突。剪裁大胆运用解构重组、oversize 与紧身的极端碰撞,如不对称夹克、拼接卫衣。色彩敢于尝试高饱和撞色、荧光色,或极致的黑白灰对比。面料常选用科技感面料与天然材质的混搭,如反光材质、再生纤维、做旧牛仔。装饰充满未来感与颠覆性,如机能风配件、抽象艺术印花,整体传递出挑战传统的叛逆个性。',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: '少女型',
|
||||||
|
gender: '男',
|
||||||
|
value:'少女型风格在这里转化为洋溢着纯粹少年感的风格,传递青春活力与阳光气息。线条多为简洁明快的直线,略带圆润感,如休闲衬衫、连帽卫衣、直筒牛仔裤。剪裁合身偏宽松,舒适无束缚。色彩偏爱明亮的浅蓝、纯白、鹅黄、浅绿等清新色调,面料多选轻盈透气的纯棉、泡泡纱、薄针织。装饰以简约俏皮的条纹、小 logo、亮色配饰为主,整体造型干净清爽,像夏日阳光般治愈,充满未经打磨的纯真感。',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: '少年型',
|
||||||
|
gender: '男',
|
||||||
|
value:'少年型风格突出清爽干练的中性化气质,彰显率真与洒脱。线条多为干脆利落的直线,棱角分明,如利落的短款夹克、合身衬衫、窄腿西裤。色彩以黑白灰、藏蓝、军绿等极具力量感的中性色为主,面料多选挺括耐磨的牛仔、帆布、厚棉布。装饰极简甚至无装饰,突出纯粹的线条感,整体传递出果断、飒爽的气场,充满少年特有的朝气与行动力。',
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
@@ -0,0 +1,273 @@
|
|||||||
|
<template>
|
||||||
|
<view class="header">
|
||||||
|
<!-- 头像和信息区 -->
|
||||||
|
<view class="info-row">
|
||||||
|
<view class="avatar-stack">
|
||||||
|
<image
|
||||||
|
class="avatar-main"
|
||||||
|
:src="userFaceInfo?.avatar_img_url"
|
||||||
|
mode="aspectFill"
|
||||||
|
/>
|
||||||
|
<image
|
||||||
|
class="avatar-shadow"
|
||||||
|
:src="userFaceInfo?.avatar_img_url"
|
||||||
|
mode="aspectFill"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 底部分栏信息 -->
|
||||||
|
<view class="">
|
||||||
|
<view class="info-main">
|
||||||
|
<view class="info-basic">
|
||||||
|
<text class="gender">{{ userFaceInfo.gender }}</text>
|
||||||
|
<text class="birthday">| {{ userFaceInfo.createTime }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-desc">
|
||||||
|
{{ userFaceInfo.feature }},
|
||||||
|
<!-- {{
|
||||||
|
userFaceInfo.Facial_three_dimensionality
|
||||||
|
}}, -->
|
||||||
|
{{ userFaceInfo.eyebrows }},{{ userFaceInfo.ocular_form }},
|
||||||
|
{{ userFaceInfo.neck_length }},
|
||||||
|
{{ userFaceInfo.nasolabial_folds }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="info-columns">
|
||||||
|
<view class="col-item">
|
||||||
|
<text class="col-label">身高</text>
|
||||||
|
<text class="col-value">{{ userFaceInfo.stature || "-" }}cm</text>
|
||||||
|
</view>
|
||||||
|
<view class="col-item">
|
||||||
|
<text class="col-label">体重</text>
|
||||||
|
<text class="col-value">{{ userFaceInfo.bodyWeight || "-" }}kg</text>
|
||||||
|
</view>
|
||||||
|
<view class="col-item">
|
||||||
|
<text class="col-label">修饰目标</text>
|
||||||
|
<text class="col-value">{{ userFaceInfo.purpose || "-" }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="col-item">
|
||||||
|
<text class="col-label">局部问题</text>
|
||||||
|
<text class="col-value">{{ userFaceInfo.question || "-" }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineProps, watch, ref } from "vue";
|
||||||
|
const props = defineProps({
|
||||||
|
userFaceInfo: {
|
||||||
|
type: Object,
|
||||||
|
default: {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const userFaceInfo = ref({});
|
||||||
|
watch(
|
||||||
|
() => props.userFaceInfo,
|
||||||
|
(newVal) => {
|
||||||
|
userFaceInfo.value = newVal;
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.header {
|
||||||
|
background: url("https://llyz.oss-cn-beijing.aliyuncs.com/diagnostic-report-h5/h5headerbg.png")
|
||||||
|
no-repeat center center;
|
||||||
|
color: #fff;
|
||||||
|
padding: 16px 20px 32px;
|
||||||
|
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||||
|
}
|
||||||
|
.status-bar-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 12px;
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
|
.status-bar-title {
|
||||||
|
color: #aaa;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.status-bar-icons {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.status-bar-time {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #fff;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
.status-bar-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.status-bar-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background: #666;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.status-bar-icon.signal {
|
||||||
|
background: linear-gradient(90deg, #fff 60%, #666 100%);
|
||||||
|
}
|
||||||
|
.status-bar-icon.wifi {
|
||||||
|
background: linear-gradient(90deg, #fff 60%, #666 100%);
|
||||||
|
}
|
||||||
|
.status-bar-icon.battery {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.header-action-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 12px 0 12px;
|
||||||
|
}
|
||||||
|
.back-btn {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
.back-arrow {
|
||||||
|
color: #222;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.header-icons {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.icon-box {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background: #444;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
.icon-box.square {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.icon-box.dot {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background: #fff;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.icon-box.circle {
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #fff;
|
||||||
|
border: 2px solid #444;
|
||||||
|
}
|
||||||
|
.info-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
/* margin-top: 8px; */
|
||||||
|
/* padding: 0 24px; */
|
||||||
|
}
|
||||||
|
.avatar-stack {
|
||||||
|
position: relative;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.avatar-main {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: 50%;
|
||||||
|
/* border: 2px solid #fff; */
|
||||||
|
position: absolute;
|
||||||
|
background-color: #fff;
|
||||||
|
object-fit: cover;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
.avatar-shadow {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: gray;
|
||||||
|
/* opacity: 0.3; */
|
||||||
|
object-fit: cover;
|
||||||
|
position: absolute;
|
||||||
|
left: 82%;
|
||||||
|
z-index: 2;
|
||||||
|
/* 关键代码:图片变黑白 */
|
||||||
|
filter: grayscale(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-main {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.info-basic {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.gender {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
.birthday {
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
.info-desc {
|
||||||
|
width: 336px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #ccc;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-family: Molengo;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 100%;
|
||||||
|
letter-spacing: 0%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.info-columns {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
/* padding: 0 24px; */
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
||||||
|
.col-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.col-label {
|
||||||
|
color: #aaa;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
font-family: Molengo;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 10px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
}
|
||||||
|
.col-value {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 15px;
|
||||||
|
font-family: Molengo;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 12px;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
/* 超出隐藏并显示省略号 */
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
/* 可选:限制最大宽度(防止 flex 布局拉伸) */
|
||||||
|
max-width: 80px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<template>
|
||||||
|
<view class="tabs">
|
||||||
|
<view class="tabs-header">
|
||||||
|
<view
|
||||||
|
v-for="(tab, index) in tabs"
|
||||||
|
:key="index"
|
||||||
|
:class="['tab-item', { active: index === activeTab }]"
|
||||||
|
@click="onTabClick(index)"
|
||||||
|
>
|
||||||
|
{{ tab.name }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view>
|
||||||
|
<slot></slot>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Tabs",
|
||||||
|
props: {
|
||||||
|
tabs: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
activeTab: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ["update:activeTab"],
|
||||||
|
methods: {
|
||||||
|
onTabClick(index) {
|
||||||
|
this.$emit("update:activeTab", index);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.tabs {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 0 0 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
.tabs-header {
|
||||||
|
display: flex;
|
||||||
|
/* border-bottom: 1px solid #eee; */
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.tab-item {
|
||||||
|
padding: 10px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #333;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.tab-item.active {
|
||||||
|
color: rgba(3, 28, 36, 1);
|
||||||
|
border-bottom: 2px solid rgba(3, 28, 36, 1);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
/* .tabs-content {
|
||||||
|
padding-bottom: 16px;
|
||||||
|
} */
|
||||||
|
</style>
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
|||||||
|
import request from '@/sheep/request';
|
||||||
|
|
||||||
|
const ReportApi = {
|
||||||
|
// 查询分类列表
|
||||||
|
getCategoryList: () => {
|
||||||
|
return request({
|
||||||
|
url: '/product/category/list',
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 查询分类列表,指定编号
|
||||||
|
getCategoryListByIds: (ids) => {
|
||||||
|
return request({
|
||||||
|
url: '/product/category/list-by-ids',
|
||||||
|
method: 'GET',
|
||||||
|
params: { ids },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReportApi;
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// 本地图片不通过input上传后端
|
||||||
|
function uploadFormData(formData) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("POST", "https://puton.meidaojia.com/api/v1/test/exchangeHair");
|
||||||
|
xhr.onload = function () {
|
||||||
|
if (xhr.status >= 200 && xhr.status < 300) {
|
||||||
|
try {
|
||||||
|
resolve(JSON.parse(xhr.responseText));
|
||||||
|
} catch (e) {
|
||||||
|
reject("返回内容不是有效JSON");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reject("上传失败,状态码:" + xhr.status);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.onerror = function () {
|
||||||
|
reject("网络错误");
|
||||||
|
};
|
||||||
|
xhr.send(formData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// url下载到本地
|
||||||
|
const downloadFile = (url) => {
|
||||||
|
return fetch(url).then((res) => {
|
||||||
|
return res.blob();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 数字转中文
|
||||||
|
function numToChinese(num) {
|
||||||
|
const map = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
|
||||||
|
return map[num] !== undefined ? map[num] : num;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMaxElement(arr) {
|
||||||
|
const elements = ["金", "木", "水", "火", "土"];
|
||||||
|
arr = arr.map(Number);
|
||||||
|
const maxIndex = arr.indexOf(Math.max(...arr));
|
||||||
|
return elements[maxIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
function reorderByValue(arr, target) {
|
||||||
|
return [
|
||||||
|
...arr.filter((item) => item.typeName + "型" === target),
|
||||||
|
...arr.filter((item) => item.typeName + "型" !== target),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
uploadFormData,
|
||||||
|
downloadFile,
|
||||||
|
numToChinese,
|
||||||
|
getMaxElement,
|
||||||
|
reorderByValue,
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user