temp1
This commit is contained in:
parent
89815a4d96
commit
04568368d7
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
68
components/AddEventModal.vue
Normal file
68
components/AddEventModal.vue
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<view v-if="show" class="modal-mask">
|
||||
<view class="modal-content">
|
||||
<view class="modal-title">新建日程</view>
|
||||
<input v-model="_title" placeholder="请输入标题" class="input" />
|
||||
<view class="sn">时间:<input v-model="_hour" type="number" min="0" max="23" />:<input v-model="_min" type="number" min="0" max="59" /></view>
|
||||
<view class="sn">
|
||||
颜色:
|
||||
<input v-model="_color" placeholder="#e3fae6" />
|
||||
</view>
|
||||
<view class="buttons">
|
||||
<button @click="$emit('cancel')">取消</button>
|
||||
<button @click="ok">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
const props = defineProps({
|
||||
show: Boolean
|
||||
});
|
||||
const emit = defineEmits(['ok', 'cancel']);
|
||||
const _title = ref('');
|
||||
const _hour = ref('');
|
||||
const _min = ref('');
|
||||
const _color = ref('#e3fae6');
|
||||
function ok() {
|
||||
emit('ok', { title:_title.value, startHour:Number(_hour.value), startMin:Number(_min.value), color:_color.value });
|
||||
_title.value = '';
|
||||
_hour.value = '';
|
||||
_min.value = '';
|
||||
_color.value = '#e3fae6';
|
||||
}
|
||||
watch(() => props.show, v => {
|
||||
if (!v) {
|
||||
_title.value = '';
|
||||
_hour.value = '';
|
||||
_min.value = '';
|
||||
_color.value = '#e3fae6';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.modal-mask {
|
||||
position: fixed; left: 0; top: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,.16);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
z-index: 999;
|
||||
}
|
||||
.modal-content {
|
||||
background: #fff; padding: 32rpx 28rpx; border-radius: 16rpx;
|
||||
min-width: 440rpx;
|
||||
}
|
||||
.modal-title {
|
||||
color: #2885ff; font-size: 20px; font-weight: 600; margin-bottom: 25rpx;
|
||||
}
|
||||
.input {
|
||||
padding: 12rpx; border-radius: 8rpx; margin: 10rpx 0; border: 1px solid #e3e3e3; width: 90%;
|
||||
}
|
||||
.buttons {
|
||||
margin-top: 24rpx; display: flex; justify-content: flex-end; gap: 30rpx;
|
||||
}
|
||||
.sn {
|
||||
margin-bottom: 14rpx;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
53
components/BottomTabbar.vue
Normal file
53
components/BottomTabbar.vue
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<template>
|
||||
<view class="bottom-tabbar">
|
||||
<view
|
||||
v-for="item in items"
|
||||
:key="item.value"
|
||||
class="tabbar-item"
|
||||
:class="{active: item.value === modelValue}"
|
||||
@click="$emit('update:modelValue', item.value)"
|
||||
>
|
||||
<uv-icon :name="item.icon" :color="item.value === modelValue ? '#2885ff' : '#888'" size="22" />
|
||||
<view class="name">{{ item.label }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
items: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
required: true
|
||||
}
|
||||
});
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.bottom-tabbar {
|
||||
position: fixed;
|
||||
left: 0; right: 0; bottom: 0;
|
||||
display: flex;
|
||||
height: 104rpx;
|
||||
background: #fff;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
z-index: 99;
|
||||
}
|
||||
.tabbar-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #888;
|
||||
font-size: 12px;
|
||||
}
|
||||
.tabbar-item.active {
|
||||
color: #2885ff;
|
||||
}
|
||||
.name {
|
||||
margin-top: 7rpx;
|
||||
}
|
||||
</style>
|
||||
81
components/DateBar.vue
Normal file
81
components/DateBar.vue
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<template>
|
||||
<scroll-view scroll-x class="date-bar">
|
||||
<view
|
||||
v-for="item in days"
|
||||
:key="item.date"
|
||||
class="date-item"
|
||||
:class="{selected: item.date===modelValue, holiday: item.isHoliday, weekend: item.isWeekend}"
|
||||
@click="$emit('update:modelValue', item.date)"
|
||||
>
|
||||
<view class="week">{{ item.weekStr }}</view>
|
||||
<view class="date">{{ item.day }}</view>
|
||||
<view class="tips" v-if="item.tip">{{ item.tip }}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
days: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.date-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
background: #fff;
|
||||
padding: 0 8rpx 0 8rpx;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.date-item {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
width: 68rpx;
|
||||
min-width: 68rpx;
|
||||
margin-right: 8rpx;
|
||||
margin-top: 12rpx;
|
||||
cursor: pointer;
|
||||
height: 76rpx;
|
||||
border-radius: 14rpx;
|
||||
transition: background .18s;
|
||||
}
|
||||
.week {
|
||||
font-size: 13px;
|
||||
color: #b1b1b1;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
.date {
|
||||
font-size: 21px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
.tips {
|
||||
font-size: 10px;
|
||||
color: #fa5e5e;
|
||||
margin-top: 2rpx;
|
||||
}
|
||||
.selected {
|
||||
background: linear-gradient(180deg, #eaf5ff 70%, #c0eaff 100%);
|
||||
color: #2885ff;
|
||||
border-bottom: 4rpx solid #2885ff;
|
||||
}
|
||||
.holiday .date {
|
||||
color: #fa5e5e;
|
||||
}
|
||||
.weekend .week {
|
||||
color: #179c29;
|
||||
}
|
||||
</style>
|
||||
27
components/FabPlus.vue
Normal file
27
components/FabPlus.vue
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<template>
|
||||
<button class="fab-plus" @click="$emit('click')">+</button>
|
||||
</template>
|
||||
<style scoped lang="scss">
|
||||
.fab-plus {
|
||||
position: fixed;
|
||||
bottom: 100rpx;
|
||||
right: 48rpx;
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 50%;
|
||||
background: #2885ff;
|
||||
color: #fff;
|
||||
box-shadow: 0 6rpx 32rpx 0 #90c3fa;
|
||||
font-size: 52rpx;
|
||||
z-index: 9;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: box-shadow .3s;
|
||||
}
|
||||
.fab-plus:active {
|
||||
box-shadow: 0 2rpx 12rpx 0 #b1d8fc;
|
||||
background:#0D5ECD;
|
||||
}
|
||||
</style>
|
||||
34
components/ScheduleBlock.vue
Normal file
34
components/ScheduleBlock.vue
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<template>
|
||||
<view class="event-block" :style="{background: event.color||'#e3fae6'}">
|
||||
<view class="event-time">{{ event.startHour }}:{{ event.startMin || '00' }}</view>
|
||||
<view class="event-title">{{ event.title }}</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
event: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.event-block {
|
||||
padding: 8rpx 18rpx 8rpx 10rpx;
|
||||
border-radius: 8rpx;
|
||||
margin: 4rpx 0;
|
||||
font-size: 13px;
|
||||
min-width: 120rpx;
|
||||
color: #3a3a3a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.event-time {
|
||||
color: #fa5e5e;
|
||||
font-size: 11px;
|
||||
margin-right: 7rpx;
|
||||
}
|
||||
.event-title {
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
59
components/TimeTable.vue
Normal file
59
components/TimeTable.vue
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<template>
|
||||
<view class="schedule-timeline">
|
||||
<view
|
||||
v-for="hour in hours"
|
||||
:key="hour"
|
||||
class="timeline-row"
|
||||
>
|
||||
<view class="time-label">{{ hour }}:00</view>
|
||||
<view class="schedule-cell">
|
||||
<ScheduleBlock
|
||||
v-for="item in getEventsByHour(hour)"
|
||||
:key="item.id"
|
||||
:event="item"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import ScheduleBlock from './ScheduleBlock.vue';
|
||||
const props = defineProps({
|
||||
hours: {
|
||||
type: Array,
|
||||
default: () => Array.from({length: 12}, (_,i) => i+8) // 8~19点
|
||||
},
|
||||
events: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
// 按小时过滤日程
|
||||
function getEventsByHour(hour) {
|
||||
return props.events.filter(e => e.startHour === hour);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.schedule-timeline {
|
||||
padding: 0 12rpx 60px;
|
||||
}
|
||||
.timeline-row {
|
||||
display: flex;
|
||||
border-bottom: 1px dashed #e3e3e3;
|
||||
align-items: center;
|
||||
height: 52px;
|
||||
}
|
||||
.time-label {
|
||||
width: 44px;
|
||||
color: #bbb;
|
||||
font-size: 13px;
|
||||
}
|
||||
.schedule-cell {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
min-height: 44px;
|
||||
}
|
||||
</style>
|
||||
60
components/TopTabs.vue
Normal file
60
components/TopTabs.vue
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<view class="top-tabs">
|
||||
<view
|
||||
v-for="tab in tabs"
|
||||
:key="tab.value"
|
||||
class="tab"
|
||||
:class="{active: tab.value === modelValue}"
|
||||
@click="$emit('update:modelValue', tab.value)"
|
||||
>
|
||||
{{ tab.label }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
tabs: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => []
|
||||
},
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
required: true
|
||||
}
|
||||
});
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.top-tabs {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #eee;
|
||||
background: #fff;
|
||||
}
|
||||
.tab {
|
||||
flex: 1;
|
||||
padding: 24rpx 0 18rpx 0;
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
color: #888;
|
||||
position: relative;
|
||||
transition: color .2s;
|
||||
}
|
||||
.tab.active {
|
||||
color: #2885ff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.tab.active::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 25%;
|
||||
right: 25%;
|
||||
height: 4rpx;
|
||||
border-radius: 3rpx;
|
||||
background: #2885ff;
|
||||
bottom: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,83 +1,106 @@
|
|||
<template>
|
||||
<uv-tabs :list="list" @click="click"></uv-tabs>
|
||||
<view class="content">
|
||||
<view class="text-area">
|
||||
<text class="title">{{ title }}</text>
|
||||
</view>
|
||||
<uv-icon name="photo" size="30" color="#909399"></uv-icon>
|
||||
<button class="uv-reset-button">点击登录</button>
|
||||
<button>点击登录</button>
|
||||
<view>
|
||||
<uv-calendar ref="calendar" mode="single" @confirm="confirm"></uv-calendar>
|
||||
<button @click="open">打开</button>
|
||||
</view>
|
||||
</view>
|
||||
<uv-tabbar :value="value" @change="val => value = val">
|
||||
<uv-tabbar-item text="首页" icon="home" dot></uv-tabbar-item>
|
||||
<uv-tabbar-item text="放映厅" icon="photo" badge="3"></uv-tabbar-item>
|
||||
<uv-tabbar-item text="直播" icon="play-right"></uv-tabbar-item>
|
||||
<uv-tabbar-item text="我的" icon="account"></uv-tabbar-item>
|
||||
</uv-tabbar>
|
||||
<!-- 顶部Tabs栏 -->
|
||||
<TopTabs :tabs="topTabs" v-model="topTabValue" />
|
||||
|
||||
<!-- 日期条 -->
|
||||
<DateBar :days="weekDays" v-model="selectedDate" />
|
||||
|
||||
<!-- 时间轴表格 -->
|
||||
<TimeTable :hours="hours" :events="eventsInDay" />
|
||||
|
||||
<!-- 悬浮新建按钮 -->
|
||||
<FabPlus @click="showAdd = true" />
|
||||
|
||||
<!-- 新建日程弹窗 -->
|
||||
<AddEventModal :show="showAdd" @ok="addEvent" @cancel="showAdd = false" />
|
||||
|
||||
<!-- 底部导航 -->
|
||||
<BottomTabbar :items="tabbarItems" v-model="tabbarVal" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
const title = ref('Hello UNI');
|
||||
const value = ref(0);
|
||||
const list = [
|
||||
{ name: '关注' },
|
||||
{ name: '推荐' },
|
||||
{ name: '电影' },
|
||||
{ name: '科技' },
|
||||
import { ref, computed } from 'vue';
|
||||
import TopTabs from '@/components/TopTabs.vue';
|
||||
import DateBar from '@/components/DateBar.vue';
|
||||
import TimeTable from '@/components/TimeTable.vue';
|
||||
import FabPlus from '@/components/FabPlus.vue';
|
||||
import BottomTabbar from '@/components/BottomTabbar.vue';
|
||||
import AddEventModal from '@/components/AddEventModal.vue';
|
||||
|
||||
// 顶部tabs选项
|
||||
const topTabs = [
|
||||
{ label: '日程编辑', value: 0 },
|
||||
{ label: '内容看板', value: 1 },
|
||||
{ label: '待办事项', value: 2 },
|
||||
{ label: '消息内容', value: 3 }
|
||||
];
|
||||
const topTabValue = ref(0);
|
||||
|
||||
const calendar = ref(null);
|
||||
|
||||
const click = (item) => {
|
||||
console.log('item', item);
|
||||
};
|
||||
const open = () => {
|
||||
if (calendar.value) calendar.value.open();
|
||||
};
|
||||
const confirm = (e) => {
|
||||
console.log('日历选择:', e);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (uni && uni.$uv && uni.$uv.os) {
|
||||
console.log(uni.$uv.os());
|
||||
// 生成一周日期
|
||||
function getWeekDays() {
|
||||
const days = [];
|
||||
const base = new Date();
|
||||
base.setHours(0,0,0,0);
|
||||
for(let i=-2; i<=4; i++) {
|
||||
const d = new Date(base);
|
||||
d.setDate(base.getDate() + i);
|
||||
const ymd = d.toISOString().slice(0,10);
|
||||
days.push({
|
||||
date: ymd,
|
||||
weekStr: '日一二三四五六'[d.getDay()],
|
||||
day: d.getDate(),
|
||||
tip: '',
|
||||
isHoliday: d.getDay() === 0 || d.getDay() === 6,
|
||||
isWeekend: d.getDay() === 6 || d.getDay() === 0,
|
||||
});
|
||||
}
|
||||
});
|
||||
return days;
|
||||
}
|
||||
const weekDays = ref(getWeekDays());
|
||||
const selectedDate = ref(weekDays.value.find(i=>i.date === new Date().toISOString().slice(0,10))?.date || weekDays.value[2].date);
|
||||
|
||||
// 小时段
|
||||
const hours = Array.from({length: 13}, (_,i)=>i+8); // 8~20点
|
||||
// 示例日程
|
||||
const allEvents = ref([
|
||||
{id:1,title:'日程标题',startHour:15,startMin:30,color:'#e3fae6',date:weekDays.value[3].date},
|
||||
{id:2,title:'日程标题',startHour:16,startMin:0,color:'#fae1e1',date:weekDays.value[3].date},
|
||||
{id:3,title:'日程标题',startHour:23,startMin:0,color:'#e3fae6',date:weekDays.value[3].date},
|
||||
]);
|
||||
|
||||
// 根据当前选择日期过滤
|
||||
const eventsInDay = computed(() =>
|
||||
allEvents.value.filter(e=>e.date===selectedDate.value)
|
||||
);
|
||||
|
||||
// 悬浮按钮/弹窗控制
|
||||
const showAdd = ref(false);
|
||||
function addEvent(e) {
|
||||
// e是{title, startHour, startMin, color},补充date
|
||||
allEvents.value.push({
|
||||
...e, date: selectedDate.value,
|
||||
id: Date.now()
|
||||
});
|
||||
showAdd.value = false;
|
||||
}
|
||||
|
||||
// 底部导航
|
||||
const tabbarItems = [
|
||||
{ label: '任务列表', value: 0, icon: 'list' },
|
||||
{ label: '首页', value: 1, icon: 'home' },
|
||||
{ label: '工作台', value: 2, icon: 'calendar' },
|
||||
{ label: '月度考核', value: 3, icon: 'integral' },
|
||||
{ label: '管理', value: 4, icon: 'account' }
|
||||
];
|
||||
const tabbarVal = ref(1);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/uni.scss";
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.logo {
|
||||
height: 200rpx;
|
||||
width: 200rpx;
|
||||
margin-top: 200rpx;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50rpx;
|
||||
}
|
||||
.text-area {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
color: $uni-color-primary;
|
||||
}
|
||||
.icon {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
:deep(.bottom-tabbar) { z-index: 1000 !important; }
|
||||
|
||||
.schedule-timeline {
|
||||
padding-bottom: 130rpx !important;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user