main
JayChou 8 months ago
commit 36f35fa1b4
  1. 5
      .env.development
  2. 3
      .env.production
  3. 4
      .env.test
  4. 2
      .eslintignore
  5. 60
      .eslintrc.cjs
  6. 24
      .gitignore
  7. 7
      .prettierignore
  8. 9
      .prettierrc.json
  9. 4
      .stylelintignore
  10. 53
      .stylelintrc.cjs
  11. 3
      .vscode/extensions.json
  12. 492
      README.md
  13. 13
      index.html
  14. 61
      package.json
  15. 5112
      pnpm-lock.yaml
  16. 1
      public/vite.svg
  17. 7
      scripts/preinstall.js
  18. 9
      src/App.vue
  19. 23
      src/Layout/footer/index.vue
  20. 134
      src/Layout/index.vue
  21. 19
      src/Layout/main/index.vue
  22. 213
      src/Layout/tabbar/index.vue
  23. BIN
      src/assets/images/LOGO.png
  24. BIN
      src/assets/images/LOGO2.png
  25. BIN
      src/assets/images/banner2.png
  26. BIN
      src/assets/images/banner3.jpg
  27. BIN
      src/assets/images/banner4.jpg
  28. BIN
      src/assets/images/crous-card.png
  29. BIN
      src/assets/images/default.png
  30. BIN
      src/assets/images/item.png
  31. BIN
      src/assets/images/jinglingtu.png
  32. BIN
      src/assets/images/kcfm.jpg
  33. 1
      src/assets/vue.svg
  34. 35
      src/components/SvgIcon/index.vue
  35. 10
      src/components/index.ts
  36. 37
      src/main.ts
  37. 13
      src/router/index.ts
  38. 55
      src/router/module/constRouter/index.ts
  39. 6
      src/store/index.ts
  40. 44
      src/styles/index.scss
  41. 188
      src/styles/reset.scss
  42. 1
      src/styles/variable.scss
  43. 118
      src/utils/rem.js
  44. 20
      src/utils/requset.ts
  45. 12
      src/views/404/index.vue
  46. 14
      src/views/achievement/index.vue
  47. 14
      src/views/appraise/index.vue
  48. 188
      src/views/home/index.vue
  49. 14
      src/views/navigation/index.vue
  50. 5
      src/vite-env-override.d.ts
  51. 1
      src/vite-env.d.ts
  52. 31
      tsconfig.json
  53. 11
      tsconfig.node.json
  54. 58
      vite.config.ts

@ -0,0 +1,5 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
VITE_APP_TITLE = '教学一体化平台'
VITE_APP_BASE_API = 'http://39.106.16.162:8080'
# VITE_APP_BASE_API = 'http://127.0.0.1:8080'

@ -0,0 +1,3 @@
NODE_ENV = 'production'
VITE_APP_TITLE = '教学一体化平台'
# VITE_APP_BASE_API = '/api'

@ -0,0 +1,4 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'test'
VITE_APP_TITLE = '教学一体化平台'
# VITE_APP_BASE_API = '/api'

@ -0,0 +1,2 @@
dist
node_modules

@ -0,0 +1,60 @@
// @see https://eslint.bootcss.com/docs/rules/
module.exports = {
publicPath:"/portal",
env: {
browser: true,
es2021: true,
node: true,
jest: true,
},
/* 指定如何解析语法 */
parser: 'vue-eslint-parser',
/** 优先级低于 parse 的语法解析配置 */
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
parser: '@typescript-eslint/parser',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true,
},
},
/* 继承已有的规则 */
extends: [
'eslint:recommended',
'plugin:vue/vue3-essential',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
plugins: ['vue', '@typescript-eslint'],
/*
* "off" 或 0 ==> 关闭规则
* "warn" 或 1 ==> 打开的规则作为警告(不影响代码执行)
* "error" 或 2 ==> 规则作为一个错误(代码不能执行,界面报错)
*/
rules: {
// eslint(https://eslint.bootcss.com/docs/rules/)
'no-var': 'error', // 要求使用 let 或 const 而不是 var
'no-multiple-empty-lines': ['warn', { max: 1 }], // 不允许多个空行
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-unexpected-multiline': 'error', // 禁止空余的多行
'no-useless-escape': 'off', // 禁止不必要的转义字符
// typeScript (https://typescript-eslint.io/rules)
'@typescript-eslint/no-unused-vars': 'warn', // 禁止定义未使用的变量
'@typescript-eslint/prefer-ts-expect-error': 'error', // 禁止使用 @ts-ignore
'@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-namespace': 'off', // 禁止使用自定义 TypeScript 模块和命名空间。
'@typescript-eslint/semi': 'off',
// eslint-plugin-vue (https://eslint.vuejs.org/rules/)
'vue/multi-word-component-names': 'off', // 要求组件名称始终为 “-” 链接的单词
'vue/script-setup-uses-vars': 'error', // 防止<script setup>使用的变量<template>被标记为未使用
'vue/no-mutating-props': 'off', // 不允许组件 prop的改变
'vue/attribute-hyphenation': 'off', // 对模板中的自定义组件强制执行属性命名样式
},
}

24
.gitignore vendored

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

@ -0,0 +1,7 @@
/dist/*
/html/*
.local
/node_modules/**
**/*.svg
**/*.sh
/public/*

@ -0,0 +1,9 @@
{
"singleQuote": true,
"semi": false,
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "ignore",
"endOfLine": "auto",
"trailingComma": "all",
"tabWidth": 2
}

@ -0,0 +1,4 @@
/node_modules/*
/dist/*
/html/*
/public/*

@ -0,0 +1,53 @@
// @see https://stylelint.bootcss.com/
module.exports = {
extends: [
'stylelint-config-standard', // 配置stylelint拓展插件
'stylelint-config-html/vue', // 配置 vue 中 template 样式格式化
'stylelint-config-standard-scss', // 配置stylelint scss插件
'stylelint-config-recommended-vue/scss', // 配置 vue 中 scss 样式格式化
'stylelint-config-recess-order', // 配置stylelint css属性书写顺序插件,
'stylelint-config-prettier', // 配置stylelint和prettier兼容
],
overrides: [
{
files: ['**/*.(scss|css|vue|html)'],
customSyntax: 'postcss-scss',
},
{
files: ['**/*.(html|vue)'],
customSyntax: 'postcss-html',
},
],
ignoreFiles: [
'**/*.js',
'**/*.jsx',
'**/*.tsx',
'**/*.ts',
'**/*.json',
'**/*.md',
'**/*.yaml',
],
/**
* null => 关闭该规则
* always => 必须
*/
rules: {
'value-keyword-case': null, // 在 css 中使用 v-bind,不报错
'no-descending-specificity': null, // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器
'function-url-quotes': 'always', // 要求或禁止 URL 的引号 "always(必须加上引号)"|"never(没有引号)"
'no-empty-source': null, // 关闭禁止空源码
'selector-class-pattern': null, // 关闭强制选择器类名的格式
'property-no-unknown': null, // 禁止未知的属性(true 为不允许)
'block-opening-brace-space-before': 'always', //大括号之前必须有一个空格或不能有空白符
'value-no-vendor-prefix': null, // 关闭 属性值前缀 --webkit-box
'property-no-vendor-prefix': null, // 关闭 属性前缀 -webkit-mask
'selector-pseudo-class-no-unknown': [
// 不允许未知的选择器
true,
{
ignorePseudoClasses: ['global', 'v-deep', 'deep'], // 忽略属性,修改element默认样式的时候能使用到
},
],
},
}

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

@ -0,0 +1,492 @@
## 教学一体化项目模板介绍
#### 目录
----`leachinontecration olattorm temolate` ----模板名称
----`node modules` ----依赖包
----`public` ----静态文件相关
----`scripts` ----项目钩子脚本文件
----`src` ----根目录开发主要文件
----`api` ----接口文件
----assets ----静态资源(图片图标)
----`copmonents` ----全局组件
----Layout ----layout固定组件
----`router` ----路由
----`store` ----仓库`pinia`
----styles ----全局样式文件
----`index.scss` ----全局样式
----`variable.scss` ----全局`scss`变量
----`utils` -----封装工具文件
----`request.ts` ----封装的请求文件
----`views` ----页面文件
----`App.vue` ----项目根组件
----`main.ts` ----项目入口文件
----`permission.ts` ----路由拦截器文件
----`.env.development` ----开发环境变量
----`.env.production` ----生产环境变量
----`.env.test` ----测试环境变量
----` .eslintignore` ----代码校验排除文件
----`.eslintrc.cjs` ----代码校验文件
----`.gitignore` ----`git`提交排除文件
----`.prettierignore` ----代码美化排除文件
----` .prettierrc.json` ----代码美化配置文件
----`.stylelintignore` ----`css`校验排除文件
----` .stylelintrc.cjs` ----`css`校验文件
----`index.html` ----挂载点
----`package.json` ----项目配置文件
----`tsconfig.json` ----`TypeScript`配置文件
----`vite.config.ts` ----打包配置文件
#### 图标组件
`components`文件下`SvgIcon` 为全局图标组件
用法:
```vue
<div><SvgIcon name="home" color="pink" width="50px" height="50px"/></div>
<!--
name:图标名称
color:图标颜色
width:图标宽度
height:图标高度
图标存放位置在src目录下 assets下icons文件取文件名即可 home.svg 取home即可
-->
```
`svg`图标下载 https://www.iconfont.cn/
选择图标下载`svg`格式修改文件名引入文件即可使用
#### vue3组件传值教程
不管是vue2还是vue3,组件通信方式很重要,不管是项目还是面试都是经常用到的知识点。
**比如:vue2组件通信方式**
**props:**可以实现父子组件、子父组件、甚至兄弟组件通信
**自定义事件**:可以实现子父组件通信
**全局事件总线$bus**:可以实现任意组件通信
**pubsub:**发布订阅模式实现任意组件通信
**vuex**:集中式状态管理容器,实现任意组件通信
**ref**:父组件获取子组件实例VC,获取子组件的响应式数据以及方法
**slot:**插槽(默认插槽、具名插槽、作用域插槽)实现父子组件通信........
### 1.1props
props可以实现父子组件通信,在vue3中我们可以通过defineProps获取父组件传递的数据。且在组件内部不需要引入defineProps方法可以直接使用!
**父组件给子组件传递数据**
```
<Child info="我爱祖国" :money="money"></Child>
```
**子组件获取父组件传递数据:方式1**
```
let props = defineProps({
info:{
type:String,//接受的数据类型
default:'默认参数',//接受默认数据
},
money:{
type:Number,
default:0
}})
```
**子组件获取父组件传递数据:方式2**
```
let props = defineProps(["info",'money']);
```
子组件获取到props数据就可以在模板中使用了,但是切记props是只读的(只能读取,不能修改)
### 1.2自定义事件
在vue框架中事件分为两种:一种是原生的DOM事件,另外一种自定义事件。
原生DOM事件可以让用户与网页进行交互,比如click、dbclick、change、mouseenter、mouseleave....
自定义事件可以实现子组件给父组件传递数据
#### 1.2.1原生DOM事件
代码如下:
```
<pre @click="handler">
我是祖国的老花骨朵
</pre>
```
当前代码级给pre标签绑定原生DOM事件点击事件,默认会给事件回调注入event事件对象。当然点击事件想注入多个参数可以按照下图操作。但是切记注入的事件对象务必叫做$event.
```
<div @click="handler1(1,2,3,$event)">我要传递多个参数</div>
```
在vue3框架click、dbclick、change(这类原生DOM事件),不管是在标签、自定义标签上(组件标签)都是原生DOM事件。
**<!--vue2中却不是这样的,在vue2中组件标签需要通过native修饰符才能变为原生DOM事件-->**
#### 1.2.2自定义事件
自定义事件可以实现子组件给父组件传递数据.在项目中是比较常用的。
比如在父组件内部给子组件(Event2)绑定一个自定义事件
```
<Event2 @xxx="handler3"></Event2>
```
在Event2子组件内部触发这个自定义事件
```
<template>
<div>
<h1>我是子组件2</h1>
<button @click="handler">点击我触发xxx自定义事件</button>
</div>
</template>
<script setup lang="ts">
let $emit = defineEmits(["xxx"]);
const handler = () => {
$emit("xxx", "法拉利", "茅台");
};
</script>
<style scoped>
</style>
```
我们会发现在script标签内部,使用了defineEmits方法,此方法是vue3提供的方法,不需要引入直接使用。defineEmits方法执行,传递一个数组,数组元素即为将来组件需要触发的自定义事件类型,此方执行会返回一个$emit方法用于触发自定义事件。
当点击按钮的时候,事件回调内部调用$emit方法去触发自定义事件,第一个参数为触发事件类型,第二个、三个、N个参数即为传递给父组件的数据。
需要注意的是:代码如下
```
<Event2 @xxx="handler3" @click="handler"></Event2>
```
正常说组件标签书写@click应该为原生DOM事件,但是如果子组件内部通过defineEmits定义就变为自定义事件了
```
let $emit = defineEmits(["xxx",'click']);
```
### 1.3全局事件总线
全局事件总线可以实现任意组件通信,在vue2中可以根据VM与VC关系推出全局事件总线。
但是在vue3中没有Vue构造函数,也就没有Vue.prototype.以及组合式API写法没有this,
那么在Vue3想实现全局事件的总线功能就有点不现实啦,如果想在Vue3中使用全局事件总线功能
可以使用插件mitt实现。
**mitt:官网地址:https://www.npmjs.com/package/mitt**
### 1.4v-model
v-model指令可是收集表单数据(数据双向绑定),除此之外它也可以实现父子组件数据同步。
而v-model实指利用props[modelValue]与自定义事件[update:modelValue]实现的。
下方代码:相当于给组件Child传递一个props(modelValue)与绑定一个自定义事件update:modelValue
实现父子组件数据同步
```
<Child v-model="msg"></Child>
```
在vue3中一个组件可以通过使用多个v-model,让父子组件多个数据同步,下方代码相当于给组件Child传递两个props分别是pageNo与pageSize,以及绑定两个自定义事件update:pageNo与update:pageSize实现父子数据同步
```
<Child v-model:pageNo="msg" v-model:pageSize="msg1"></Child>
```
### 1.5useAttrs
在Vue3中可以利用useAttrs方法获取组件的属性与事件(包含:原生DOM事件或者自定义事件),次函数功能类似于Vue2框架中$attrs属性与$listeners方法。
比如:在父组件内部使用一个子组件my-button
```
<my-button type="success" size="small" title='标题' @click="handler"></my-button>
```
子组件内部可以通过useAttrs方法获取组件属性与事件.因此你也发现了,它类似于props,可以接受父组件传递过来的属性与属性值。需要注意如果defineProps接受了某一个属性,useAttrs方法返回的对象身上就没有相应属性与属性值。
```
<script setup lang="ts">
import {useAttrs} from 'vue';
let $attrs = useAttrs();
</script>
```
### 1.6ref与$parent
ref,提及到ref可能会想到它可以获取元素的DOM或者获取子组件实例的VC。既然可以在父组件内部通过ref获取子组件实例VC,那么子组件内部的方法与响应式数据父组件可以使用的。
比如:在父组件挂载完毕获取组件实例
父组件内部代码:
```
<template>
<div>
<h1>ref与$parent</h1>
<Son ref="son"></Son>
</div>
</template>
<script setup lang="ts">
import Son from "./Son.vue";
import { onMounted, ref } from "vue";
const son = ref();
onMounted(() => {
console.log(son.value);
});
</script>
```
但是需要注意,如果想让父组件获取子组件的数据或者方法需要通过defineExpose对外暴露,因为vue3中组件内部的数据对外“关闭的”,外部不能访问
```
<script setup lang="ts">
import { ref } from "vue";
//数据
let money = ref(1000);
//方法
const handler = ()=>{
}
defineExpose({
money,
handler
})
</script>
```
$parent可以获取某一个组件的父组件实例VC,因此可以使用父组件内部的数据与方法。必须子组件内部拥有一个按钮点击时候获取父组件实例,当然父组件的数据与方法需要通过defineExpose方法对外暴露
```
<button @click="handler($parent)">点击我获取父组件实例</button>
```
### 1.7provide与inject
**provide[提供]**
**inject[注入]**
vue3提供两个方法provide与inject,可以实现隔辈组件传递参数
组件组件提供数据:
provide方法用于提供数据,此方法执需要传递两个参数,分别提供数据的key与提供数据value
```
<script setup lang="ts">
import {provide} from 'vue'
provide('token','admin_token');
</script>
```
后代组件可以通过inject方法获取数据,通过key获取存储的数值
```
<script setup lang="ts">
import {inject} from 'vue'
let token = inject('token');
</script>
```
### 1.8pinia
**pinia官网:https://pinia.web3doc.top/**
pinia也是集中式管理状态容器,类似于vuex。但是核心概念没有mutation、modules,使用方式参照官网
### 1.9slot
插槽:默认插槽、具名插槽、作用域插槽可以实现父子组件通信.
**默认插槽:**
在子组件内部的模板中书写slot全局组件标签
```
<template>
<div>
<slot></slot>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>
```
在父组件内部提供结构:Todo即为子组件,在父组件内部使用的时候,在双标签内部书写结构传递给子组件
注意开发项目的时候默认插槽一般只有一个
```
<Todo>
<h1>我是默认插槽填充的结构</h1>
</Todo>
```
**具名插槽:**
顾名思义,此插槽带有名字在组件内部留多个指定名字的插槽。
下面是一个子组件内部,模板中留两个插槽
```
<template>
<div>
<h1>todo</h1>
<slot name="a"></slot>
<slot name="b"></slot>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>
```
父组件内部向指定的具名插槽传递结构。需要注意v-slot:可以替换为#
```
<template>
<div>
<h1>slot</h1>
<Todo>
<template v-slot:a> //可以用#a替换
<div>填入组件A部分的结构</div>
</template>
<template v-slot:b>//可以用#b替换
<div>填入组件B部分的结构</div>
</template>
</Todo>
</div>
</template>
<script setup lang="ts">
import Todo from "./Todo.vue";
</script>
<style scoped>
</style>
```
**作用域插槽**
作用域插槽:可以理解为,子组件数据由父组件提供,但是子组件内部决定不了自身结构与外观(样式)
子组件Todo代码如下:
```
<template>
<div>
<h1>todo</h1>
<ul>
<!--组件内部遍历数组-->
<li v-for="(item,index) in todos" :key="item.id">
<!--作用域插槽将数据回传给父组件-->
<slot :$row="item" :$index="index"></slot>
</li>
</ul>
</div>
</template>
<script setup lang="ts">
defineProps(['todos']);//接受父组件传递过来的数据
</script>
<style scoped>
</style>
```
父组件内部代码如下:
```
<template>
<div>
<h1>slot</h1>
<Todo :todos="todos">
<template v-slot="{$row,$index}">
<!--父组件决定子组件的结构与外观-->
<span :style="{color:$row.done?'green':'red'}">{{$row.title}}</span>
</template>
</Todo>
</div>
</template>
<script setup lang="ts">
import Todo from "./Todo.vue";
import { ref } from "vue";
//父组件内部数据
let todos = ref([
{ id: 1, title: "吃饭", done: true },
{ id: 2, title: "睡觉", done: false },
{ id: 3, title: "打豆豆", done: true },
]);
</script>
<style scoped>
</style>
```
##

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

@ -0,0 +1,61 @@
{
"name": "teaching-integration-platform-template",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --open",
"build:test": "vite build",
"build:pro": "vue-tsc && vite build --mode production",
"preview": "vite preview",
"lint": "eslint src",
"fix": "eslint src --fix",
"format": "prettier --write \"./**/*.{html,vue,ts,js,json,md}\"",
"lint:eslint": "eslint src/**/*.{ts,vue} --cache --fix",
"lint:style": "stylelint src/**/*.{css,scss,vue} --cache --fix",
"preinstall": "node ./scripts/preinstall.js"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"axios": "^1.6.8",
"element-plus": "^2.6.2",
"pinia": "^2.1.7",
"postcss-plugin-px2rem": "^0.8.1",
"px2rem-loader": "^0.1.9",
"vue": "^3.4.21",
"vue-router": "^4.3.0"
},
"devDependencies": {
"@babel/eslint-parser": "^7.24.1",
"@types/node": "^20.11.30",
"@typescript-eslint/eslint-plugin": "^7.4.0",
"@typescript-eslint/parser": "^7.4.0",
"@vitejs/plugin-vue": "^5.0.4",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.24.0",
"postcss": "^8.4.38",
"postcss-html": "^1.6.0",
"postcss-scss": "^4.0.9",
"prettier": "^3.2.5",
"sass": "^1.72.0",
"sass-loader": "^14.1.1",
"stylelint": "^16.3.1",
"stylelint-config-prettier": "^9.0.5",
"stylelint-config-recess-order": "^5.0.0",
"stylelint-config-recommended-scss": "^14.0.0",
"stylelint-config-standard": "^36.0.0",
"stylelint-config-standard-scss": "^13.0.0",
"stylelint-config-standard-vue": "^1.0.0",
"stylelint-order": "^6.0.4",
"stylelint-scss": "^6.2.1",
"typescript": "^5.2.2",
"vite": "^5.2.0",
"vite-plugin-svg-icons": "^2.0.1",
"vue-tsc": "^2.0.6"
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,7 @@
if (!/pnpm/.test(process.env.npm_execpath || '')) {
console.warn(
`\u001b[33mThis repository must using pnpm as the package manager ` +
` for scripts to work properly.\u001b[39m\n`,
)
process.exit(1)
}

@ -0,0 +1,9 @@
<template>
<router-view></router-view>
</template>
<script lang="ts" setup>
import {} from 'vue'
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,23 @@
<template>
<div class="footer">
<h1>FOOTER</h1>
</div>
</template>
<script lang="ts" setup>
import {} from 'vue'
</script>
<style lang="scss" scoped>
.footer {
// position: fixed;
// bottom: 0;
height: 300px;
width: 100%;
background-color: #252527;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
}
</style>

@ -0,0 +1,134 @@
<template>
<Tabbar />
<div class="main-container">
<Main />
</div>
<Footer />
<div ref="totop" v-if="show" class="gotop" @click="scrollToTop"></div>
</template>
<script lang="ts" setup>
//
import Tabbar from './tabbar/index.vue'
//
import Main from './main/index.vue'
//
import Footer from './footer/index.vue'
import { onMounted, ref } from 'vue'
const totop = ref()
const flog = ref(false)
const show = ref(true)
const scrollToTop = () => {
flog.value = true
const dom = document.querySelector('.gotop') as Element
console.log(dom)
totop.value.style.backgroundPosition = '-6px -145px'
setTimeout(() => {
totop.value.style.backgroundPosition = '-363px -78px'
}, 200)
setTimeout(() => {
totop.value.style.backgroundPosition = '-316px -78px'
}, 400)
setTimeout(() => {
totop.value.style.backgroundPosition = '-6px -80px'
totop.value.style.height = '60px'
scrollTop()
}, 500)
}
const scrollTop = () => {
window.scrollTo({
top: 0,
left: 0,
behavior: 'smooth',
});
setTimeout(() => {
show.value = false
},1500);
setTimeout(() => {
show.value = true
flog.value = false
num = 800
},1600);
}
let num = 800
onMounted(() => {
//
window.onscroll = function () {
let high = document.documentElement.scrollTop || document.body.scrollTop //
// console.log(high,totop.value.offsetTop)
if (flog.value) {
num = num - 40
totop.value.style.top = num + 'px'
return
}
//
if (high >= 900) {
totop.value.style.display = 'block'
} else {
totop.value.style.display = 'none'
}
}
})
</script>
<style lang="scss" scoped>
.main-container {
width: 100%;
min-height: 100vh;
// padding-top: 80PX;
}
.gotop {
display: none;
width: 45PX;
height: 45PX;
z-index: 999;
position: fixed;
bottom: 50px;
right: 50px;
background-image: url('../assets/images/jinglingtu.png');
background-position: -264PX -78PX;
transition: top 0.2s linear;
animation: show 0.5s ease-in-out;
}
.gotop:hover {
transition: background-image 0.2s ease-in;
background-position: -215PX -78PX;
}
.bian {
animation: bian 0.1s ease-in-out;
}
.fei {
position: absolute;
// animation: fei 3s ease-in-out;
}
@keyframes show {
0% {
opacity: 0;
transform: translate3d(0, 100%, 0);
}
100% {
opacity: 1;
transform: none;
}
}
@keyframes bian {
0% {
background-position: -6PX -145PX;
}
100% {
background-position: -316PX -78PX;
}
}
// @keyframes fei {
// 0% {
// bottom: 50px;
// }
// 100% {
// top: 10%;
// }
// }
</style>

@ -0,0 +1,19 @@
<template>
<!-- <section class="app-main">
<transition name="fade-transform" mode="out-in">
<router-view :key="$route.path" />
</transition>
</section> -->
<router-view></router-view>
</template>
<script lang="ts" setup>
import {} from 'vue'
import {useRoute } from 'vue-router'
const $route = useRoute()
console.log( $route);
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,213 @@
<template>
<div :class="!flog ? 'tabbar' : 'tabbar-active'">
<div class="container">
<div class="left">
<div class="logo-box">
<div class="lesson">LGOG</div>
</div>
<div class="menu">
<ul>
<li v-for="(item, index) in constRouter.children" :key="index">
{{ item.meta.title }}
</li>
</ul>
</div>
</div>
<div class="right">
<div class="registered gradient">注册</div>
<div class="login">登录</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { constRouter } from '@/router/module/constRouter'
// import { useRouter, useRoute } from 'vue-router'
import { onMounted, ref } from 'vue'
// const $router = useRouter()
// const $route = useRoute()
console.log(constRouter, '111')
const flog = ref(false)
onMounted(() => {
window.addEventListener('scroll', () => {
if (window.scrollY >= 50) {
flog.value = true
} else if (flog.value && window.scrollY <= 50) {
flog.value = false
}
})
})
</script>
<style lang="scss" scoped>
.logo-box {
display: flex;
align-items: center;
column-gap: 20px;
.logo {
width: 50px;
img {
width: 100%;
height: 100%;
}
}
.lesson {
font-size: 30px;
font-weight: bolder;
color: #1d2129;
}
}
.tabbar {
position: fixed;
top: 0;
z-index: 1;
height: 80px;
width: 100%;
background-color: transparent;
animation-duration: 0.3s;
animation-name: tabber-to;
}
.tabbar-active {
position: fixed;
top: 0;
height: 80px;
width: 100%;
background-color: #fff;
animation-duration: 0.3s;
animation-name: tabber;
z-index: 999;
box-shadow: 0px 0px 43px rgba(0, 0, 0, 0.2);
}
@keyframes tabber {
0% {
// transform: translateY(-80px);
background-color: transparent;
}
100% {
// transform: translateY(0);
background-color: #fff;
}
}
@keyframes tabber-to {
0% {
// transform: translateY(-80px);
background-color: #fff;
}
100% {
// transform: translateY(0);
background-color: transparent;
}
}
.container {
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 auto;
width: $base-container-width;
.left {
display: flex;
align-items: center;
}
.right {
display: flex;
div {
width: 108px;
height: 40px;
text-align: center;
line-height: 40px;
border-radius: 5px;
font-size: 14px;
cursor: pointer;
}
.registered{
color: #fff;
}
.login{
color: #1D2129;
margin-left: 20px;
border: 1px solid #E5E6EB;
}
}
.menu {
position: relative;
padding: 0 20px;
height: 100%;
display: flex;
align-items: center;
ul {
display: flex;
align-items: center;
li {
padding: 12px 20px;
color: #1d2129;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
position: relative;
}
li:hover {
color: #0033f7;
transform: translateY(-5px);
}
.active::before {
content: ' ';
display: block;
position: absolute;
top: 0px;
left: 50%;
transform: translateX(-50%);
width: 5px;
height: 5px;
border-radius: 50%;
background-color: #fff;
}
}
.entry {
position: absolute;
top: 5px;
right: 30px;
display: flex;
align-content: center;
div {
// padding: 5px;
margin: 5px;
margin-right: 0;
padding: 0 10px;
border-right: 1px solid #fff;
color: #fff;
font-size: 14px;
cursor: pointer;
}
div:last-child {
border-right: none;
}
}
}
.logo {
// height: 100%;
display: flex;
align-items: center;
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

@ -0,0 +1,35 @@
<template>
<div>
<svg :style="{ width: width, height: height }">
<use :xlink:href="prefix + name" :fill="color"></use>
</svg>
</div>
</template>
<script setup lang="ts">
defineProps({
//xlink:href
prefix: {
type: String,
default: '#icon-',
},
//svg
name: String,
//svg
color: {
type: String,
default: '',
},
//svg
width: {
type: String,
default: '16px',
},
//svg
height: {
type: String,
default: '16px',
},
})
</script>
<style scoped></style>

@ -0,0 +1,10 @@
import SvgIcon from './SvgIcon/index.vue'
import type { App, Component } from 'vue'
const components: { [name: string]: Component } = { SvgIcon }
export default {
install(app: App) {
Object.keys(components).forEach((key: string) => {
app.component(key, components[key])
})
},
}

@ -0,0 +1,37 @@
import { createApp } from 'vue'
// import G6 from '@antv/g6'
// 导入svg插件
import 'virtual:svg-icons-register'
import App from './App.vue'
// 引入elementplus组件库
import ElementPlus from 'element-plus'
// 样式;
import 'element-plus/dist/index.css'
//@ts-expect-error忽略当前文件ts类型的检测否则有红色提示(打包会失败)
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import gloablComponent from './components/index'
// 引入全局样式
import '@/styles/index.scss'
// 引入路由
import router from '@/router/index'
// 引入仓库
import pinia from '@/store/index'
import '@/utils/rem.js'
// 创建vue实例
const app = createApp(App)
// 注册element plus组件库
app.use(ElementPlus, {
locale: zhCn,
})
// 注册全局组件
app.use(gloablComponent)
app.use(router)
app.use(pinia)
// app.use(G6)
// 挂载点
app.mount('#app')
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}

@ -0,0 +1,13 @@
import { createRouter, createWebHashHistory,createWebHistory } from 'vue-router'
import { constRouter } from './module/constRouter'
const router = createRouter({
history: createWebHashHistory(),
routes: [constRouter],
scrollBehavior() {
return {
left: 0,
top: 0,
}
},
})
export default router

@ -0,0 +1,55 @@
export const constRouter: any =
{
path: '/',
component: () => import('@/Layout/index.vue'),
name: 'Layout',
redirect: '/home',
meta: {
icon: '',
title: '',
hidden: false,
},
children: [
{
path: '/home',
name: 'Home',
component: () => import('@/views/home/index.vue'),
meta: {
icon: '',
title: '首页',
hidden: false,
},
},
{
path: '/appraise',
name: 'Appraise',
component: () => import('@/views/appraise/index.vue'),
meta: {
icon: '',
title: '竞赛评价',
hidden: false,
},
},
{
path: '/navigation',
name: 'Navigation',
component: () => import('@/views/navigation/index.vue'),
meta: {
icon: '',
title: '竞赛导航',
hidden: false,
},
},
{
path: '/achievement',
name: 'Achievement',
component: () => import('@/views/achievement/index.vue'),
meta: {
icon: '',
title: '竞赛成果',
hidden: false,
},
},
],
}

@ -0,0 +1,6 @@
// 引入仓库
import { createPinia } from 'pinia'
// 创建仓库
const pinia = createPinia()
// 暴露仓库
export default pinia

@ -0,0 +1,44 @@
// 引入清除浏览器默认样式文件
@import './reset.scss';
.container-1420{
width: 1420px;
margin: 0 auto;
// background-color: #c2acac;
}
.gradient{
background: linear-gradient(90deg, rgba(66,217,172,1) 0%, rgba(0,208,208,1) 100%);
}
/* 设置滚动条的宽度和颜色 */
::-webkit-scrollbar {
width: 10px;
/* 滚动条宽度 */
}
/* 设置滚动条的轨道背景色 */
::-webkit-scrollbar-track {
background-color: #f1f1f1;
}
/* 设置滚动条上下按钮的样式 */
::-webkit-scrollbar-button {
background-color: #bfbfbf;
}
/* 设置滚动条的滑块样式 */
::-webkit-scrollbar-thumb {
background-color: #bfbfbf;
border-radius: 5px;
/* 滑块圆角 */
}
/* 设置滑块在hover状态下的样式 */
::-webkit-scrollbar-thumb:hover {
background-color: #999999;
}
.scene-tooltip {
color: #6da0ff !important;
font-weight: 700;
opacity: .5;
}

@ -0,0 +1,188 @@
/**
* ENGINE
* v0.2 | 20150615
* License: none (public domain)
*/
*,
*:after,
*:before {
box-sizing: border-box;
outline: none;
}
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
font: inherit;
font-size: 100%;
margin: 0;
padding: 0;
vertical-align: baseline;
border: 0;
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
&:before,
&:after {
content: '';
content: none;
}
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -.5em;
}
sub {
bottom: -.25em;
}
table {
border-spacing: 0;
border-collapse: collapse;
}
input,
textarea,
button {
font-family: inhert;
font-size: inherit;
color: inherit;
}
select {
text-indent: .01px;
text-overflow: '';
border: 0;
border-radius: 0;
-webkit-appearance: none;
-moz-appearance: none;
}
select::-ms-expand {
display: none;
}
code,
pre {
font-family: monospace, monospace;
font-size: 1em;
}

@ -0,0 +1 @@
$base-container-width:1620px;

@ -0,0 +1,118 @@
;(function(win, lib) {
var doc = win.document;
var docEl = doc.documentElement;
var metaEl = doc.querySelector('meta[name="viewport"]');
var flexibleEl = doc.querySelector('meta[name="flexible"]');
var dpr = 0;
var scale = 0;
var tid;
var flexible = lib.flexible || (lib.flexible = {});
if (metaEl) {
console.warn('将根据已有的meta标签来设置缩放比例');
var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
if (match) {
scale = parseFloat(match[1]);
dpr = parseInt(1 / scale);
}
} else if (flexibleEl) {
var content = flexibleEl.getAttribute('content');
if (content) {
var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
if (initialDpr) {
dpr = parseFloat(initialDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
if (maximumDpr) {
dpr = parseFloat(maximumDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
}
}
if (!dpr && !scale) {
var isAndroid = win.navigator.appVersion.match(/android/gi);
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}
docEl.setAttribute('data-dpr', dpr);
if (!metaEl) {
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
doc.write(wrap.innerHTML);
}
}
function refreshRem(){
var width = docEl.getBoundingClientRect().width;
if (width / dpr > 1920) {
width = 1920 * dpr;
}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
win.addEventListener('resize', function() {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}, false);
win.addEventListener('pageshow', function(e) {
if (e.persisted) {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);
if (doc.readyState === 'complete') {
doc.body.style.fontSize = 12 * dpr + 'px';
} else {
doc.addEventListener('DOMContentLoaded', function(e) {
doc.body.style.fontSize = 12 * dpr + 'px';
}, false);
}
refreshRem();
flexible.dpr = win.dpr = dpr;
flexible.refreshRem = refreshRem;
flexible.rem2px = function(d) {
var val = parseFloat(d) * this.rem;
if (typeof d === 'string' && d.match(/rem$/)) {
val += 'px';
}
return val;
}
flexible.px2rem = function(d) {
var val = parseFloat(d) / this.rem;
if (typeof d === 'string' && d.match(/px$/)) {
val += 'rem';
}
return val;
}
})(window, window['lib'] || (window['lib'] = {}));

@ -0,0 +1,20 @@
// 引入第三方请求库axios
import axios from 'axios'
// 创建axios实例
const server = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout:30000,
})
// 创建请求拦截器
server.interceptors.request.use((config) => {
return config
})
// 创建相应拦截器
server.interceptors.response.use((response) => {
return response.data
})
// 暴露axios实例
export default server

@ -0,0 +1,12 @@
<template>
<h1>404</h1>
</template>
<script lang='ts' setup>
import { } from 'vue'
</script>
<style lang='scss' scoped>
</style>

@ -0,0 +1,14 @@
<template>
<div>
成果
</div>
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
</script>
<style lang='scss' scoped>
</style>

@ -0,0 +1,14 @@
<template>
<div>
评价
</div>
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
</script>
<style lang='scss' scoped>
</style>

@ -0,0 +1,188 @@
<template>
<div class="container-1420">
<div class="banner">
<div class="title">2024年度全国电子科技大赛火热报名中</div>
<div class="description">
我是一段比赛简介我是一段比赛简介我是一段比赛简介我是一段比赛简介我是一段比赛简介我是一段比赛简介我是一段比赛简介我是一段比赛简介我是一段比赛简介
</div>
<div class="application gradient">立即报名</div>
<div class="nav-title">
<div class="top">竞赛导航</div>
<div class="bottom">30+项目登陆后报名</div>
</div>
</div>
<!-- 比赛列表 -->
<div class="race-list">
<div class="item" v-for="i in 8" :key="i">
<div class="image">
<img src="../../assets/images/item.png" alt="" />
</div>
<div class="reac-info">
<div class="reac-title">全国计算机等级大赛</div>
<div class="reac-project">全国计算机等级大赛项目</div>
<div class="time">报名时间:2022-01-01 18:00-2022-01-01:18:00</div>
</div>
</div>
</div>
<!-- 新闻列表 -->
<div class="news-list">
<div class="news-title">
<div class="top">竞赛导航</div>
<div class="bottom">30+项目登陆后报名</div>
</div>
<div class="newa-panel">
<div class="tab">
<div :class="active === i ? 'item active gradient' : 'item'" v-for="i in 5" :key="i" @click="active = i">全部</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const active = ref(1)
</script>
<style lang="scss" scoped>
.banner {
position: relative;
width: 100%;
height: 692px;
background: radial-gradient(
circle,
rgba(131, 255, 197, 1) 0%,
rgba(255, 255, 255, 1) 50%
);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.title {
font-size: 50px;
font-weight: 600;
color: #333333;
}
.description {
width: 1074px;
text-align: center;
font-size: 20px;
color: #666;
margin-top: 37px;
}
.application {
width: 272px;
height: 64px;
text-align: center;
line-height: 64px;
border-radius: 10px;
font-size: 24px;
color: #fff;
cursor: pointer;
margin-top: 50px;
}
.nav-title {
position: absolute;
bottom: 0;
color: #1e2033;
.top {
font-size: 40px;
font-weight: 600;
text-align: center;
}
.bottom {
font-size: 16px;
text-align: center;
margin-top: 10px;
}
}
}
.race-list {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-top: 40px;
.item {
width: 340px;
height: 360px;
// background-color: pink;
.image {
width: 100%;
height: 194px;
img {
width: 100%;
height: 100%;
}
}
.reac-info {
.reac-title {
margin-top: 19px;
color: #c9c9c9;
font-size: 12px;
}
.reac-project {
font-size: 16px;
font-weight: 600;
color: #333333;
margin-top: 10px;
}
.time {
font-size: 14px;
color: #8c8b8b;
margin-top: 40px;
}
}
}
.item:nth-child(n) {
margin-top: 20px;
}
}
.news-list {
margin-top: 170px;
.news-title {
.top {
font-size: 40px;
font-weight: 600;
text-align: center;
}
.bottom {
font-size: 16px;
text-align: center;
margin-top: 10px;
}
}
.newa-panel {
width: 100%;
height: 630px;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
box-shadow: 2px 6px 14px rgba(0, 0, 0, 0.25);
margin-top: 25px;
padding: 46px 180px 70px 180px;
.tab {
display: flex;
justify-content: space-between;
align-items: center;
.item {
width: 152px;
height: 70px;
border-radius: 10px;
text-align: center;
line-height: 70px;
color: #1e2033;
font-size: 18px;
box-shadow: 7px 7px 22px -10px rgba(0, 0, 0, 0.22);
cursor: pointer;
transition: all 0.2s;
}
.item:hover{
transform: scale(1.1);
}
.active {
color: #fff;
}
}
}
}
</style>

@ -0,0 +1,14 @@
<template>
<div>
导航
</div>
</template>
<script lang='ts' setup>
import { onMounted, reactive, ref, toRefs, watch } from 'vue'
</script>
<style lang='scss' scoped>
</style>

@ -0,0 +1,5 @@
declare module '*.vue' {
import { DefineComponent } from "vue"
const component: ReturnType<typeof DefineComponent>
export default component
}

1
src/vite-env.d.ts vendored

@ -0,0 +1 @@
/// <reference types="vite/client" />

@ -0,0 +1,31 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": "./", //
"paths": {
//baseUrl
"@/*": ["src/*"]
},
"types": ["vite/client"]
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "src/utils/rem.js"],
"references": [{ "path": "./tsconfig.node.json" }]
}

@ -0,0 +1,11 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strict": true
},
"include": ["vite.config.ts"]
}

@ -0,0 +1,58 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
//@ts-ignore
import postcssPluginPx2rem from "postcss-plugin-px2rem"; //引入插件
//配置参数
const px2remOptions = {
rootValue: 192, //换算基数, 默认100 ,也就是1440px ,这样的话把根标签的字体规定为1rem为50px,这样就可以从设计稿上量出多少个px直接在代码中写多少px了
unitPrecision: 5, //允许REM单位增长到的十进制数字,其实就是精度控制
// propWhiteList: [], // 默认值是一个空数组,这意味着禁用白名单并启用所有属性。
// propBlackList: [], // 黑名单
// exclude:false, //默认false,可以(reg)利用正则表达式排除某些文件夹的方法,例如/(node_module)/ 。如果想把前端UI框架内的px也转换成rem,请把此属性设为默认值
// selectorBlackList: [], //要忽略并保留为px的选择器
// ignoreIdentifier: false, //(boolean/string)忽略单个属性的方法,启用ignoreidentifier后,replace将自动设置为true。
// replace: true, // (布尔值)替换包含REM的规则,而不是添加回退。
mediaQuery: false, //(布尔值)允许在媒体查询中转换px
minPixelValue: 0 , //设置要替换的最小像素值(3px会被转rem)。 默认 0
}
export default defineConfig({
plugins: [
vue(),
// 配置svg插件
createSvgIconsPlugin({
// Specify the icon folder to be cached
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
// Specify symbolId format
symbolId: 'icon-[dir]-[name]',
}),
],
server:{
host: '0.0.0.0',
port: 8866,
},
resolve: {
alias: {
'@': path.resolve('./src'), // 相对路径别名配置,使用 @ 代替 src
// 'vue': 'vue/dist/vue.esm-bundler.js',
},
},
css: {
preprocessorOptions: {
scss: {
javascriptEnabled: true,
additionalData: '@import "./src/styles/variable.scss";',
},
},
postcss: {
plugins: [
// 配置响应式插件
postcssPluginPx2rem(px2remOptions)
],
},
},
})
Loading…
Cancel
Save