Browse Source

添加应用基础结构和样式,包含首页、导航栏、状态栏等组件,更新数据管理功能,增加日期处理工具,完善项目配置文件

master
npmrun 2 months ago
commit
1eeb944be6
  1. 1
      .gitignore
  2. 46
      App.uvue
  3. BIN
      assets/fonts/ZhanKuKuaiLeTi2016XiuDingBan-1.ttf
  4. 6
      assets/style/common.scss
  5. 85
      components/x-navbar/x-navbar.uvue
  6. 35
      components/x-page/x-page.uvue
  7. 20
      components/x-status-bar/x-status-bar.uvue
  8. 116
      data/index.uts
  9. 20
      index.html
  10. 10
      main.uts
  11. 48
      manifest.json
  12. 17
      pages.json
  13. 222
      pages/index/index.uvue
  14. 1
      readme.md
  15. BIN
      static/logo.png
  16. 76
      uni.scss
  17. 103
      utils/date.uts

1
.gitignore

@ -0,0 +1 @@
unpackage

46
App.uvue

@ -0,0 +1,46 @@
<script lang="uts">
let firstBackTime = 0
export default {
onLaunch: function () {
console.log('App Launch')
},
onShow: function () {
console.log('App Show')
},
onHide: function () {
console.log('App Hide')
},
// #ifdef APP-ANDROID
onLastPageBackPress: function () {
console.log('App LastPageBackPress')
if (firstBackTime == 0) {
uni.showToast({
title: '再按一次退出应用',
position: 'bottom',
})
firstBackTime = Date.now()
setTimeout(() => {
firstBackTime = 0
}, 2000)
} else if (Date.now() - firstBackTime < 2000) {
firstBackTime = Date.now()
uni.exit()
}
},
// #endif
onExit: function () {
console.log('App Exit')
},
}
</script>
<style lang="scss">
/*每个页面公共css */
.uni-row {
flex-direction: row;
}
.uni-column {
flex-direction: column;
}
</style>

BIN
assets/fonts/ZhanKuKuaiLeTi2016XiuDingBan-1.ttf

Binary file not shown.

6
assets/style/common.scss

@ -0,0 +1,6 @@
@font-face {
font-family: ZhanKuKuaiLeTi;
src: url('@/assets/fonts/ZhanKuKuaiLeTi2016XiuDingBan-1.ttf');
}

85
components/x-navbar/x-navbar.uvue

@ -0,0 +1,85 @@
<template>
<view class="x-navbar">
<x-status-bar></x-status-bar>
<view class="x-navbar__content"></view>
<view class="x-navbar__fixed">
<x-status-bar></x-status-bar>
<view class="x-navbar__content">
<view class="x-navbar__content__left">
<slot name="left"></slot>
</view>
<view class="x-navbar__content__title">
<slot></slot>
</view>
<view class="x-navbar__content__right">
<slot name="right"></slot>
</view>
</view>
</view>
</view>
</template>
<script setup>
const res = uni.getSystemInfoSync()
const statusBarHeight = ref(res.statusBarHeight)
const xNavbarStyle = computed(() => {
return {
height: statusBarHeight.value
}
})
</script>
<style lang="scss">
$navbar__height: 75rpx;
$navbar__bg: white;
$navbar__title__size: 35px;
$navbar__title__color: deeppink;
$navbar__title__family: cursive;
.x-navbar {
.x-status-bar {
background-color: $navbar__bg;
}
.x-navbar__fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
border-bottom: 1px solid #eaeaea;
}
.x-navbar__content {
height: $navbar__height;
background-color: $navbar__bg;
display: flex;
align-items: center;
justify-content: center;
position: relative;
.x-navbar__content__title {
font-size: $navbar__title__size;
color: $navbar__title__color;
font-family: $navbar__title__family;
}
&__left {
position: absolute;
left: 0;
height: 100%;
align-items: center;
justify-content: center;
}
&__right {
position: absolute;
right: 0;
height: 100%;
align-items: center;
justify-content: center;
}
}
}
</style>

35
components/x-page/x-page.uvue

@ -0,0 +1,35 @@
<template>
<view class="x-page">
<!-- #ifdef APP -->
<scroll-view class="x-page__page" :show-scrollbar="false">
<!-- #endif -->
<slot></slot>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</view>
</template>
<script>
export default {
name: "x-page",
data() {
return {
};
}
}
</script>
<style>
.x-page {
display: flex;
flex-direction: column;
height: 100%;
}
.x-page__page {
height: 100%;
}
</style>

20
components/x-status-bar/x-status-bar.uvue

@ -0,0 +1,20 @@
<template>
<view class="x-status-bar" :style="xStatusBarStyle"></view>
</template>
<script setup>
const res = uni.getSystemInfoSync()
const statusBarHeight = ref(res.statusBarHeight)
const xStatusBarStyle = computed(() => {
return {
height: statusBarHeight.value
}
})
</script>
<style lang="scss">
.x-status-bar {
height: var(--status-bar-height);
}
</style>

116
data/index.uts

@ -0,0 +1,116 @@
const primaryKey = "id";
const allData: IItem[] = [];
interface IItem {
id?: number;
timestamp: string;
[key: string]: any; // 允许任意其他字段
}
interface IPartialItem {
id?: number;
timestamp?: string;
[key: string]: any; // 允许任意其他字段
}
let key = 0;
// 添加记录
function addRecord(data: IItem): IItem {
const id = data.id ?? ++key;
const existingIndex = allData.findIndex((v) => v.id === id);
if (existingIndex === -1) {
const newRecord = {
id,
...data,
timestamp: data.timestamp || String(Date.now())
};
allData.push(newRecord);
return newRecord;
} else {
throw new Error(`Record with id ${id} already exists`);
}
}
// 查询所有记录*-
function getAllRecords(): IItem[] {
return [...allData]; // 返回副本以避免外部修改
}
// 根据ID查询单条记录
function getRecordById(id: number): IItem | undefined {
return allData.find((v) => v.id === id);
}
// 更新记录
function updateRecord(id: number, updates: IPartialItem): IItem | undefined {
const index = allData.findIndex((v) => v.id === id);
if (index !== -1) {
const updatedRecord = {
...allData[index],
...updates,
id // 确保ID不会被更新
};
allData[index] = updatedRecord;
return updatedRecord;
}
return undefined;
}
// 删除记录
function deleteRecord(id: number): boolean {
const index = allData.findIndex((v) => v.id === id);
if (index !== -1) {
allData.splice(index, 1);
return true;
}
return false;
}
// 查询记录 (简单条件)
function queryRecords(conditions: IPartialItem): IItem[] {
return allData.filter(item => {
return Object.entries(conditions).every(([key, value]: any) => {
return item[key] === value;
});
});
}
// 事务支持
function transaction(operations: (() => void)[]): boolean {
const originalData = [...allData];
try {
operations.forEach(op => op());
return true;
} catch (error) {
// 回滚
allData.length = 0;
allData.push(...originalData);
console.error('Transaction failed:', error);
return false;
}
}
// 示例使用
try {
// 添加记录
addRecord({ timestamp: String(Date.now()), name: 'Item 1', price: 100 });
addRecord({ timestamp: String(Date.now()), name: 'Item 2', price: 200 });
// 更新记录
transaction([
() => updateRecord(1, { price: 150 }),
() => addRecord({ timestamp: String(Date.now()), name: 'Item 3' })
]);
// 查询
const expensiveItems = queryRecords({ price: 200 });
console.log(expensiveItems);
// 删除
deleteRecord(2);
console.log(getAllRecords());
} catch (error) {
console.error('Database operation failed:', error);
}

20
index.html

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main"></script>
</body>
</html>

10
main.uts

@ -0,0 +1,10 @@
import App from './App.uvue'
import "@/assets/style/common.scss"
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}

48
manifest.json

@ -0,0 +1,48 @@
{
"name": "uni-x",
"appid": "__UNI__D9CF915",
"description": "",
"versionName": "1.0.0",
"versionCode": "100",
"uni-app-x": {},
/* */
"quickapp": {},
/* */
"mp-weixin": {
"appid": "",
"setting": {
"urlCheck": false
},
"usingComponents": true
},
"mp-alipay": {
"usingComponents": true
},
"mp-baidu": {
"usingComponents": true
},
"mp-toutiao": {
"usingComponents": true
},
"uniStatistics": {
"enable": false
},
"vueVersion": "3",
"app": {
"distribute": {
"icons": {
"android": {
"hdpi": "",
"xhdpi": "",
"xxhdpi": "",
"xxxhdpi": ""
}
}
}
},
"web": {
"router": {
"mode": ""
}
}
}

17
pages.json

@ -0,0 +1,17 @@
{
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationStyle": "custom"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app x",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"uniIdRouter": {}
}

222
pages/index/index.uvue

@ -0,0 +1,222 @@
<template>
<x-page>
<x-navbar>
<text class="navbar-text">LuMi</text>
</x-navbar>
<view class="tabs">
<view class="tab">
<text class="tab-text">{{nowYear}}年{{nowMonth}}月</text>
</view>
</view>
<view class="rili">
<view class="grid">
<view class="grid-item" :style="getGridItemStyle(day)" v-for="day in all" :key="day">
<view class="grid-item-box">
<view class="grid-item-content">
<text class="grid-item-text" :style="getGridItemTextStyle(day)">{{ day.label }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- <text class="clear" @click="click2" v-if="recordDate.length">清除所有</text> -->
<view class="list">
<view class="item" v-for="(item, index) in recordDate">
<text class="label">{{item.label}}</text>
<text class="close" @click="delIem(item, index)">x</text>
</view>
</view>
<view class="float-btn" @click="click">
<text class="float-btn-text">打卡</text>
</view>
</x-page>
</template>
<script setup>
import { getCurMonthDayNum, getCurrentMonth, formatFull, getLastMonth, getLastMonthDay, format, getWeek, getNextMonthDay, generateDate } from "@/utils/date.uts"
type TItem = { label : string; value : Date; }
const nowDate = ref(new Date())
const nowYear = computed(() => {
return nowDate.value.getFullYear()
})
const nowMonth = computed(() => {
return nowDate.value.getMonth() + 1
})
const recordDate = ref<TItem[]>([])
const all = computed(() => {
return generateDate<TItem>(format(nowDate.value), (v : Date) => ({
label: v.getDate() + "",
value: v
}))
})
const isRecord = (date : Date) => {
let count = 0
for (var i = 0; i < recordDate.value.length; i++) {
var item = recordDate.value[i];
if (format(item.value) == format(date)) {
count++
}
}
return count
}
const isToday = (date : Date) => {
return format(nowDate.value) == format(date)
}
const getGridItemStyle = (day : TItem) => {
const count = isRecord(day.value)
return {
backgroundColor: count ? `rgba(0, 0, 0, ${count * 0.2})` : 'rgba(0, 0, 0, 0.05)'
}
}
const getGridItemTextStyle = (day : TItem) => {
const count = isRecord(day.value)
return {
color: count ? '#ffffff' : isToday(day.value) ? 'black' : 'rgba(0, 0, 0, .08)'
}
}
const data = uni.getStorageSync("save")
if (Array.isArray(data as [])) {
let result : TItem[] = []
for (let i = 0; i < data.length; i++) {
let v : number = data[i] as number
const date = new Date(v)
result.push({
label: formatFull(date),
value: date
})
}
recordDate.value = result
} else {
recordDate.value = []
}
const click2 = () => {
uni.removeStorageSync("save")
recordDate.value = []
}
const click = () => {
const date = new Date()
recordDate.value.unshift({
label: formatFull(date),
value: date
} as TItem)
uni.setStorageSync("save", unref(recordDate).map(v => v.value.getTime()))
}
const delIem = (item : TItem, index : number) => {
recordDate.value.splice(index, 1)
uni.setStorageSync("save", unref(recordDate))
}
</script>
<style lang="scss" scoped>
.navbar-text {
font-size: 45rpx;
font-family: ZhanKuKuaiLeTi;
}
.tabs {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 25rpx 0 15rpx;
margin-left: -20rpx;
.tab {
margin-left: 20rpx;
.tab-text {
font-size: 45rpx;
font-family: ZhanKuKuaiLeTi;
}
}
}
.clear {
padding: 20rpx 100rpx;
}
.list {
.item {
padding: 20rpx 100rpx;
flex-direction: row;
align-items: center;
.label {
flex: 1;
width: 0;
}
}
}
.rili {
margin-top: -30rpx;
.grid {
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
margin-left: -30rpx;
margin-top: -30rpx;
padding: 30rpx 80rpx 0;
.grid-item {
width: 55rpx;
margin-left: 30rpx;
margin-top: 30rpx;
border-radius: 15rpx;
background-color: rgba(0, 0, 0, 0.05);
.grid-item-box {
padding-bottom: 100%;
position: relative;
.grid-item-content {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
align-items: center;
justify-content: center;
.grid-item-text {
color: rgba(0, 0, 0, .08);
font-size: 25rpx;
}
}
}
}
}
}
.float-btn {
position: fixed;
z-index: 99;
left: 50%;
transform: translate(-50%);
bottom: 100rpx;
width: 100rpx;
height: 100rpx;
align-items: center;
justify-content: center;
border-radius: 100rpx;
background-color: white;
box-shadow:
0 2px 10px rgba(0, 0, 0, 0.1);
.float-btn-text {
font-size: 25rpx;
}
}
</style>

1
readme.md

@ -0,0 +1 @@
[](https://ext.dcloud.net.cn/plugin?id=16902)

BIN
static/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

76
uni.scss

@ -0,0 +1,76 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color:#333;//基本色
$uni-text-color-inverse:#fff;//反色
$uni-text-color-grey:#999;//辅助灰色如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable:#c0c0c0;
/* 背景颜色 */
$uni-bg-color:#ffffff;
$uni-bg-color-grey:#f8f8f8;
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
/* 边框颜色 */
$uni-border-color:#c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm:12px;
$uni-font-size-base:14px;
$uni-font-size-lg:16px;
/* 图片尺寸 */
$uni-img-size-sm:20px;
$uni-img-size-base:26px;
$uni-img-size-lg:40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2C405A; // 文章标题颜色
$uni-font-size-title:20px;
$uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px;

103
utils/date.uts

@ -0,0 +1,103 @@
export function getWeek(time : Date) {
let week = time.getDay() // 当前周几
// week = (week - 1) < 0 ? 0 : (week - 1)
if(week == 0) week = 7 // 0 表示周日
return week
}
export function getCurrentMonth(time : Date) : number {
let month = time.getMonth() + 1
return month
}
export function getLastMonth(curDate : Date) {
let lastDate = new Date()
lastDate.setMonth(curDate.getMonth() - 1)
return lastDate
}
export function getLastMonthDay(curDate : Date, offset : number) {
let lastDate = new Date(curDate.getTime())
lastDate.setHours(-24 * offset);
return lastDate
}
export function getNextMonthDay(curDate : Date, offset : number) {
let lastDate = new Date(curDate.getTime())
lastDate.setHours(24 * offset);
return lastDate
}
export function format(time : Date) {
return `${time.getFullYear()}-${time.getMonth() + 1}-${time.getDate()}`
}
export function formatFull(time : Date) {
const year = (time.getFullYear()) + ""
const month = (time.getMonth() + 1) + ""
const date = (time.getDate()) + ""
const hour = (time.getHours()) + ""
const minute = (time.getMinutes()) + ""
const second = (time.getSeconds()) + ""
return `${year}-${month.padStart(2, '0')}-${date.padStart(2, '0')} ${hour.padStart(2, '0')}:${minute.padStart(2, '0')}:${second.padStart(2, '0')}`
}
// 获取当前月份应该有多少天
export function getCurMonthDayNum(time : Date) : number {
let day = 31;
let month = time.getMonth() + 1
let year = time.getFullYear()
if ([1, 3, 5, 7, 8, 10, 12].indexOf(month) != -1) {
day = 31;
} else if ([4, 6, 9, 11].indexOf(month) != -1) {
day = 30;
} else if (year % 4 == 0) {
day = 29
} else {
day = 28
}
return day
}
export function generateDate<T = Date>(_nowDate : Date | string | number, format : (date : Date) => T = (v : Date) => (v as T)) {
if (typeof _nowDate === "string" || typeof _nowDate === "number") {
_nowDate = new Date(_nowDate as string)
}
const allDate : T[] = []
let firstDate = new Date(_nowDate.getTime())
firstDate.setDate(1)
const firstWeek = getWeek(firstDate)
console.log(firstDate.getTime());
for (var i = 1; i < firstWeek; i++) {
const date = getLastMonthDay(firstDate, i)
allDate.unshift(format(new Date(date.getTime())) as T)
}
let curDate = new Date(_nowDate.getTime())
const allDay = getCurMonthDayNum(curDate)
for (var i = 1; i <= getCurMonthDayNum(curDate); i++) {
curDate.setDate(i)
allDate.push(format(new Date(curDate.getTime())) as T)
}
let endDate = new Date(_nowDate.getTime())
endDate.setDate(allDay)
const endWeek = 7 - getWeek(endDate)
for (var i = 1; i <= endWeek; i++) {
const date = getNextMonthDay(endDate, i)
allDate.push(format(new Date(date.getTime())) as T)
}
// 至少6行
if (~~(allDate.length / 7) < 6) {
for (var i = endWeek + 1; i < endWeek + 1 + 7; i++) {
const date = getNextMonthDay(endDate, i)
allDate.push(format(new Date(date.getTime())) as T)
}
}
return allDate
}
Loading…
Cancel
Save