feat(history): 新增历史记录页面

This commit is contained in:
yangzhe 2025-10-29 13:34:05 +08:00
parent 9327eb673a
commit acedbd0477
3 changed files with 667 additions and 11 deletions

View File

@ -215,6 +215,15 @@
"enablePullDownRefresh": false, "enablePullDownRefresh": false,
"navigationStyle": "custom" "navigationStyle": "custom"
} }
},
{
"path" : "pages/home/history/history",
"style" :
{
"navigationBarTitleText" : "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
} }
], ],
"subPackages": [], "subPackages": [],

View File

@ -1,16 +1,6 @@
<template> <template>
<view class="admissions-container"> <view class="admissions-container">
<view class="header"> <header-bar title="在线咨询" @leftClick="handleLeftClick"></header-bar>
<div class="header-left">
<u-icon
class="header-left-icon"
name="arrow-left"
@click="handleLeftClick"
></u-icon>
</div>
<text class="header-title">在线咨询</text>
<div></div>
</view>
<view class="admissions-content"> <view class="admissions-content">
<!-- 自定义tab --> <!-- 自定义tab -->
@ -63,10 +53,12 @@
</template> </template>
<script> <script>
import HeaderBar from "@/components/HeaderBar.vue"; //
import LeaveMessage from "@/components/LeaveMessage.vue"; import LeaveMessage from "@/components/LeaveMessage.vue";
export default { export default {
components: { components: {
HeaderBar, //
LeaveMessage, LeaveMessage,
}, },
data() { data() {

View File

@ -0,0 +1,655 @@
<template>
<view class="history-page">
<header-bar title="历史记录" @leftClick="handleLeftClick"></header-bar>
<view class="tab-container">
<view class="custom-tab">
<view
v-for="tab in tabList"
:key="tab.id"
:class="['tab-item', { active: activeTab === tab.id }]"
@click="switchTab(tab.id)"
>
<text class="tab-text">{{ tab.name }}</text>
<view class="tab-underline" v-if="activeTab === tab.id"></view>
</view>
</view>
<view class="more-icon-container" @click="toggleMoreMenu">
<image
class="more-icon"
src="/static/common/images/icon-more.png"
mode="scaleToFill"
></image>
</view>
<!-- 右上角弹出菜单批量删除 -->
<view v-if="isMoreMenuVisible" class="more-menu" @click.stop>
<view class="menu-card" @click="handleBatchDeleteClick">
<!-- <image
class="menu-icon"
src="/static/common/images/icon_delete.png"
mode="widthFix"
/> -->
<u-icon name="trash" color="#FF647D" size="28"></u-icon>
<text class="menu-text">批量删除</text>
</view>
</view>
</view>
<!-- 点击其他区域关闭弹层 -->
<view v-if="isMoreMenuVisible" class="overlay" @click="hideMoreMenu"></view>
<!-- 内容区域包装器 -->
<view class="content-wrapper">
<!-- 历史记录列表按日期分组日期固定在上方 -->
<view class="history-list">
<view
class="date-group"
v-for="group in groupedHistoryList"
:key="group.header"
>
<view class="date-header">{{ group.header }}</view>
<view class="history-item" v-for="item in group.items" :key="item.id">
<!-- 批量删除模式下的选择框 -->
<view
class="item-checkbox"
v-if="isBatchDeleteMode"
@click="toggleItemSelection(item.id)"
>
<view
class="checkbox"
:class="{ checked: selectedItems.includes(item.id) }"
>
<text class="checkmark" v-if="selectedItems.includes(item.id)"
></text
>
</view>
</view>
<!-- 历史记录内容 -->
<view class="item-content">
<view class="item-header">
<view class="item-icon">
<text class="icon-text">B</text>
</view>
<view class="item-title">{{ item.title }}</view>
</view>
<view class="item-description">{{ item.content }}</view>
</view>
</view>
</view>
</view>
</view>
<!-- 批量删除操作栏 -->
<view class="batch-actions" v-if="isBatchDeleteMode">
<view class="select-all-container" @click="toggleSelectAll">
<view class="checkbox" :class="{ checked: selectAll }">
<text class="checkmark" v-if="selectAll"></text>
</view>
<text class="select-all-text">全选</text>
</view>
<view class="function-btn">
<view class="cancel-btn" @click="cancelBatchDelete">取消</view>
<view class="delete-btn" @click="batchDelete">删除</view>
</view>
</view>
<!-- 提示 -->
<u-toast ref="uToast" />
</view>
</template>
<script>
import HeaderBar from "@/components/HeaderBar.vue";
export default {
components: {
HeaderBar,
},
data() {
return {
activeTab: "1", // tab
tabList: [
{ id: "1", name: "AI咨询" },
{ id: "2", name: "人工咨询" },
],
isBatchDeleteMode: false, //
isMoreMenuVisible: false, //
selectedItems: [], //
selectAll: false, //
historyList: [
{
id: 1,
title: "学校哪些专业比较好?",
content:
"学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?",
time: new Date().getTime(), //
type: "ai",
},
{
id: 2,
title: "学校哪些专业比较好?",
content:
"学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?",
time: new Date().getTime(), //
type: "ai",
},
{
id: 3,
title: "学校哪些专业比较好?",
content:
"学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?",
time: new Date().getTime(), //
type: "ai",
},
{
id: 4,
title: "学校哪些专业比较好?",
content:
"学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?",
time: new Date().getTime(), //
type: "ai",
},
{
id: 5,
title: "学校哪些专业比较好?",
content:
"学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?",
time: new Date().getTime() - 24 * 60 * 60 * 1000 * 2, // 2
type: "ai",
},
{
id: 6,
title: "学校哪些专业比较好?",
content:
"学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?",
time: new Date().getTime() - 365 * 24 * 60 * 60 * 1000, //
type: "human",
},
{
id: 7,
title: "学校哪些专业比较好?",
content:
"学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?",
time: new Date().getTime() - 24 * 60 * 60 * 1000 * 5, // 5
type: "human",
},
],
};
},
computed: {
// tab
filteredHistoryList() {
return this.historyList.filter((item) => {
if (this.activeTab === "1") {
return item.type === "ai";
} else {
return item.type === "human";
}
});
},
//
groupedHistoryList() {
//
const list = [...this.filteredHistoryList].sort(
(a, b) => b.time - a.time
);
const groups = [];
let currentHeader = null;
let currentItems = [];
list.forEach((item) => {
const header = this.formatTime(item.time);
if (header !== currentHeader) {
if (currentItems.length) {
groups.push({ header: currentHeader, items: currentItems });
}
currentHeader = header;
currentItems = [item];
} else {
currentItems.push(item);
}
});
if (currentItems.length) {
groups.push({ header: currentHeader, items: currentItems });
}
return groups;
},
},
methods: {
//
formatTime(timestamp) {
const now = new Date();
const date = new Date(timestamp);
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const itemDate = new Date(
date.getFullYear(),
date.getMonth(),
date.getDate()
);
//
const diffTime = today.getTime() - itemDate.getTime();
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
//
if (diffDays === 0) {
return `${String(date.getMonth() + 1).padStart(2, "0")}/${String(
date.getDate()
).padStart(2, "0")}`;
}
// 1-6
if (diffDays > 0 && diffDays <= 6) {
const weekdays = [
"周日",
"周一",
"周二",
"周三",
"周四",
"周五",
"周六",
];
return weekdays[date.getDay()];
}
//
if (date.getFullYear() === now.getFullYear()) {
return `${String(date.getMonth() + 1).padStart(2, "0")}/${String(
date.getDate()
).padStart(2, "0")}`;
}
//
return `${date.getFullYear()}/${String(date.getMonth() + 1).padStart(
2,
"0"
)}/${String(date.getDate()).padStart(2, "0")}`;
},
handleLeftClick() {
uni.navigateBack();
},
switchTab(tab) {
this.activeTab = tab;
},
// /
toggleMoreMenu() {
this.isMoreMenuVisible = !this.isMoreMenuVisible;
},
//
hideMoreMenu() {
this.isMoreMenuVisible = false;
},
//
cancelBatchDelete() {
this.isBatchDeleteMode = false;
},
//
handleBatchDeleteClick() {
this.isMoreMenuVisible = false;
if (!this.isBatchDeleteMode) {
this.toggleBatchDeleteMode();
}
},
//
toggleBatchDeleteMode() {
this.isBatchDeleteMode = !this.isBatchDeleteMode;
if (!this.isBatchDeleteMode) {
// 退
this.selectedItems = [];
this.selectAll = false;
}
},
//
toggleItemSelection(itemId) {
const index = this.selectedItems.indexOf(itemId);
if (index > -1) {
this.selectedItems.splice(index, 1);
} else {
this.selectedItems.push(itemId);
}
//
this.selectAll =
this.selectedItems.length === this.filteredHistoryList.length;
},
//
toggleSelectAll() {
this.selectAll = !this.selectAll;
if (this.selectAll) {
this.selectedItems = this.filteredHistoryList.map((item) => item.id);
} else {
this.selectedItems = [];
}
},
//
batchDelete() {
if (this.selectedItems.length === 0) {
this.$refs.uToast.show({
title: "请选择要删除的项目",
type: "warning",
});
return;
}
//
this.historyList = this.historyList.filter(
(item) => !this.selectedItems.includes(item.id)
);
//
this.selectedItems = [];
this.selectAll = false;
this.isBatchDeleteMode = false;
this.$refs.uToast.show({
title: "删除成功",
type: "success",
});
},
handleDelete(item, callback) {
console.log("handleDelete", item);
setTimeout(() => {
this.$refs.uToast.show({
title: "撤回成功",
type: "success",
});
callback(true);
}, 1500);
},
},
};
</script>
<style lang="scss" scoped>
.history-page {
height: 100vh;
background-color: #ffffff;
display: flex;
flex-direction: column;
position: relative;
padding-top: 88rpx;
.tab-container {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
}
.custom-tab {
padding: 0 30rpx;
margin: 24rpx 0;
display: flex;
justify-content: space-between;
align-items: center;
.tab-item {
position: relative;
padding: 12rpx 0;
margin-right: 80rpx;
cursor: pointer;
.tab-text {
font-size: 32rpx;
font-weight: 500;
color: #505866;
letter-spacing: 0.04rpx;
}
&.active .tab-text {
color: #4f6aff;
font-weight: 600;
}
.tab-underline {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
right: 0;
width: 80rpx;
height: 4rpx;
background-color: #4f6aff;
border-radius: 2rpx;
}
}
}
.more-icon-container {
width: 50rpx;
height: 50rpx;
margin-right: 30rpx;
.more-icon {
width: 50rpx;
height: 10rpx;
margin-right: 30rpx;
}
}
/* 右上角更多菜单弹层 */
.more-menu {
position: absolute;
top: 80rpx;
right: 30rpx;
z-index: 100;
background-color: #ffffff;
border: 1rpx solid rgba(79, 106, 255, 0.12);
border-radius: 24rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
padding: 24rpx 28rpx;
}
.menu-card {
display: flex;
align-items: center;
gap: 6rpx;
// .menu-icon {
// color: #FF647D;
// width: 32rpx;
// height: 32rpx;
// margin-right: 12rpx;
// }
.menu-text {
font-size: 28rpx;
color: #ff647d;
letter-spacing: 2rpx;
}
}
/* 点击其它区域关闭的遮罩(透明) */
.overlay {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 99;
background: rgba(0, 0, 0, 0);
}
.content-wrapper {
padding: 30rpx;
flex: 1;
overflow-y: auto;
}
.history-list {
.date-group {
margin-bottom: 32rpx;
}
.date-header {
font-size: 26rpx;
color: #9aa0a8;
margin-bottom: 16rpx;
line-height: 1;
}
.history-item {
display: flex;
align-items: flex-start;
padding: 24rpx 0;
.item-checkbox {
margin-right: 24rpx;
padding-top: 8rpx;
cursor: pointer;
width: 48rpx;
flex: 0 0 48rpx; /* 固定宽度,避免内容因显隐而抖动 */
.checkbox {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #d9d9d9;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background-color: #ffffff;
&.checked {
background-color: #ff4757;
border-color: #ff4757;
.checkmark {
color: #ffffff;
font-size: 22rpx;
font-weight: bold;
}
}
}
}
.item-content {
flex: 1;
.item-header {
display: flex;
align-items: center;
margin-bottom: 24rpx;
.item-icon {
width: 48rpx;
height: 48rpx;
background-color: #4f6aff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16rpx;
.icon-text {
color: #ffffff;
font-size: 24rpx;
font-weight: bold;
}
}
.item-title {
font-size: 32rpx;
font-weight: bold;
color: #333333;
flex: 1;
}
}
.item-description {
letter-spacing: 0.04rpx;
font-size: 28rpx;
color: #666666;
line-height: 1.5;
margin-bottom: 12rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
}
}
}
.batch-actions {
height: 130rpx;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 30rpx;
border-top: 1rpx solid #eeeeee;
.select-all-container {
display: flex;
align-items: center;
cursor: pointer;
.checkbox {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #d9d9d9;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background-color: #ffffff;
&.checked {
background-color: #ff4757;
border-color: #ff4757;
.checkmark {
color: #ffffff;
font-size: 22rpx;
font-weight: bold;
}
}
}
.select-all-text {
margin-left: 16rpx;
font-size: 28rpx;
color: #333333;
}
}
.function-btn {
display: flex;
align-items: center;
gap: 20rpx;
.cancel-btn {
background-color: #e5e3e3e4;
color: #333333;
padding: 16rpx 32rpx;
border-radius: 16rpx;
font-size: 28rpx;
cursor: pointer;
}
.delete-btn {
background-color: #ff4757;
color: #ffffff;
padding: 16rpx 32rpx;
border-radius: 16rpx;
font-size: 28rpx;
cursor: pointer;
}
}
}
}
</style>