聆听历史的回响——多源异构数据采集与融合应用综合实践 _
这个项目属于哪个课程 | 2024数据采集与融合技术实践 |
---|---|
组名、项目简介 | 组名:scrapy能帮我爬到美味蟹黄堡的秘方吗 项目需求:文物不能很好的融入我们的生活,它们仿佛一具冰冷的尸体躺在博物馆的展示柜中,静静地接受着岁月的侵蚀和尘埃的覆盖。 项目目标:赋予文物新的生命力,让它们“动”起来。通过人工智能技术,为文物创造更加精彩动人的故事,生成曼妙的声音,制作生动震撼的展示视频,让历史以全新的方式触动人心。 技术路线:vue3前端web网站搭建,python flask后端,华为云平台(服务器接口调用),阿里云平台(部分数据存储),kimi背景故事AI生成,百度ai文本转语音,快手可灵ai平台对口型视频生成和通过接口实现视频生成 |
团队成员学号 | 曹星才-072208130 张诗悦-052205144 朱佳杰-012202239 黄悦佳-102202142 詹镇壕-102202149 |
这个项目目标 | 将静止的文物与现代科技相结合,赋予它们新的生命和表现力。我们通过先进的人工智能技术,让文物“动”起来,呈现出它们沉睡千年的故事与情感。AI将为每件文物创作更加丰富和感人的背景故事,让它们不再只是历史的见证者,而是活生生的叙述者。同时,通过AI生成曼妙的声音,让这些故事更加生动,引人入胜;借助视觉技术,生成精美的展示视频,生动再现文物的诞生过程、文化背景与历史场景。最终,我们希望通过这一系列创新方式,让更多人触及文物的灵魂,感受历史与文化的魅力。 |
其他参考文献 | https://element-plus.org/zh-CN/component/overview.html https://cn.vuejs.org/guide/introduction w3school 在线教程 uiverse.io Apache ECharts https://docs.qingque.cn/d/home/eZQClW07IFEuX1csc-VejdY2M?identityId=1oEG9JKKMFv#section=h.a6acy8mosh |
码云链接(代码已汇总,各小组成员代码不分开放) | 前端:综合设计实践——前端 ·2022级数据采集与融合技术 - 码云 - 开源中国 后端:综合设计实践——后端 ·2022级数据采集与融合技术 - 码云 - 开源中国 爬虫:https://gitee.com/jia1666372886/museumspider/tree/master/ |
一、项目背景
文物不能很好的融入我们的生活,它们仿佛一具冰冷的尸体躺在博物馆的展示柜中,静静地接受着岁月的侵蚀和尘埃的覆盖。那些曾经承载着历史脉络与文化荣光的物品,如今只剩下了沉默的外壳,无法向我们诉说它们曾经的故事。它们被时间的长河淹没,渐渐失去了与我们生活的联系,变成了一个个静止的符号,等待着被无数的目光定格,却难以真正融入现代人的日常。
这些文物或许曾在千百年前激荡起社会的波澜,或许曾是某个伟大文明的象征,但如今它们的存在仿佛和现代世界隔绝了。我们虽然能够从它们的外观上看到历史的痕迹,感受到当时的技艺与智慧,却难以触摸到那段历史背后的人情冷暖、风云变幻。它们不再是生活的一部分,而是被孤立在某个遥远的过去,只是某种文化的遗物。
然而,尽管它们远离了现代的喧嚣,文物依然是人类历史与文化的见证者。每一件文物都有自己独特的生命力,它们承载着前人的思想、情感和智慧。要想让文物重新焕发出活力,我们需要跨越时空的藩篱,用现代科技去唤醒它们沉睡的记忆。通过人工智能、虚拟现实等技术,文物可以不再是冰冷的展品,而是成为我们与过去沟通的桥梁。它们能够重新讲述那个时代的故事,带领我们走进千年之前的世界,让我们重新感受到历史的脉搏和文化的力量。
在这个数字化、智能化的时代,我们不妨通过创新的方式,去打破文物与现代生活之间的隔阂,让这些承载着历史与文明的珍宝重新走进我们的生活,让它们不再只是博物馆里的静物,而是活跃在我们日常的对话和思考之中。
二、项目分工
成员 | 任务 |
---|---|
曹星才 | 前端页面设计,接口调度与路由跳转逻辑,js代码编写 |
张诗悦 | 前端页面设计,web原型设计,css样式与html框架设计 |
朱佳杰 | 文物信息爬取 |
黄悦佳 | 后端服务器搭建与数据库管理 |
詹镇壕 | 后端服务器搭建与api接口编写 |
三、个人分工
🍕首页设计
template
<template>
<div class="back">
<video class="video-background" autoplay muted loop>
<source
src="https://img.dpm.org.cn/Uploads/File/2024/12/13/u675bf541eb938.mp4"
type="video/mp4"
/>
</video>
<!-- 其他内容 -->
<div class="content">
<div class="management-container">
<h1 class="title">聆听历史的回响</h1>
<el-carousel :interval="3000" type="card" height="300px">
<el-carousel-item
v-for="randomArtifact in randomArtifacts"
:key="randomArtifact.id"
class="image-item"
>
<img
:src="randomArtifact.thumbnail_path"
alt=""
@click="handleAction(randomArtifact.id)"
/>
<!-- <img :src="randomArtifact.thumbnail_path" alt="" @click="handleAction(randomArtifact.id)"
style="width: 100%;height: 100%;">
-->
<!-- </div> -->
</el-carousel-item>
</el-carousel>
<div class="title-container">
<h1 class="title2">数字藏馆介绍</h1>
<p style="font-size: 14px; color: gray">
(在岁月的长河中,历史的回声悄然回荡。如果文物能够诉说,它们会讲述历经千年的风雨沧桑。而我们,唯有以敬畏之心,聆听它们的低语)
</p>
</div>
<hr />
<div class="content2">
<p class="title3">欢迎踏入 “聆听历史的回响”</p>
<blockquote>
这座汇聚历史光辉的数字殿堂。在这里,文物不再是静默的展品,而是历史的生动叙述者。每一件文物都如同星辰般璀璨,承载着往昔的记忆与荣光,待您逐一探寻。
</blockquote>
<p class="highlight">借助前沿的人工智能技术,我们为文物赋予了全新的生命力。</p>
<ul>
<li class="intro">
AI生成的文物背景故事,仿佛打开了一扇通往古代的神秘之门,让那些被时间遗忘的故事缓缓展开,或壮阔激昂,或细腻动人,让您仿佛穿越时空,与历史紧密相连。
</li>
<li class="intro">
AI文物视频,以生动的画面重现文物诞生的辉煌时刻,或是工匠的精湛技艺,或是历史场景的生动再现,让您仿佛亲临其境,见证历史的流转。
</li>
<li class="intro">若您厌倦了文字,AI语音介绍也将为历史爱好者带来文物的轻声细语。</li>
</ul>
<p class="highlight">
文化如同一位深邃的智者,虽不言不语,却通过文物传递着无尽的智慧与力量。
</p>
<p>历史宛如一曲悠长的乐章,虽不发一言,却在文物的每一道纹理、每一缕气息中回荡着余音。</p>
<p>
在这里,让我们一同在文物的世界中驻足,让浮躁的心灵在历史的滋养下恢复宁静,在岁月的静默中,倾听石语,感受历史的深沉回响。
</p>
</div>
<h1 class="title2">文物的生命力</h1>
<hr />
<div class="common-layout">
<el-container>
<el-aside width="60%">
<div>
<div class="demo-image__lazy">
<!-- <el-image v-for="url in videos" :key="url" :src="url" lazy /> -->
<video
v-for="url in videos"
:key="url"
:src="url"
controls
loop
width="auto"
height="650px"
style="margin: 60px auto; display: block"
>
你的浏览器不支持视频标签。
</video>
</div>
</div>
</el-aside>
<el-container>
<el-header class="header"> 文物的倾诉 </el-header>
<el-main class="main-content"
>文物与你说话,不再是冰冷的历史遗物,而是化身为时代的见证者,带着千年积淀的故事轻声诉说。它们穿越时空的缝隙,带你回到那个曾经辉煌的时代,仿佛能感受到匠人手中每一锤敲击的力量,每一丝细腻雕刻的温度。文物与你说话,仿佛是和历史的对话,每一道纹路、每一处裂痕,都讲述着一段不为人知的传奇。它们不仅仅是静止的物件,而是蕴含着无尽智慧和情感的生命体,正等待着你去倾听和理解。借助现代科技,尤其是人工智能的力量,文物的声音得以被唤醒,带领你深入探索那些被岁月遗忘的记忆,听见过去的回响,感知那份跨越时间的情感联结。在这段对话中,你将与历史亲密接触,穿越时光,触摸文明的脉搏。</el-main
>
</el-container>
</el-container>
</div>
<div class="common-layout">
<el-container>
<el-container>
<el-header class="header"> 神面形玉佩 </el-header>
<div style="background-color: aliceblue;">
<audio
style="margin: 0 auto;display: block;" ref="audioPlayer" controls src="http://aipe-speech.bj.bcebos.com/text_to_speech/2024-12-15/675ea9a1b98eeb0001a3f1fb/speech/0.mp3?authorization=bce-auth-v1%2FALTAKjI91nE52nvtDNRgFlUCVz%2F2024-12-15T10%3A04%3A30Z%2F259200%2F%2F44088dfbff04bd433dc4b753030db22de1613b8b2423752970362ec0af62b979"></audio>
</div>
<el-main class="main-content">
在古老的红山文化中,流传着一个关于神面形玉佩的神秘传说。相传,在红山的深处,居住着一位拥有超凡力量的神祇,他的名字早已被时间遗忘,但他的形象却以一种特殊的方式流传了下来——那就是神面形玉佩。
这枚玉佩,由最纯净的青玉雕琢而成,其上刻画着神祇的面容,那内凹的圆眼仿佛能洞察世间一切秘密,五组长齿显露出神祇的威严与力量,而那卷曲成勾云形的角状,则象征着神祇与天地之间的联系。
传说中,这枚玉佩是神祇与人间沟通的媒介。每当人间遭遇灾难,或者需要神祇的指引时,部落的长老便会将这枚玉佩高举,向神祇祈求。而神祇,也会通过玉佩上的神面纹,向人间传达他的意志。
然而,随着时间的流逝,红山文化逐渐衰落,神面形玉佩也失去了它的光辉。直到有一天,一位年轻的探险家在红山的遗迹中发现了这枚玉佩。当他拿起玉佩的那一刻,玉佩上的神面仿佛活了过来,内凹的圆眼闪烁着神秘的光芒,五组长齿似乎在低语着古老的咒语。
探险家被这股神秘的力量所吸引,他开始研究这枚玉佩,试图解开其中的秘密。随着研究的深入,他发现玉佩上的神面纹与红山文化中的神话故事有着千丝万缕的联系。每当月圆之夜,玉佩上的神面似乎会更加活跃,仿佛在等待着某个重要的时刻。
终于,在一次月圆之夜,探险家在玉佩的指引下,来到了红山的深处。在那里,他看到了一个古老的祭坛,祭坛上刻着与玉佩上相同的神面纹。当他将玉佩放在祭坛上时,一道光芒从玉佩中射出,照亮了整个祭坛。随着光芒的消散,神祇的形象逐渐显现,他的声音在探险家的心中响起,指引他走向了一个新的时代。
从此,神面形玉佩不再是一个简单的玉器,它成为了连接过去与未来,神与人的桥梁。而那位探险家,也因为他的发现,被后人尊称为“红山的守护者”。</el-main
>
</el-container>
<el-aside width="50%">
<!-- <el-image v-for="url in videos" :key="url" :src="url" lazy /> -->
<video
src="https://cdn.klingai.com/bs2/upload-kling-api/3601927415/image2video/CjS7emdSwTgAAAAAAXBeXw-0_raw_video_1.mp4"
controls
loop
width="100%"
height="auto"
style="height: 100%; display: block"
>
你的浏览器不支持视频标签。
</video>
</el-aside>
</el-container>
</div>
</div>
</div>
</div>
</template>
style
<style scoped>
.back {
position: relative;
width: 100%;
height: 100%;
overflow-y: auto;
padding-top: 4.7%;
background-image: url(https://digicol.dpm.org.cn/images/bg2.jpg);
}
.video-background {
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover; /* 保持视频的比例,裁剪并填充整个容器 */
z-index: -1; /* 将视频放置在内容的下方 */
pointer-events: none; /* 禁用视频的鼠标事件,防止用户操作 */
animation: none !important; /* 确保没有任何动画影响视频 */
}
.content {
position: relative;
z-index: 1; /* 确保内容显示在视频上方 */
color: rgb(12, 12, 12); /* 设置文本颜色,避免在视频背景下看不清楚 */
z-index: 1; /* 确保内容显示在视频上方 */
}
.management-container {
margin: -4px auto;
width: 90%;
height: fit-content; /* 自适应高度 */
background-color: rgb(248, 244, 238); /* 管理容器背景色 */
padding: 40px 40px;
box-sizing: border-box;
margin-bottom: 15%;
}
.image-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #f8f4ee;
border-radius: 5px;
box-sizing: border-box;
cursor: pointer;
}
.image-item img {
width: auto;
height: 100%;
aspect-ratio: 48/30;
}
.el-carousel__item:nth-child(2n) {
background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n + 1) {
background-color: #d3dce6;
}
.title {
text-align: center; /* 水平居中 */
font-family: 'KaiTi', 'STKaiti', serif; /* 设置优雅字体 */
font-size: 50px; /* 设置字体大小 */
font-weight: bold; /* 设置字体加粗 */
color: #4a4a4a; /* 设置字体颜色 */
margin: 40px 0;
}
.title2 {
font-family: 'KaiTi', 'STKaiti', serif; /* 设置优雅字体 */
font-weight: bold; /* 设置字体加粗 */
color: #4a4a4a; /* 设置字体颜色 */
}
.title-container {
margin-top: 100px;
display: flex;
}
.demo-pagination-block + .demo-pagination-block {
margin-top: 10px;
}
.demo-pagination-block .demonstration {
margin-bottom: 16px;
}
.content2 {
font-family: 'Arial', sans-serif;
line-height: 1.8;
color: #333;
margin: 20px;
}
.intro {
font-size: 18px;
color: #555;
margin-bottom: 20px;
}
.title3 {
font-size: 24px;
font-weight: bold;
margin: 40px 0 20px 0;
color: #3a3a3a;
}
.highlight {
font-size: 20px;
font-weight: bold;
color: #ff6347; /* 使用温暖的颜色突出重要内容 */
}
blockquote {
font-size: 18px;
font-style: italic;
border-left: 4px solid #ff6347;
margin: 20px 0;
padding-left: 20px;
color: #666;
}
p {
font-size: 16px;
margin: 10px 0;
}
p:last-of-type {
margin-bottom: 0;
}
.indent {
font-size: 18px;
margin-left: 20px; /* 缩进效果 */
font-style: italic;
color: #666;
}
.demo-image__lazy {
height: 700px;
overflow-y: auto;
}
.common-layout {
margin-top: 100px;
}
.header {
text-align: center;
font-size: 2rem;
font-weight: bold;
color: #2c3e50;
padding: 20px;
background-color: #f4f6f8;
}
.main-content {
flex: 1;
padding: 20px;
font-size: 1.2rem;
line-height: 2;
color: #34495e;
background-color: #ecf0f1;
text-align: justify;
}
.main-content p {
margin-bottom: 20px;
}
</style>
静态效果
①
②
③
④
⑤
动态效果
①
②
③
④
🍔展示页设计
template
<template>
<div class="exhibit-container">
<Filter @categoryChanged="fetchArtifactsByCategories" style="margin-bottom: 20px"></Filter>
<div class="gallery" v-if="images.length">
<div v-for="item in images" :key="item.id" class="image-item" @click="handleAction(item.id)">
<img :src="item.thumbnail_path" :alt="`Image ${item.name}`" />
<div class="image-name">{{ item.name }}</div>
<div class="category">[{{ item.category }}]</div>
</div>
</div>
<div v-else></div>
</div>
</template>
style
<style scoped>
.exhibit-container {
background-color: rgb(91, 37, 40);
padding-top: 100px;
box-sizing: border-box;
min-height: 100vh;
}
.gallery {
width: 90%;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
margin: 0 auto;
box-sizing: border-box;
}
.image-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #f8f4ee;
padding: 20px;
border-radius: 5px;
box-sizing: border-box;
cursor: pointer;
}
.image-item img {
width: 90%;
aspect-ratio: 1/1;
}
.image-name {
margin-top: 10px;
font-size: 15px;
font-weight: bold;
}
.category {
color: gray;
font-size: 10px;
font-weight: bold;
}
</style>
静态效果
动态效果
🍟列表页设计
template
<template>
<div class="back">
<img src="@/assets/pic/qianLiJiangShangTu.jpg" alt="qianLiJiangShangTu" class="qianli" />
<div class="management-container">
<h1 class="title">文物列表</h1>
<div class="search-panels">
<div class="search-group">
<input
required=""
type="text"
v-model="searchQuery"
name="text"
autocomplete="on"
class="input"
/>
<label class="enter-label">请输入文物名称或ID </label>
<div class="btn-box">
<button class="btn-search" @click="handleSearch">
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512">
<path
d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"
></path>
<circle id="svg-circle" cx="208" cy="208" r="144"></circle>
</svg>
</button>
</div>
<div class="btn-box-x">
<button class="btn-cleare">
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 384 512">
<path
d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"
id="cleare-line"
></path>
</svg>
</button>
</div>
</div>
</div>
<div class="bar">
<table>
<colgroup>
<!-- 设置每列宽度 -->
<col style="width: 10%" />
<col style="width: 20%" />
<col style="width: 5%" />
<col style="width: 20%" />
<col style="width: 20%" />
<col style="width: 15%" />
<col style="width: 20%" />
</colgroup>
<thead>
<tr>
<th>文物编号</th>
<th>文物名称</th>
<th>年代</th>
<th>
<label for="categoryFilter"
>类别(
<select id="categoryFilter" v-model="selectedCategory">
<option value="">所有类别</option>
<option value="传世品">传世品</option>
<option value="革命文物">革命文物</option>
<option value="国史文物">国史文物</option>
<option value="货币">货币</option>
<option value="考古发掘品">考古发掘品</option>
<option value="民族民俗文物">民族民俗文物</option>
<option value="古籍文献">古籍文献</option>
<option value="外国文物">外国文物</option>
<option value="艺术品">艺术品</option>
<!-- 你可以根据实际的类别数据动态生成选项 --></select
>)</label
>
</th>
<th>参数</th>
<th>图片</th>
<th>操作</th>
</tr>
</thead>
</table>
</div>
<div class="collect" v-loading="loading">
<table>
<colgroup>
<!-- 设置每列宽度 -->
<col style="width: 10%" />
<col style="width: 20%" />
<col style="width: 5%" />
<col style="width: 20%" />
<col style="width: 20%" />
<col style="width: 15%" />
<col style="width: 20%" />
</colgroup>
<thead>
<tr v-for="item in filteredArtifacts" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.period }}</td>
<td>{{ item.category }}</td>
<!-- <td>{{ item.location_time }}</td> -->
<td>{{ item.parameter }}</td>
<td>
<img
:src="item.thumbnail_path"
class="artifact-image"
alt="artifact image"
width="60"
height="60"
/>
</td>
<td class="action-buttons">
<button @click="handleAction(item.id)" style="background-color: rgb(97, 41, 47)">
详细信息
</button>
<!-- <div > -->
<button
@click="handleDelete(item.id)"
v-if="isAdmin"
style="background-color: rgb(220, 53, 69)"
>
删除文物
</button>
</td>
</tr>
</thead>
</table>
</div>
</div>
</div>
</template>
style
<style scoped>
.back {
width: 100vw;
height: 100vh;
background-color: rgb(91, 37, 40); /* 背景颜色 */
overflow-y: auto; /* 如果内容超出,允许垂直滚动 */
}
.management-container {
margin: -4px auto;
width: 80%;
height: fit-content; /* 自适应高度 */
background-color: rgb(248, 244, 238); /* 管理容器背景色 */
padding: 40px 15px;
box-sizing: border-box;
margin-bottom: 15%;
}
.title {
text-align: center; /* 水平居中 */
font-family: 'KaiTi', 'STKaiti', serif; /* 设置优雅字体 */
font-size: 40px; /* 设置字体大小 */
font-weight: bold; /* 设置字体加粗 */
color: #4a4a4a; /* 设置字体颜色 */
margin-bottom: 30px;
}
.qianli {
height: 40%;
width: 100%;
margin-bottom: 0;
}
.bar {
width: 100%;
height: 50px;
background-color: rgb(151, 66, 75);
margin: 0;
padding: 0;
display: flex; /* 让子元素使用弹性布局 */
justify-content: space-evenly; /* 子元素均匀分布 */
align-items: center; /* 垂直方向居中 */
margin-bottom: 25px;
}
.bar table {
width: 100%; /* 表格占满父容器宽度 */
border-collapse: collapse; /* 去掉单元格之间的间隙 */
text-align: center; /* 表格内容居中 */
}
.bar th {
padding: 5px 10px; /* 给单元格添加适当内边距 */
color: white; /* 设置文字颜色 */
font-weight: bold; /* 加粗文字 */
}
.collect {
width: 100%;
background-color: rgb(255, 255, 255);
margin: 0;
padding: 0;
display: flex; /* 让子元素使用弹性布局 */
justify-content: center; /* 子元素均匀分布 */
align-items: center; /* 垂直方向居中 */
}
.collect table {
width: 100%; /* 表格占满父容器宽度 */
border-collapse: collapse; /* 合并边框间隙 */
text-align: center; /* 表格内容居中 */
}
.collect tr {
border-bottom: 4px solid rgb(248, 244, 238); /* 为每一行添加灰色的底边线 */
height: 80px;
}
.collect td {
padding: 10px; /* 为单元格添加适当的内边距 */
font-weight: bold; /* 加粗文字 */
color: rgb(2, 2, 2); /* 设置文字颜色 */
}
.collect tr:last-child {
border-bottom: none; /* 去掉最后一行的底边线 */
}
.collect .action-buttons {
text-align: center;
/* display: flex; */
flex-direction: column;
align-items: center;
gap: 10px;
}
.collect .action-buttons button {
background-color: #007bff; /* 设置按钮的背景色 */
border-radius: 8px; /* 更加圆润的边角 */
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 添加阴影 */
margin: 3px 5px;
color: white; /* 设置字体颜色 */
padding: 5px 5px; /* 设置内边距 */
font-size: 10px; /* 设置字体大小 */
transition: all 0.1s ease; /* 平滑过渡 */
width: 70px; /* 设置按钮宽度 */
height: 30px; /* 设置按钮高度 */
font-weight: bold; /* 加粗文字 */
}
/* 悬浮效果 */
.collect .action-buttons button:hover {
box-shadow: 0 4px 10px rgba(236, 5, 5, 0.2); /* 增加阴影 */
transform: scale(1.1); /* 鼠标悬停时稍微放大 */
}
p {
font-size: 24px;
color: #333;
margin: 20px 0; /* 段落间距 */
}
.artifact-image {
width: 60px; /* 设置图片的默认宽度 */
height: 60px; /* 设置图片的默认高度 */
transition: transform 0.3s ease; /* 设置平滑过渡效果 */
}
.artifact-image:hover {
transform: scale(3); /* 悬浮时放大 1.2 倍 */
}
/* 隐藏select框的样式 */
#categoryFilter {
-webkit-appearance: none; /* 移除默认的下拉框样式 (适用于Webkit浏览器) */
-moz-appearance: none; /* 移除默认的下拉框样式 (适用于Firefox) */
appearance: none; /* 移除默认的下拉框样式 (适用于所有浏览器) */
background: transparent; /* 背景设置为透明 */
border: none; /* 移除边框 */
padding: 1px; /* 设置内边距,给箭头留出空间 */
width: auto; /* 设置宽度,按需调整 */
cursor: pointer; /* 设置鼠标样式为指针 */
color: white; /* 设置文字颜色 */
font-weight: bold; /* 加粗文字 */
font-size: 15px;
}
#categoryFilter option {
color: #333; /* 待选择的选项文字颜色 */
font-family: 'KaiTi', 'STKaiti', serif; /* 设置优雅字体 */
font-weight: bold; /* 加粗文字 */
}
/* 自定义下拉箭头样式 */
#categoryFilter::after {
content: '▼'; /* 使用 Unicode 字符来创建自定义箭头 */
font-size: 16px; /* 设置箭头的大小 */
position: absolute; /* 使用绝对定位 */
right: 10px; /* 设置箭头的位置,距离右侧一定距离 */
top: 50%; /* 垂直居中 */
transform: translateY(-50%); /* 精确垂直居中 */
pointer-events: none; /* 确保箭头不干扰点击 */
color: rgb(3, 3, 3);
}
.input-container {
position: relative;
margin: 50px auto;
width: 200px;
}
.input-container input[type='text'] {
font-size: 20px;
width: 100%;
border: none;
border-bottom: 2px solid #ccc;
padding: 5px 0;
background-color: transparent;
outline: none;
}
.input-container .label {
position: absolute;
top: 0;
left: 0;
color: #ccc;
transition: all 0.3s ease;
pointer-events: none;
}
.input-container input[type='text']:focus ~ .label,
.input-container input[type='text']:valid ~ .label {
top: -20px;
font-size: 16px;
color: #333;
}
.input-container .underline {
position: absolute;
bottom: 0;
left: 0;
height: 2px;
width: 100%;
background-color: #333;
transform: scaleX(0);
transition: all 0.3s ease;
}
.input-container input[type='text']:focus ~ .underline,
.input-container input[type='text']:valid ~ .underline {
transform: scaleX(1);
}
/* From Uiverse.io by Li-Deheng */
.search-panels {
--default-color: #9e9e9e;
--color-text: #ccc;
--color-active: rgb(151, 66, 75);
--color-active-input: #f5f5f5;
--transition: 150ms cubic-bezier(0.4, 0, 0.2, 1);
--width-input: 200px;
font-family: 'KaiTi', 'STKaiti', serif; /* 设置优雅字体 */
font-weight: bold; /* 设置字体加粗 */
display: flex;
justify-content: flex-end; /* 将输入框对齐到右侧 */
width: 100%; /* 确保父容器占满全宽 */
padding: 10px;
}
.search-group {
position: relative;
}
.input {
width: var(--width-input);
border: solid 1.5px var(--default-color);
border-radius: 80px;
background: none;
padding: 20px 3rem 15px 10px;
font-size: 1rem;
color: var(--color-active-input);
transition: border var(--transition);
color: #333;
height: 10%;
margin-right: 20px;
}
.enter-label {
position: absolute;
left: 15px;
color: var(--default-color);
pointer-events: none;
transform: translateY(1rem);
transition: var(--transition);
}
.enter-label {
color: var(--default-color);
pointer-events: none;
}
.input:focus,
input:valid {
outline: none;
border: 1.5px solid var(--color-active);
}
.input:focus ~ label,
input:valid ~ label {
transform: translateY(-50%) scale(0.8);
background-color: var(--color-active);
border-radius: 20px;
padding: 0.2em 0.6em;
color: var(--color-text);
}
.btn-box {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: -1em;
right: 80%;
border-radius: 100%;
transition: 300ms cubic-bezier(0.4, 0, 0.2, 1);
transition-delay: 100ms;
opacity: 0;
}
.input:focus ~ .btn-box,
input:valid ~ .btn-box {
right: 10%;
opacity: 1;
transition-delay: 0s;
transition: var(--transition);
}
.input:not(:focus) ~ .btn-box,
input:not(:valid) ~ .btn-box {
transition-property: right, opacity;
transition-delay: 300ms;
}
.input:focus ~ .btn-box:hover,
input:valid ~ .btn-box:hover {
transform: scale(1.2);
}
.input:focus ~ .btn-box:hover:active,
input:valid ~ .btn-box:hover:active {
transform: scale(1);
}
.btn-search {
position: relative;
cursor: pointer;
background-color: var(--color-active);
width: 2em;
height: 2em;
top: 2.5px;
border: none;
border-radius: 100%;
padding: 0;
transition: var(--transition);
}
.btn-search:active {
transform: scale(1);
}
.btn-search svg {
position: absolute;
top: 25%;
left: 25%;
fill: var(--color-text);
width: 12px;
height: 12px;
}
#svg-circle {
fill: var(--color-text);
transition: var(--transition);
}
.input:focus ~ .btn-box:hover #svg-circle,
input:valid ~ .btn-box:hover #svg-circle {
fill: transparent;
}
.btn-box-x {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 1em;
right: 7%;
opacity: 0;
transform: rotate(-90deg) scale(0.1);
transition: var(--transition);
}
.input:focus ~ .btn-box-x,
input:valid ~ .btn-box-x {
opacity: 1;
transform: rotate(0) scale(1);
}
.btn-cleare {
position: relative;
cursor: pointer;
background-color: transparent;
width: 2em;
height: 2em;
border: none;
border-radius: 100%;
padding: 0;
transition: var(--transition);
}
.btn-cleare svg {
width: 15px;
height: 15px;
}
#cleare-line {
fill: var(--default-color);
}
.btn-box-x:hover #cleare-line {
fill: var(--color-active);
}
</style>
静态效果
①
②
动态效果
①
②
🌭文物详细页设计
template
<template>
<div class="back">
<div class="management-container">
<h1>{{ artifact.name }}</h1>
<div class="content">
<!-- 左边展示图片 -->
<div class="image-container">
<img :src="artifact.thumbnail_path" alt="Artifact Image" class="artifact-image" />
</div>
<!-- 右边展示文本信息 -->
<div class="text-container">
<div v-if="isAdmin"></div>
<div v-else>
<div class="info-item">
<span class="info-key">文物ID</span>
<span class="info-value">{{ artifact.id }}</span>
</div>
<div class="info-item">
<span class="info-key">年代</span>
<span class="info-value">{{ artifact.period }}</span>
</div>
<div class="info-item">
<span class="info-key">类别</span>
<span class="info-value">{{ artifact.category }}</span>
</div>
<div class="info-item">
<span class="info-key">出土地点</span>
<span class="info-value">{{ artifact.material }}</span>
</div>
<div class="info-item">
<span class="info-key">参数</span>
<span class="info-value">{{ artifact.parameter }}</span>
</div>
<div class="info-item">
<span class="info-key">文物现存位置</span>
<span class="info-value">{{ artifact_location.current_location }}</span>
</div>
<div class="info-item">
<span class="info-key">位置详细信息</span>
<span class="info-value">{{ artifact_location.storage_address }}</span>
</div>
<div class="info-item">
<span class="info-key">详情</span>
<span class="info-value">{{ artifact.description }}</span>
</div>
</div>
</div>
</div>
</div>
<div
class="management-container"
style="display: flex; flex-direction: column; align-items: center"
>
<h1>"活的"文物</h1>
<div style="width: fit-content">
<button v-if="ai_button" type="button" class="button" @click="handleAI(artifact.id)">
<span class="fold"></span>
<div class="points_wrapper">
<i class="point"></i>
<i class="point"></i>
<i class="point"></i>
<i class="point"></i>
<i class="point"></i>
<i class="point"></i>
<i class="point"></i>
<i class="point"></i>
<i class="point"></i>
<i class="point"></i>
</div>
<span class="inner">
<svg
class="icon"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2.5"
>
<polyline
points="13.18 1.37 13.18 9.64 21.45 9.64 10.82 22.63 10.82 14.36 2.55 14.36 13.18 1.37"></polyline>
</svg>AI生成</span>
</button>
</div>
<div v-loading="loading2">
<!-- <button @click="generateAudioAndPlay">生成音频并播放</button> -->
<audio ref="audioPlayer" controls v-if="audioUrl" :src="audioUrl"></audio>
<!-- 加载提示 -->
<div v-if="loading2" >正在生成音频,请稍等...</div>
</div>
<div class="create-content" v-loading="loading">
<!-- <span >{{ compiledMarkdown }}</span> -->
<div v-html="compiledMarkdown"></div>
</div>
<div v-loading="loading3">
<!-- <video v-if="videoUrl" :src="videoUrl" controls loop width="100%" height="auto">
你的浏览器不支持视频标签。
</video> -->
<video v-if="v" src="https://cdn.klingai.com/bs2/upload-kling-api/3601927415/image2video/CjS7emdSwTgAAAAAAXBeXw-0_raw_video_1.mp4" controls loop width="100%" height="auto">
你的浏览器不支持视频标签。
</video>
<div v-if="loading3" >正在生成视频,请稍等...</div>
</div>
</div>
</div>
</template>
style
<style scoped>
.back {
width: 100vw;
height: 100vh;
/* background-color: rgb(91, 37, 40); */
background-image: url(https://digicol.dpm.org.cn/images/bg2.jpg);
overflow-y: auto; /* 如果内容超出,允许垂直滚动 */
}
.management-container {
margin: 0px auto;
width: 85%;
height: fit-content; /* 自适应高度 */
background-color: rgb(248, 244, 238); /* 管理容器背景色 */
padding: 120px 40px 80px 40px;
margin-bottom: 15%;
}
.content {
display: flex;
justify-content: space-between;
align-items: center;
gap: 20px; /* 使左右元素之间有间隔 */
}
.image-container {
flex: 1;
max-width: 40%;
}
.artifact-image {
width: 100%;
height: auto;
border-radius: 8px; /* 圆角图片 */
margin-left: 5%;
}
.text-container {
flex: 1;
max-width: 50%;
}
h2,
p {
font-size: 18px;
color: #333;
}
h1 {
text-align: center;
margin-bottom: 5%;
font-family: 'KaiTi', 'STKaiti', serif;
font-size: 45px;
font-weight: bold;
color: #4a4a4a;
}
.text-container input {
display: block;
margin-top: 10px;
padding: 5px;
width: 100%;
}
.text-container label {
margin-top: 10px;
font-weight: bold;
}
.title {
margin: 10% 2%;
font-size: 30px;
font-weight: bold;
}
.info-item {
display: flex;
justify-content: space-between; /* 键和值之间水平排列 */
margin-bottom: 30px; /* 每项之间的垂直间距 */
}
.info-key {
font-weight: bold; /* 键的字体加粗 */
width: 30%; /* 键的宽度占30% */
white-space: nowrap; /* 防止键值换行 */
font-size: 22px; /* 设置字体大小 */
color: #4a4a4a; /* 设置字体颜色 */
}
.info-value {
flex-grow: 1; /* 值的部分占据剩余空间 */
word-wrap: break-word; /* 当文本超出宽度时,自动换行 */
width: 70%; /* 设置值的宽度占70% */
font-size: 21px; /* 设置字体大小 */
}
.button {
--h-button: 48px;
--w-button: 102px;
--round: 0.75rem;
cursor: pointer;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
overflow: hidden;
transition: all 0.25s ease;
background: radial-gradient(
65.28% 65.28% at 50% 100%,
rgba(223, 113, 255, 0.8) 0%,
rgba(223, 113, 255, 0) 100%
),
linear-gradient(0deg, #7a5af8, #7a5af8);
border-radius: var(--round);
border: none;
outline: none;
padding: 20px 60px;
}
.button::before,
.button::after {
content: '';
position: absolute;
inset: var(--space);
transition: all 0.5s ease-in-out;
border-radius: calc(var(--round) - var(--space));
z-index: 0;
}
.button::before {
--space: 1px;
background: linear-gradient(177.95deg, rgba(255, 255, 255, 0.19) 0%, rgba(255, 255, 255, 0) 100%);
}
.button::after {
--space: 2px;
background: radial-gradient(
65.28% 65.28% at 50% 100%,
rgba(223, 113, 255, 0.8) 0%,
rgba(223, 113, 255, 0) 100%
),
linear-gradient(0deg, #7a5af8, #7a5af8);
}
.button:active {
transform: scale(0.95);
}
.fold {
z-index: 1;
position: absolute;
top: 0;
right: 0;
height: 1rem;
width: 1rem;
display: inline-block;
transition: all 0.5s ease-in-out;
background: radial-gradient(
100% 75% at 55%,
rgba(223, 113, 255, 0.8) 0%,
rgba(223, 113, 255, 0) 100%
);
box-shadow: 0 0 3px black;
border-bottom-left-radius: 0.5rem;
border-top-right-radius: var(--round);
}
.fold::after {
content: '';
position: absolute;
top: 0;
right: 0;
width: 150%;
height: 150%;
transform: rotate(45deg) translateX(0%) translateY(-18px);
background-color: #e8e8e8;
pointer-events: none;
}
.button:hover .fold {
margin-top: -1rem;
margin-right: -1rem;
}
.points_wrapper {
overflow: hidden;
width: 100%;
height: 100%;
pointer-events: none;
position: absolute;
z-index: 1;
}
.points_wrapper .point {
bottom: -10px;
position: absolute;
animation: floating-points infinite ease-in-out;
pointer-events: none;
width: 2px;
height: 2px;
background-color: #fff;
border-radius: 9999px;
}
@keyframes floating-points {
0% {
transform: translateY(0);
}
85% {
opacity: 0;
}
100% {
transform: translateY(-55px);
opacity: 0;
}
}
.points_wrapper .point:nth-child(1) {
left: 10%;
opacity: 1;
animation-duration: 2.35s;
animation-delay: 0.2s;
}
.points_wrapper .point:nth-child(2) {
left: 30%;
opacity: 0.7;
animation-duration: 2.5s;
animation-delay: 0.5s;
}
.points_wrapper .point:nth-child(3) {
left: 25%;
opacity: 0.8;
animation-duration: 2.2s;
animation-delay: 0.1s;
}
.points_wrapper .point:nth-child(4) {
left: 44%;
opacity: 0.6;
animation-duration: 2.05s;
}
.points_wrapper .point:nth-child(5) {
left: 50%;
opacity: 1;
animation-duration: 1.9s;
}
.points_wrapper .point:nth-child(6) {
left: 75%;
opacity: 0.5;
animation-duration: 1.5s;
animation-delay: 1.5s;
}
.points_wrapper .point:nth-child(7) {
left: 88%;
opacity: 0.9;
animation-duration: 2.2s;
animation-delay: 0.2s;
}
.points_wrapper .point:nth-child(8) {
left: 58%;
opacity: 0.8;
animation-duration: 2.25s;
animation-delay: 0.2s;
}
.points_wrapper .point:nth-child(9) {
left: 98%;
opacity: 0.6;
animation-duration: 2.6s;
animation-delay: 0.1s;
}
.points_wrapper .point:nth-child(10) {
left: 65%;
opacity: 1;
animation-duration: 2.5s;
animation-delay: 0.2s;
}
.inner {
z-index: 2;
gap: 6px;
position: relative;
width: 100%;
color: white;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 16px;
font-weight: 500;
line-height: 1.5;
transition: color 0.2s ease-in-out;
}
.inner svg.icon {
width: 18px;
height: 18px;
transition: fill 0.1s linear;
}
.button:focus svg.icon {
fill: white;
}
.button:hover svg.icon {
fill: transparent;
animation:
dasharray 1s linear forwards,
filled 0.1s linear forwards 0.95s;
}
@keyframes dasharray {
from {
stroke-dasharray: 0 0 0 0;
}
to {
stroke-dasharray: 68 68 0 0;
}
}
@keyframes filled {
to {
fill: white;
}
}
/* .create-content {
width: 100%;
} */
/* 设置整个文本的字体大小和行间距 */
.create-content {
font-family: 'Arial', sans-serif; /* 设置字体 */
font-size: 20px; /* 设置默认字体大小 */
line-height: 1.8; /* 设置行间距 */
color: #333; /* 设置文本颜色 */
max-width: 85%; /* 限制最大宽度 */
/* margin: 0 auto; 使内容居中显示 */
padding: 20px; /* 设置内边距 */
}
/* 设置标题的样式 */
.create-content h1,
.create-content h2,
.create-content h3 {
font-weight: bold; /* 设置标题加粗 */
color: #444; /* 设置标题颜色 */
}
/* 设置段落的样式 */
.create-content p {
margin-bottom: 20px; /* 设置段落之间的间距 */
font-size: 18px; /* 设置段落的字体大小 */
}
/* 设置链接的样式 */
.create-content a {
color: #1E90FF; /* 设置链接的颜色 */
text-decoration: none; /* 去掉链接下划线 */
}
.create-content a:hover {
text-decoration: underline; /* 鼠标悬停时显示下划线 */
}
</style>
静态效果
①
②
动态效果
🍿登陆页设计
成功登陆后右下角会有管理员工具
template
<template>
<div class="bg">
<form class="form login" @submit.prevent="handleLogin">
<!-- 退出登录按钮,只有在已登录时显示 -->
<div v-if="isAuthenticated">
<input class="submit" type="submit" @click="handleLogout" value="退出登录" />
</div>
<div v-else>
<span class="input-span">
<label for="text" class="label">账号</label>
<input v-model="account" type="text" name="text" id="text" />
</span>
<span class="input-span">
<label for="password" class="label">密码</label>
<input v-model="password" type="password" name="password" id="password" />
</span>
<input class="submit" type="submit" value="登录" />
</div>
</form>
</div>
</template>
style
<style scoped>
.bg {
width: 100vw;
height: 100vh;
background-image: url(https://digicol.dpm.org.cn/images/bg2.jpg);
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-repeat: no-repeat;
background-position: top center;
background-size: cover;
z-index: 1;
}
.login {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #f5f5f5;
border-radius: 25px;
padding: 40px 50px;
box-sizing: border-box;
}
.form {
--bg-light: #efefef;
--bg-dark: #707070;
--clr: #58bc82;
--clr-alpha: #9c9c9c60;
display: flex;
flex-direction: column;
gap: 2rem;
width: 100%;
max-width: 500px;
padding: 40px 100px;
}
.input-span {
width: 100%;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.form input[type='text'],
.form input[type='password'] {
border-radius: 0.5rem;
padding: 1rem 0.75rem;
border: none;
display: flex;
align-items: center;
gap: 0.5rem;
background-color: #d5d5d560;
outline: 2px solid grey;
width: 100%;
margin-bottom: 20px;
}
.form input[type='text']:focus,
.form input[type='password']:focus {
outline: 2px solid #5b2528;
}
.label {
align-self: flex-start;
color: #5b2528;
font-weight: 600;
}
.submit {
padding: 1rem 0.75rem;
width: 100%;
border-radius: 3rem;
background-color: #5b2528;
color: var(--bg-light);
border: none;
cursor: pointer;
transition: all 300ms;
font-weight: 600;
font-size: 0.9rem;
margin-left: 10px;
margin-top: 10px;
}
.logout {
padding: 1rem 0.75rem;
width: 88%;
border-radius: 3rem;
background-color: #5b2528;
color: var(--bg-light);
border: none;
cursor: pointer;
transition: all 300ms;
font-weight: 600;
font-size: 0.9rem;
}
.submit:hover {
background-color: #8d6371;
color: white;
}
.span {
text-decoration: none;
color: var(--bg-dark);
}
.span a {
color: var(--clr);
}
</style>
静态效果
动态效果
🌮添加/修改信息页设计
template
<template>
<div class="back">
<div class="form-container">
<h2 style="text-align: center; margin-bottom: 20px; margin-top: 10px">
{{ isEdit ? '更新文物信息' : '添加文物' }}
</h2>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
class="demo-ruleForm"
:size="formSize"
status-icon
>
<el-form-item label="文物ID" prop="id" v-if="isEdit">
<el-input v-model="ruleForm.id" disabled />
</el-form-item>
<el-form-item label="文物名称" prop="name">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="年代" prop="period">
<el-input v-model="ruleForm.period" />
</el-form-item>
<el-form-item label="类别" prop="category">
<el-select v-model="ruleForm.category" placeholder="选择类别">
<el-option label="传世品" value="传世品" />
<el-option label="革命文物" value="革命文物" />
<el-option label="国史文物" value="国史文物" />
<el-option label="货币" value="货币" />
<el-option label="考古发掘品" value="考古发掘品" />
<el-option label="民族民俗文物" value="民族民俗文物" />
<el-option label="古籍文献" value="古籍文献" />
<el-option label="外国文物" value="外国文物" />
<el-option label="艺术品" value="艺术品" />
</el-select>
</el-form-item>
<el-form-item label="参数" prop="parameter">
<el-input v-model="ruleForm.parameter" />
</el-form-item>
<el-form-item label="出土地点和时间" prop="material">
<el-input v-model="ruleForm.material" />
</el-form-item>
<el-form-item label="图片链接" prop="thumbnail_path">
<el-input v-model="ruleForm.thumbnail_path" />
</el-form-item>
<el-form-item label="详情" prop="description">
<el-input
v-model="ruleForm.description"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4 }"
/>
</el-form-item>
<el-form-item>
<div style="width: 100%; display: flex; justify-content: center">
<el-button type="primary" @click="submitForm(ruleFormRef)">
{{ isEdit ? '更新文物' : '提交' }}
</el-button>
<el-button @click="resetForm(ruleFormRef)">重置</el-button>
<el-button v-if="isEdit" @click="handleDelete(ruleForm.id)"> 删除文物 </el-button>
</div>
</el-form-item>
</el-form>
</div>
</div>
</template>
style
<style scoped>
.back {
width: 100vw;
min-height: 100vh;
background-image: url(https://digicol.dpm.org.cn/images/bg2.jpg);
/* background-color: rgb(91, 37, 40); */
overflow-y: auto;
}
.form-container {
width: 60%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 6px;
padding-top: 50px;
background-color: #f4f4f4;
box-sizing: border-box;
border-radius: 10px;
}
.demo-ruleForm {
display: flex;
flex-direction: column;
gap: 10px;
width: 100%;
max-width: 500px;
margin: 0 auto;
}
</style>
静态效果
①
②
动态效果
🥩数据分析页
template
<template>
<div class="back">
<div class="management-container">
<div ref="chart1" style="width: 100%; height: 600px"></div>
<div ref="chart2" style="width: 100%; height: 400px; margin-top: 10%"></div>
<div ref="chart3" style="width: 100%; height: 400px; margin-top: 10%"></div>
</div>
</div>
</template>
style
<style scoped>
.back {
width: 100vw;
height: 100vh;
background-color: rgb(91, 37, 40);
overflow-y: auto;
}
.management-container {
margin: -4px auto;
width: 90%;
height: fit-content;
background-color: rgb(250, 247, 244);
padding: 40px 15px;
box-sizing: border-box;
margin-bottom: 15%;
padding-top: 12%;
}
</style>
静态效果
①
②
③
动态效果
四、收获
这次综合设计实践我收获特别多!我是负责前端页面的设计,刚开始页面的原型不太好设计,我就参照多个博物馆的配色、样式以及一些布局等,使我有了很大的启发。有了这些想法后,我就着手开始敲代码,对于页面的一些布局,有时候不懂padding该设为多少,不懂margin该设为多少,不懂改用flex布局还是float布局,但是通过不断的调试,我逐渐熟练起来了,面对所有困难我都不怕了!通过不断地调试,学习文档,学习怎么布局、怎么调出一个好看的界面,和我们的组员一起攻克困难,和组长对接需求,不断设计、调试页面,使其最终呈现出了一个挺好看的效果hhh。