BitMap实现打卡(一)
前提说明
- 这里我们采用
Vue3 + TS 进行前端打卡页面的编写 - 采用
Element Plus : https://element-plus.org/zh-CN - 后端我们使用
Spring Boot2.6.5 + JDK17 进行编码
很显然,用这些主要是为了装x,其实用其他的并没有啥区别
前端环境搭建
创建项目
控制台输入指令
yarn create @vitejs/app clock_in
选择vue然后选择vue-ts
创建.env.development
# 开发环境
VITE_APP_TITLE = "打卡"
#端口号
VITE_APP_PORT = "3002"
# 请求接口
VITE_APP_DEV_URL = "http://localhost:8080"
# 前缀
VITE_APP_BASE_API = "/api"
创建.env.production
# 开发环境
VITE_APP_TITLE = "打卡"
#端口号
VITE_APP_PORT = "3002"
# 请求接口
VITE_APP_DEV_URL = "http://localhost:8080"
# 前缀
VITE_APP_BASE_API = "/api"
配置vite.js
import {defineConfig, loadEnv} from 'vite'
import vue from '@vitejs/plugin-vue'
const path = require('path')
const resolve = (dir: string) => path.join(__dirname, dir)
export default ({mode}) => defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': resolve('./src'),
'@v': resolve('./src/views'),
'@c': resolve('./src/components'),
'@u': resolve('./src/utils'),
'@a': resolve('./src/api'),
},
},
server: {
host: '0.0.0.0',
port: Number(loadEnv(mode, process.cwd()).VITE_APP_PORT),
strictPort: true,
https: false,
open: true,
proxy: {
'/api': {
target: mode==='development'?loadEnv(mode, process.cwd()).VITE_APP_DEV_URL:loadEnv(mode, process.cwd()).VITE_APP_PROD_URL,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
},
hmr: {
overlay: true
}
},
build: {
chunkSizeWarningLimit: 1500,
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
}
}
}
}
})
安装包依赖
cnpm i
允许使用node(爆红再使用)
cnpm i --save-dev @types/node
安装Element Plus
cnpm install element-plus --save
安装sass
cnpm install sass --save
修改main.ts
import {createApp} from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
const app = createApp(App)
app.use(ElementPlus, {
locale: zhCn,
})
app.mount('#app')
安装Axios
cnpm install axios --save
安装moment.js
cnpm install moment --save
创建type.ts
export interface IResponse {
success: boolean,
errCode: string,
errMessage: string,
data: any
}
配置axios
import axios, {AxiosInstance, AxiosRequestConfig, AxiosResponse} from 'axios';
import {ElMessage} from "element-plus";
import {IResponse} from "./type";
let service: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API + "/api",
headers: {'Content-Type': 'application/json;charset=utf-8'}
});
// 请求拦截器
service.interceptors.request.use(
(config: AxiosRequestConfig) => {
return config;
},
(error: any) => {
return Promise.reject(error);
}
)
// 响应拦截器
service.interceptors.response.use(
(response: AxiosResponse) => {
let res: IResponse = response.data
if (!res.success) {
ElMessage.error({message: res.errMessage || '请求失败', duration: 5 * 1000})
}
return Promise.resolve(res.data)
},
(error: any) => {
const {response} = error;
if (response) {
ElMessage.error({message: '请求失败', duration: 5 * 1000})
return Promise.reject(response.data);
} else {
ElMessage.warning('网络连接异常,请稍后再试!');
}
}
);
export default service
编写测试接口
import request from "../util/request";
export const test = () => {
return request({
url: '/test',
method: 'GET'
})
}
创建一个打卡界面
<script setup lang="ts">
import {reactive, ref, unref} from "vue";
import {ElNotification} from "element-plus";
import moment from "moment";
interface ClockData {
day: string,
clock: boolean
}
// 是否已经打卡
let isClocked = ref<boolean>(false)
// 打卡信息
let clockData = ref<Array<ClockData>>([
{
"day": "2022-01-01",
"clock": false
},
{
"day": "2022-01-02",
"clock": false
},
{
"day": "2022-04-24",
"clock": true
},
{
"day": "2022-04-25",
"clock": true
}
])
// 打卡
const clockIn = async () => {
ElNotification({showClose: true, message: '打卡成功', type: 'success', duration: 2000})
isClocked.value = !unref(isClocked)
clockData.value.push({
day: moment().format("YYYY-MM-DD"),
clock: unref(isClocked)
})
console.log(clockData)
}
// 判断是否打卡
const checkClockIn = () => {
}
</script>
<template>
<div class="picker">
<el-card style="height: 760px">
<div style="display: flex;justify-content: space-between">
<span style="font-size: 22px;display: block;margin-bottom: 30px;margin-left: 10px;">打卡</span>
<span v-if="!isClocked"><el-button type="primary" class="el-icon-s-promotion" style="border: none"
@click="clockIn"><span
style="color: white;font-weight: bolder">未打卡</span></el-button></span>
<span v-else><el-button type="success" class="el-icon-s-promotion" style="border: none"><span
style="color: white;font-weight: bolder">已打卡</span></el-button></span>
</div>
<el-calendar :first-day-of-week="7">
<template #dateCell="{ data }">
<p>{{ data.day.split('-').slice(2).join('-') }}<br/></p>
<div v-for="(item, index) in clockData" :key="index">
<div v-if="data.day === item.day">
<span v-if="item.clock">
<i class="el-icon-check" style="color: green;font-weight: bolder;">已打卡</i>
</span>
</div>
</div>
</template>
</el-calendar>
</el-card>
</div>
</template>
<style scoped lang="scss">
.picker {
display: flex;
justify-content: center;
align-items: center;
}
::v-deep(.el-calendar-table .el-calendar-day) {
padding: 0;
}
::v-deep(.el-calendar-table td.is-today ) {
background-color: #fff;
}
::v-deep(.el-calendar-table td.is-selected ) {
background-color: #fff;
}
::v-deep(.el-calendar-table .el-calendar-day) {
height: 60px;
font-size: 12px;
text-align: center;
}
::v-deep(.el-calendar-table:not(.is-range) td.prev) {
.calendarFont {
color: #C0C4CC;
}
pointer-events: none;
}
::v-deep(.el-calendar-table thead th) {
font-size: 12px;
padding-bottom: 6px;
}
.cal ::v-deep(.el-calendar-day .calendar_circle1) {
margin: 0 auto;
padding: 2px;
text-align: center;
}
.cal ::v-deep(.el-calendar-day .calendar_circle2) {
border: 1px solid #DE4747;
border-radius: 50%;
margin: 0 auto;
padding: 2px;
text-align: center;
}
</style>
|